ci: remove container publication workflows
This commit is contained in:
1 parent
cfb4c36569
commit
fff284aaa0
4 files changed
+2
-195
No files matched your search
@@ -150,26 +150,3 @@ jobs:
|
||||
path: build-reports/
|
||||
if-no-files-found: warn
|
||||
retention-days: 14
|
||||
|
||||
# Publish only after checks and the production deployment have succeeded.
|
||||
# Serial execution also avoids two builds competing on the self-hosted runner.
|
||||
publish-container:
|
||||
needs: deploy
|
||||
if: gitea.event_name == 'push' && (gitea.ref_name == 'main' || gitea.ref_name == 'master')
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ gitea.repository }}
|
||||
token: ${{ gitea.token }}
|
||||
|
||||
- name: Build, verify portability and publish
|
||||
shell: bash
|
||||
env:
|
||||
REGISTRY_SERVER: ${{ gitea.server_url }}
|
||||
REGISTRY_REPOSITORY: ${{ gitea.repository }}
|
||||
REGISTRY_NAMESPACE: ${{ vars.CONTAINER_REGISTRY_NAMESPACE }}
|
||||
REGISTRY_USER: ${{ secrets.CONTAINER_REGISTRY_USER }}
|
||||
REGISTRY_TOKEN: ${{ secrets.CONTAINER_REGISTRY_TOKEN }}
|
||||
run: bash scripts/publish-container.sh
|
||||
@@ -1,35 +0,0 @@
|
||||
name: Publish portable container
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ gitea.repository }}
|
||||
token: ${{ gitea.token }}
|
||||
- name: Install and verify
|
||||
run: |
|
||||
node scripts/check-node-toolchain.mjs
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm typecheck
|
||||
pnpm biome:lint
|
||||
pnpm test:coverage
|
||||
env:
|
||||
SKIP_ENV_VALIDATION: 1
|
||||
NODE_ENV: test
|
||||
DATABASE_URL: mysql://test:[email protected]:9/test
|
||||
AUTH_SECRET: container-test-secret-at-least-32-characters
|
||||
- name: Build, verify portability and publish
|
||||
shell: bash
|
||||
env:
|
||||
REGISTRY_SERVER: ${{ gitea.server_url }}
|
||||
REGISTRY_REPOSITORY: ${{ gitea.repository }}
|
||||
REGISTRY_NAMESPACE: ${{ vars.CONTAINER_REGISTRY_NAMESPACE }}
|
||||
REGISTRY_USER: ${{ secrets.CONTAINER_REGISTRY_USER }}
|
||||
REGISTRY_TOKEN: ${{ secrets.CONTAINER_REGISTRY_TOKEN }}
|
||||
run: bash scripts/publish-container.sh
|
||||
@@ -250,9 +250,7 @@ bash cms update
|
||||
|
||||
The updater pulls the configured Git upstream, loads `.docker-install`, uses the
|
||||
matching application/migration images and verifies the running release locally
|
||||
and at the saved public URL. If publication for the new commit is still running,
|
||||
it stops before replacing the current container; run the same command after CI
|
||||
succeeds. Existing application rollback remains available on a failed cutover;
|
||||
and at the saved public URL. Existing application rollback remains available on a failed cutover;
|
||||
database migrations are not reversed.
|
||||
|
||||
`bash cms install --configure-only` saves configuration without preparing runtime
|
||||
@@ -261,67 +259,7 @@ committed or sent in Docker build contexts. Existing users of
|
||||
`bash scripts/docker-update.sh` retain the previous behavior when no wizard
|
||||
profile exists; explicit `CMS_IMAGE_REPOSITORY`/`CMS_PUBLIC_URL` overrides still work.
|
||||
|
||||
To publish from Gitea:
|
||||
|
||||
Gitea packages belong to an account or organization, independently of repository
|
||||
permissions. Publication defaults to the lowercase `CONTAINER_REGISTRY_USER`
|
||||
namespace, so a Simo token publishes `simo/epicnext-cms` even though the Git
|
||||
repository belongs to remco. Set the Actions variable
|
||||
`CONTAINER_REGISTRY_NAMESPACE` only to override this (for example, an organization
|
||||
where the token account has package write access). Keep the login username and
|
||||
token from the same account. Changing namespace also changes the image URL used
|
||||
by installations; existing remco image tags are not moved automatically.
|
||||
|
||||
1. In the repository's Actions secrets, configure `CONTAINER_REGISTRY_USER` and
|
||||
`CONTAINER_REGISTRY_TOKEN`. Use a Gitea access token with package read/write
|
||||
permission belonging to the login account. For the Simo token, set
|
||||
`CONTAINER_REGISTRY_USER=Simo`; the default package namespace will be `simo`.
|
||||
2. Every push to `main` or `master` automatically builds and publishes the images
|
||||
after the CI checks and production deployment succeed. Pull requests do not
|
||||
publish images. The publication job builds from committed source only and checks
|
||||
the same application image with two runtime configurations before pushing.
|
||||
Missing registry secrets fail the publication job explicitly; they do not undo
|
||||
an already successful production deployment. No `latest` tag is moved.
|
||||
**Publish portable container** remains available for manual retries on the
|
||||
commit/branch to distribute, without redeploying production.
|
||||
3. The images are `<gitea-host>/<owner>/<repository-lowercase>:<full-commit>` and
|
||||
`:<full-commit>-migrations`. Only the application image runs the website; the
|
||||
migrations image is used temporarily for the matching database migrations.
|
||||
|
||||
Publication uses checksum-pinned regctl v0.11.6 with 8 MiB blob requests to
|
||||
avoid monolithic layer uploads exceeding reverse-proxy limits. Both images are
|
||||
exported and uploaded sequentially; temporary archives and credentials are removed
|
||||
on exit. The runner needs curl, sha256sum and temporary disk space for one Docker
|
||||
image archive plus its extracted OCI layout. The remote image config digest is checked against the locally normalized archive
|
||||
after each upload. A proxy must still allow the OCI registry PATCH/PUT endpoints.
|
||||
|
||||
For this repository the image base is
|
||||
`gitlab.epicnabbo.nl/simo/epicnext-cms`. Package access is controlled by Gitea.
|
||||
For private packages, run `docker login gitlab.epicnabbo.nl` on the installation
|
||||
with a token that can read packages. Then update with:
|
||||
|
||||
```bash
|
||||
CMS_IMAGE_REPOSITORY=gitlab.epicnabbo.nl/simo/epicnext-cms \
|
||||
CMS_PUBLIC_URL=https://your-hotel.example \
|
||||
bash scripts/docker-update.sh
|
||||
```
|
||||
|
||||
The updater pulls the configured Git upstream and requires both images for that
|
||||
exact commit. A missing image or failed login stops before replacing the running
|
||||
CMS. Local builds remain the default when `CMS_IMAGE_REPOSITORY` is unset. Both
|
||||
paths retain the existing image/HTTP checks and automatic application rollback.
|
||||
Migration secrets are mounted read-only for the temporary migration container;
|
||||
they are never copied into its image. Registry images currently target the Linux
|
||||
architecture of the self-hosted build runner; this is not a multi-architecture release.
|
||||
|
||||
The portability gate checks release identity, runtime avatar/badge routing and
|
||||
absence of installation environment files in the application image. It uses an
|
||||
unreachable fixture database and does not replace a full live database/site smoke
|
||||
test. See `scripts/verify-portable-image.mjs`. Production deployment remains verified
|
||||
separately by the existing CI workflow.
|
||||
|
||||
References: [Gitea container registry](https://docs.gitea.com/usage/packages/container/)
|
||||
and [Next.js runtime environment variables](https://nextjs.org/docs/app/guides/self-hosting).
|
||||
Container publication is disabled. CI builds, checks and deploys the CMS, but it does not log in to a registry or upload container images.
|
||||
|
||||
### Diagnose an update that is not visible
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
: "${REGISTRY_SERVER:?Missing Gitea server URL}"
|
||||
: "${REGISTRY_REPOSITORY:?Missing owner/repository}"
|
||||
: "${REGISTRY_USER:?Configure CONTAINER_REGISTRY_USER}"
|
||||
: "${REGISTRY_TOKEN:?Configure CONTAINER_REGISTRY_TOKEN with package write access}"
|
||||
sha="$(git rev-parse HEAD)"
|
||||
[[ "$sha" =~ ^[0-9a-f]{40}$ ]] || exit 1
|
||||
registry="${REGISTRY_SERVER#https://}"
|
||||
registry="${registry%/}"
|
||||
[[ "$REGISTRY_SERVER" = https://* && "$registry" != */* ]] || { echo "Registry must use HTTPS at the Gitea server root" >&2; exit 1; }
|
||||
repository="${REGISTRY_REPOSITORY,,}"
|
||||
[[ "$repository" =~ ^[a-z0-9._-]+/[a-z0-9._-]+$ ]] || exit 1
|
||||
# Gitea packages belong to a user/organization, independently of repository ACLs.
|
||||
# A collaborator token cannot publish to another user's personal namespace.
|
||||
namespace="${REGISTRY_NAMESPACE:-$REGISTRY_USER}"
|
||||
namespace="${namespace,,}"
|
||||
[[ "$namespace" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || { echo "Invalid registry namespace; use a Gitea username or organization" >&2; exit 1; }
|
||||
repository="$namespace/${repository#*/}"
|
||||
image="$registry/$repository:$sha"
|
||||
# Isolate credentials from the self-hosted runner's normal Docker configuration.
|
||||
export DOCKER_CONFIG
|
||||
DOCKER_CONFIG="$(mktemp -d)"
|
||||
context="$(mktemp -d)"
|
||||
trap 'rm -rf -- "$DOCKER_CONFIG" "$context"' EXIT
|
||||
# Build only the committed source, never untracked files from a shared runner.
|
||||
git archive HEAD | tar -x -C "$context"
|
||||
printf '%s' "$REGISTRY_TOKEN" | docker login "$registry" --username "$REGISTRY_USER" --password-stdin
|
||||
unset REGISTRY_TOKEN
|
||||
# On the shared runner, publish the exact image already verified by deployment.
|
||||
local_image="epicnext-cms:$sha"
|
||||
local_revision="$(docker image inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' "$local_image" 2>/dev/null || true)"
|
||||
local_id="$(docker image inspect --format '{{.Id}}' "$local_image" 2>/dev/null || true)"
|
||||
verified_id="$(docker image inspect --format '{{.Id}}' "epicnext-cms:verified-$sha" 2>/dev/null || true)"
|
||||
if [[ "$local_revision" = "$sha" && -n "$local_id" && "$local_id" = "$verified_id" ]]; then
|
||||
docker tag "$verified_id" "$image"
|
||||
echo "Reusing verified release image $local_image"
|
||||
else
|
||||
docker build --network=host --build-arg NEXT_DEPLOYMENT_ID="$sha" -t "$image" "$context"
|
||||
fi
|
||||
docker build --network=host --target migrations -t "$image-migrations" "$context"
|
||||
node scripts/verify-portable-image.mjs "$image" "$sha"
|
||||
# Publish only after the same application image passed both runtime configurations.
|
||||
# Bound each blob request below reverse-proxy upload limits. Pin the uploader
|
||||
# and verify its checksum before giving it access to the temporary Docker login.
|
||||
case "$(uname -m)" in
|
||||
x86_64) arch=amd64; checksum=8e0e62a497fcdb8048d18aa927a139613176ba0531f412bc541044e28f9856bd ;;
|
||||
aarch64|arm64) arch=arm64; checksum=a9b71a3ee79b2d1dbbd7d51fd5e8fa214722c192864235d3d8764463c751a1ff ;;
|
||||
*) echo "Unsupported registry uploader architecture" >&2; exit 1 ;;
|
||||
esac
|
||||
curl --fail --silent --show-error --location --retry 3 --connect-timeout 15 --max-time 120 \
|
||||
"https://github.com/regclient/regclient/releases/download/v0.11.6/regctl-linux-$arch" -o "$context/regctl"
|
||||
printf '%s %s\n' "$checksum" "$context/regctl" | sha256sum --check --status
|
||||
chmod 700 "$context/regctl"
|
||||
export REGCTL_CONFIG="$DOCKER_CONFIG/regctl.json"
|
||||
regctl() { "$context/regctl" "$@"; }
|
||||
regctl registry set "$registry" --blob-chunk 8388608 --blob-max 8388608
|
||||
for target in "$image-migrations" "$image"; do
|
||||
echo "Publishing $target with blob requests up to 8 MiB"
|
||||
docker image save --output "$context/image.tar" "$target"
|
||||
# Normalize the saved archive locally before copying it unchanged to Gitea.
|
||||
# Docker engine IDs and OCI index IDs are not interchangeable with config IDs.
|
||||
local_ref="ocidir://$context/oci:verified"
|
||||
regctl image import "$local_ref" "$context/image.tar"
|
||||
expected_config="$(regctl manifest get "$local_ref" --platform "linux/$arch" --format '{{.GetConfig.Digest}}')"
|
||||
[[ "$expected_config" =~ ^sha256:[0-9a-f]{64}$ ]] || { echo "Invalid local image config digest" >&2; exit 1; }
|
||||
regctl image copy "$local_ref" "$target"
|
||||
remote_config="$(regctl manifest get "$target" --platform "linux/$arch" --format '{{.GetConfig.Digest}}')"
|
||||
[[ "$remote_config" = "$expected_config" ]] || { echo "Published image config does not match verified local image" >&2; exit 1; }
|
||||
rm -f -- "$context/image.tar"
|
||||
rm -rf -- "$context/oci"
|
||||
done
|
||||
echo "Published application and migrations: $image"
|
||||
Reference in new issue
Block a user