prettier, ruff, yamllint and hadolint were the only CI tools still called bare, straight off whatever the runner happened to have installed. Pin them in tool-versions.env like the other three and install them the same way, so the versions Renovate moves are the versions CI runs. Each pinned version equals what is already on the runner, so this changes what CI does not at all today. It changes what CI does on a rebuilt runner: the pinned one gets installed over the drift. The four need four different mechanisms, which is why this is not one pattern: hadolint a bare binary per platform, like actionlint ruff, yamllint PyPI wheels, unpacked by uv prettier an npm tarball, unpacked by tar prettier is the awkward one. Its entry point requires ../package.json relative to its own real path, so copying the single file out -- which is what every other installer here does -- yields a module-not-found at the first run. It keeps its package directory in a versioned one next to a relative symlink, and the tarball ships bin/ without the exec bit, so that needs chmod too. hadolint's release names one platform uname-style and the other Go-style (x86_64 but arm64), which 404s on the first architecture if you assume otherwise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
608 lines
22 KiB
YAML
608 lines
22 KiB
YAML
name: ci
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- "**"
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
|
|
|
env:
|
|
REGISTRY: gcr.forust.xyz
|
|
|
|
jobs:
|
|
lint-compose:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
# Structure check for every committed Compose file, active or not.
|
|
# Interpolation, env-file and bind-mount resolution are all switched off,
|
|
# because inactive stacks have no .env here and would only fail on their
|
|
# ${VAR:?} guards. Active stacks get the full check with interpolation in
|
|
# the deploy workflow, where the real .env files live.
|
|
- name: Validate Compose files
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
source .gitea/workflows/compose-lint.sh
|
|
|
|
mapfile -t safe_flags < <(compose_safe_flags)
|
|
echo "docker compose config ${safe_flags[*]-}"
|
|
|
|
mapfile -t files < <(compose_files)
|
|
if [ "${#files[@]}" -eq 0 ]; then
|
|
echo "No Compose files found."
|
|
exit 0
|
|
fi
|
|
|
|
failed=0
|
|
for f in "${files[@]}"; do
|
|
if ! out="$(validate_compose_file "$f" ${safe_flags[@]+"${safe_flags[@]}"} 2>&1)"; then
|
|
failed=1
|
|
echo "::error file=${f}::$(printf '%s' "$out" | head -1)"
|
|
fi
|
|
done
|
|
|
|
if [ "$failed" -ne 0 ]; then
|
|
echo "Compose validation failed."
|
|
exit 1
|
|
fi
|
|
echo "checked ${#files[@]} Compose file(s)"
|
|
|
|
lint-actionlint:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Lint Gitea Actions workflows with actionlint
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh actionlint)"
|
|
export PATH="$tools_dir:$PATH"
|
|
actionlint -config-file .gitea/actionlint.yaml -color .gitea/workflows/*.yaml
|
|
|
|
lint-shellcheck:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Lint shell scripts with ShellCheck
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh shellcheck)"
|
|
export PATH="$tools_dir:$PATH"
|
|
# userbot/ is a git subtree synced from forust/userbot, so its shell
|
|
# scripts are upstream's to maintain, not ours. Linting them would let a
|
|
# routine subtree pull turn the deploy gate red on code we do not own.
|
|
mapfile -t scripts < <(
|
|
git ls-files '*.sh' ':(glob)**/*.bash' ':!userbot/**'
|
|
)
|
|
if [ "${#scripts[@]}" -eq 0 ]; then
|
|
echo "No shell scripts found."
|
|
exit 0
|
|
fi
|
|
shellcheck --external-sources --source-path=SCRIPTDIR --severity=style "${scripts[@]}"
|
|
|
|
lint-prettier:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Check formatting with Prettier
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh prettier)"
|
|
export PATH="$tools_dir:$PATH"
|
|
|
|
mapfile -t prettier_files < <(
|
|
git ls-files \
|
|
| grep -E '\.(md|json|ya?ml|html|css)$' \
|
|
| grep -Ev '^(\.docs/|\.zed/|errorpages/html/|homepages/(forust_files|xdfnx_files)/)'
|
|
)
|
|
|
|
if [ "${#prettier_files[@]}" -eq 0 ]; then
|
|
echo "No Prettier-managed files found."
|
|
exit 0
|
|
fi
|
|
|
|
prettier --check --ignore-unknown "${prettier_files[@]}"
|
|
|
|
lint-ruff:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Lint and format-check Python with Ruff
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh ruff)"
|
|
export PATH="$tools_dir:$PATH"
|
|
ruff check .
|
|
ruff format --check .
|
|
|
|
lint-yaml:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Lint YAML syntax
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh yamllint)"
|
|
export PATH="$tools_dir:$PATH"
|
|
|
|
mapfile -t yaml_files < <(
|
|
git ls-files '*.yaml' '*.yml' \
|
|
':!node_modules/**' \
|
|
':!**/.venv/**'
|
|
)
|
|
|
|
if [ "${#yaml_files[@]}" -eq 0 ]; then
|
|
echo "No YAML files found."
|
|
exit 0
|
|
fi
|
|
|
|
yamllint -c .yamllint "${yaml_files[@]}"
|
|
|
|
lint-dockerfiles:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Lint Dockerfiles
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh hadolint)"
|
|
export PATH="$tools_dir:$PATH"
|
|
|
|
mapfile -t dockerfiles < <(
|
|
git ls-files ':(glob)**/Dockerfile' ':(glob)**/Dockerfile.*'
|
|
)
|
|
|
|
if [ "${#dockerfiles[@]}" -eq 0 ]; then
|
|
echo "No Dockerfiles found."
|
|
exit 0
|
|
fi
|
|
|
|
hadolint -c .hadolint.yaml "${dockerfiles[@]}"
|
|
|
|
test-backend:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 15
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
# 25 tests over the panel's pydantic models, its auth flow, the SPA
|
|
# fallback and the Kubernetes client it shells out with. They existed and
|
|
# had never been executed by anything.
|
|
#
|
|
# Note that userbot/ is a subtree synced from forust/userbot, so a routine
|
|
# sync can turn this red on upstream's code. Unlike the shellcheck job,
|
|
# which skips that tree because style disagreements there are ours to
|
|
# lose, a failing test here is a real defect in a service we deploy.
|
|
- name: Run the panel backend test suite
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh uv)"
|
|
export PATH="$tools_dir:$PATH"
|
|
|
|
# A venv in a temp dir rather than a checked-out one: the runner is
|
|
# shared, and a leftover .venv would let a dependency the
|
|
# requirements no longer pin still satisfy an import.
|
|
venv="$(mktemp -d)/venv"
|
|
uv venv --quiet "$venv"
|
|
uv pip install --quiet --python "$venv/bin/python" \
|
|
-r userbot/panel/backend/requirements-dev.txt
|
|
|
|
# `python -m`, not bare `pytest`: the tests import `app.*` relative to
|
|
# the backend directory, which only works if the cwd is on sys.path,
|
|
# and only `python -m` puts it there.
|
|
cd userbot/panel/backend
|
|
"$venv/bin/python" -m pytest tests/ -q
|
|
|
|
test-frontend:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 15
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
# One `npm ci` for both checks below: it is by far the slowest part of
|
|
# this job, and a second one would learn nothing the first did not.
|
|
#
|
|
# `npm ci`, not `npm install`, for the same reason the Dockerfile uses it:
|
|
# the lockfile is what makes the tree that gets checked the tree that
|
|
# gets shipped.
|
|
- name: Type-check and test the panel frontend
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
cd userbot/panel/frontend
|
|
npm ci
|
|
|
|
# svelte-check has been a devDependency all along with no script
|
|
# pointing at it, so the type errors it reports had nowhere to
|
|
# surface. It is clean today, which is the only reason it can be a
|
|
# gate: it stops at whatever upstream introduces rather than
|
|
# reporting a backlog we inherited.
|
|
npm run check
|
|
npm test
|
|
|
|
validate:
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
|
|
- name: Validate Kubernetes manifests against JSON schemas
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh kubeconform)"
|
|
export PATH="$tools_dir:$PATH"
|
|
|
|
mapfile -t manifests < <(
|
|
git ls-files ':(glob)**/k8s/**/*.yaml' ':(glob)**/k8s/**/*.yml' \
|
|
| grep -Ev '(^|/)(kustomization\.ya?ml|.*\.example\.ya?ml|.*values\.ya?ml|patch-.*\.ya?ml)$'
|
|
)
|
|
|
|
if [ "${#manifests[@]}" -eq 0 ]; then
|
|
echo "No Kubernetes manifests found."
|
|
exit 0
|
|
fi
|
|
|
|
kubeconform \
|
|
-strict \
|
|
-ignore-missing-schemas \
|
|
-summary \
|
|
"${manifests[@]}"
|
|
|
|
# kubeconform has no schemas for CRDs, so every IngressRoute, Certificate,
|
|
# PrometheusRule, Middleware, ServersTransport and ServiceMonitor is silently
|
|
# skipped above. The live API server knows the real CRD schemas (and runs the
|
|
# cert-manager / Traefik admission webhooks), so validate there too.
|
|
#
|
|
# Only services marked with a k8s/active marker are checked: server-side
|
|
# dry-run needs the target namespace to exist, and inactive services are not
|
|
# deployed. Services being enabled for the first time are still covered by
|
|
# the JSON-schema pass above.
|
|
#
|
|
# Main pushes only. `--dry-run=server` persists nothing, but it does execute
|
|
# the admission webhooks of the production API server, so anyone able to open
|
|
# a pull request would be able to run arbitrary manifest content through
|
|
# cert-manager and Traefik. A pull request has nothing to gain from it either:
|
|
# only main is ever deployed, and this job runs to completion before the
|
|
# deploy workflow is allowed to start, so a bad CRD is still caught before
|
|
# anything reaches the cluster -- just on the push rather than on the PR.
|
|
- name: Note the server-side check is not running here
|
|
if: github.event_name == 'pull_request' || github.ref != 'refs/heads/main'
|
|
shell: bash
|
|
run: |
|
|
echo "::notice::Skipping the server-side dry-run. It executes the cert-manager and" \
|
|
"Traefik admission webhooks against the production API server, so it is limited" \
|
|
"to pushes to main. CRDs are still schema-checked by kubeconform above, and the" \
|
|
"server-side pass still runs on main before the deploy."
|
|
|
|
- name: Validate active manifests against the live API server
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if ! kubectl get --raw='/readyz' --request-timeout=10s >/dev/null 2>&1; then
|
|
echo "::warning::Cluster unreachable — skipped server-side validation of CRDs (IngressRoute, Certificate, PrometheusRule). Review manifest changes manually."
|
|
exit 0
|
|
fi
|
|
|
|
mapfile -t k8s_dirs < <(
|
|
git ls-files '*.yaml' '*.yml' \
|
|
| grep -E '(^|/)k8s/' \
|
|
| sed -E 's#((^|.*/)k8s)/.*#\1#' \
|
|
| sort -u
|
|
)
|
|
|
|
manifests=()
|
|
kustomize_apps=()
|
|
for dir in "${k8s_dirs[@]}"; do
|
|
if [ ! -f "${dir}/active" ]; then
|
|
echo "skip (no k8s/active): ${dir}"
|
|
continue
|
|
fi
|
|
if [ -f "${dir}/overlays/prod/kustomization.yaml" ]; then
|
|
kustomize_apps+=("${dir}/overlays/prod")
|
|
elif [ -f "${dir}/base/kustomization.yaml" ]; then
|
|
kustomize_apps+=("${dir}/base")
|
|
else
|
|
while IFS= read -r f; do
|
|
[ -n "$f" ] && manifests+=("$f")
|
|
done < <(
|
|
git ls-files "${dir}/*.yaml" "${dir}/*.yml" \
|
|
| grep -Ev '(^|/)(kustomization\.ya?ml|.*\.example\.ya?ml|.*values\.ya?ml|patch-.*\.ya?ml)$'
|
|
)
|
|
fi
|
|
done
|
|
|
|
echo "server-side dry-run: ${#manifests[@]} manifests, ${#kustomize_apps[@]} kustomize apps"
|
|
failed=0
|
|
for m in ${manifests[@]+"${manifests[@]}"}; do
|
|
if ! out="$(kubectl apply --dry-run=server -f "$m" 2>&1)"; then
|
|
failed=1
|
|
echo "::error file=${m}::$(printf '%s' "$out" | head -1)"
|
|
fi
|
|
done
|
|
for k in ${kustomize_apps[@]+"${kustomize_apps[@]}"}; do
|
|
if ! out="$(kubectl apply -k "$k" --dry-run=server 2>&1)"; then
|
|
failed=1
|
|
echo "::error file=${k}::$(printf '%s' "$out" | head -1)"
|
|
fi
|
|
done
|
|
|
|
if [ "$failed" -ne 0 ]; then
|
|
echo "Server-side validation failed. The API server (or an admission webhook) rejected these manifests."
|
|
exit 1
|
|
fi
|
|
echo "server-side dry-run: all active manifests accepted by the API server"
|
|
|
|
build:
|
|
needs:
|
|
[lint-actionlint, lint-shellcheck, lint-compose, lint-prettier, lint-ruff, lint-yaml, lint-dockerfiles, validate]
|
|
if: github.event_name != 'pull_request' && (github.ref_name == 'main' || github.ref_name == 'dev')
|
|
runs-on: [self-hosted, linux, arch, homelab]
|
|
timeout-minutes: 60
|
|
outputs:
|
|
services: ${{ steps.services.outputs.services }}
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Detect changed docker-built services
|
|
id: services
|
|
shell: bash
|
|
run: |
|
|
base="${{ github.event.before }}"
|
|
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ]; then
|
|
base="$(git rev-list --max-parents=0 HEAD)"
|
|
fi
|
|
|
|
mapfile -t changed_files < <(git diff --name-only "$base" "${GITHUB_SHA}")
|
|
|
|
services=()
|
|
|
|
add_service() {
|
|
local name="$1"
|
|
local seen=0
|
|
for existing in "${services[@]}"; do
|
|
if [ "$existing" = "$name" ]; then
|
|
seen=1
|
|
break
|
|
fi
|
|
done
|
|
if [ "$seen" -eq 0 ]; then
|
|
services+=("$name")
|
|
fi
|
|
}
|
|
|
|
for file in "${changed_files[@]}"; do
|
|
case "$file" in
|
|
dtek_notif/*)
|
|
add_service dtek_notif
|
|
;;
|
|
errorpages/*)
|
|
add_service errorpages
|
|
;;
|
|
userbot/*)
|
|
add_service userbot
|
|
;;
|
|
homepages/*)
|
|
add_service homepages
|
|
;;
|
|
edu_master/phpsessid-bot/*|edu_master/webinar-checker/*|edu_master/compose.yaml)
|
|
add_service edu_master
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ "${#services[@]}" -eq 0 ]; then
|
|
echo "No docker-built services changed."
|
|
echo "services=" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
|
|
printf '%s\n' "${services[@]}" | tee /tmp/services.txt
|
|
echo "services=$(paste -sd, /tmp/services.txt)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Log in to registry
|
|
if: steps.services.outputs.services != ''
|
|
shell: bash
|
|
run: |
|
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \
|
|
-u "${{ secrets.REGISTRY_USERNAME }}" \
|
|
--password-stdin
|
|
|
|
- name: Build and push changed images
|
|
if: steps.services.outputs.services != ''
|
|
shell: bash
|
|
run: |
|
|
IFS=, read -r -a services <<< "${{ steps.services.outputs.services }}"
|
|
|
|
for service in "${services[@]}"; do
|
|
case "$service" in
|
|
dtek_notif)
|
|
image="${REGISTRY}/forust/dtek-notif"
|
|
tags=()
|
|
case "${GITHUB_REF_NAME}" in
|
|
main)
|
|
tags+=("main" "prod")
|
|
;;
|
|
dev)
|
|
tags+=("dev")
|
|
;;
|
|
esac
|
|
build_args=()
|
|
for tag in "${tags[@]}"; do
|
|
build_args+=(-t "${image}:${tag}")
|
|
done
|
|
docker build \
|
|
--cache-from "type=registry,ref=${image}:buildcache" \
|
|
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
|
"${build_args[@]}" dtek_notif
|
|
for tag in "${tags[@]}"; do
|
|
docker push "${image}:${tag}"
|
|
done
|
|
;;
|
|
errorpages)
|
|
image="${REGISTRY}/forust/error-pages"
|
|
tags=()
|
|
case "${GITHUB_REF_NAME}" in
|
|
main)
|
|
tags+=("main" "prod")
|
|
;;
|
|
dev)
|
|
tags+=("dev")
|
|
;;
|
|
esac
|
|
build_args=()
|
|
for tag in "${tags[@]}"; do
|
|
build_args+=(-t "${image}:${tag}")
|
|
done
|
|
docker build \
|
|
--cache-from "type=registry,ref=${image}:buildcache" \
|
|
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
|
"${build_args[@]}" errorpages
|
|
for tag in "${tags[@]}"; do
|
|
docker push "${image}:${tag}"
|
|
done
|
|
;;
|
|
userbot)
|
|
tags=()
|
|
case "${GITHUB_REF_NAME}" in
|
|
main)
|
|
tags+=("main" "prod")
|
|
;;
|
|
dev)
|
|
tags+=("dev")
|
|
;;
|
|
esac
|
|
for target in runtime panel; do
|
|
case "$target" in
|
|
runtime)
|
|
context="userbot"
|
|
image="${REGISTRY}/forust/userbot"
|
|
;;
|
|
panel)
|
|
context="userbot/panel"
|
|
image="${REGISTRY}/forust/userbot-panel"
|
|
;;
|
|
esac
|
|
build_args=()
|
|
for tag in "${tags[@]}"; do
|
|
build_args+=(-t "${image}:${tag}")
|
|
done
|
|
docker build \
|
|
--cache-from "type=registry,ref=${image}:buildcache" \
|
|
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
|
"${build_args[@]}" "$context"
|
|
for tag in "${tags[@]}"; do
|
|
docker push "${image}:${tag}"
|
|
done
|
|
done
|
|
;;
|
|
homepages)
|
|
for variant in forust xdfnx; do
|
|
case "$variant" in
|
|
forust)
|
|
image="${REGISTRY}/forust/forust-homepage"
|
|
;;
|
|
xdfnx)
|
|
image="${REGISTRY}/forust/xdfnx-homepage"
|
|
;;
|
|
esac
|
|
tags=()
|
|
case "${GITHUB_REF_NAME}" in
|
|
main)
|
|
tags+=("main" "prod")
|
|
;;
|
|
dev)
|
|
tags+=("dev")
|
|
;;
|
|
esac
|
|
build_args=()
|
|
for tag in "${tags[@]}"; do
|
|
build_args+=(-t "${image}:${tag}")
|
|
done
|
|
docker build \
|
|
--cache-from "type=registry,ref=${image}:buildcache" \
|
|
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
|
"${build_args[@]}" -f "homepages/Dockerfile.${variant}" homepages
|
|
for tag in "${tags[@]}"; do
|
|
docker push "${image}:${tag}"
|
|
done
|
|
done
|
|
;;
|
|
edu_master)
|
|
for variant in session-keeper webinar-checker; do
|
|
case "$variant" in
|
|
session-keeper)
|
|
context="edu_master/phpsessid-bot"
|
|
image="${REGISTRY}/forust/session-keeper"
|
|
;;
|
|
webinar-checker)
|
|
context="edu_master/webinar-checker"
|
|
image="${REGISTRY}/forust/webinar-checker"
|
|
;;
|
|
esac
|
|
tags=()
|
|
case "${GITHUB_REF_NAME}" in
|
|
main)
|
|
tags+=("main" "prod")
|
|
;;
|
|
dev)
|
|
tags+=("dev")
|
|
;;
|
|
esac
|
|
build_args=()
|
|
for tag in "${tags[@]}"; do
|
|
build_args+=(-t "${image}:${tag}")
|
|
done
|
|
docker build \
|
|
--cache-from "type=registry,ref=${image}:buildcache" \
|
|
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
|
|
"${build_args[@]}" "$context"
|
|
for tag in "${tags[@]}"; do
|
|
docker push "${image}:${tag}"
|
|
done
|
|
done
|
|
;;
|
|
esac
|
|
done
|