Run CI on the self-hosted GitLab #1

Merged
jcoffey-dev merged 1 commits from ci/gitlab-pipeline into main 2026-09-21 03:35:55 +00:00
+99
View File
@@ -0,0 +1,99 @@
# CI on the self-hosted GitLab, ported from .github/workflows/ci.yml and
# publish.yml when the GitHub account was suspended on 2026-09-20. The Actions
# files stay in the tree: they are the reference this was written from and work
# unchanged if the appeal succeeds.
#
# This one mattered more than most. ghcr.io/coffey-labs/ihasvpn went dark with
# the account while the deployment on Web_Host was still pulling from it, and
# the only surviving copy was the image already on that host -- amd64 only,
# because that is the platform it runs. The multi-arch tag is rebuilt here.
#
# Images are pinned by digest, with the tag in the trailing comment: the
# replacement for the workflow's SHA-pinned actions, since GitLab has no
# action allowlist.
stages: [build, check, publish]
variables:
IMAGE: $CI_REGISTRY_IMAGE
default:
interruptible: true
.on-change: &on-change
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# The Go binary embeds the built web assets, so the frontend build comes first
# and hands its output to the Go job as an artifact.
web:
stage: build
image: node:26-bookworm-slim@sha256:582460f614631b59b824ac6020533b9bf339c7fdf3a6d7db31abb6b4065f0212 # 26-bookworm-slim
variables:
NPM_CONFIG_CACHE: "$CI_PROJECT_DIR/.npm"
cache:
key:
files: [web/package-lock.json]
paths: [.npm/]
script:
- cd web
- npm ci --ignore-scripts --no-audit --no-fund
- npm run build
artifacts:
paths: [web/dist/]
expire_in: 1 week
<<: *on-change
go:
stage: check
image: golang:1.27-bookworm@sha256:69a7b9788769bec032d238959b61854e9ae87f57be9029ec04e9885fabf99195 # 1.27-bookworm
needs: [web]
cache:
key: go-mod
paths: [.gocache/]
variables:
GOPATH: "$CI_PROJECT_DIR/.gocache"
script:
- go vet ./...
- go test -count=1 ./...
# Left as `go run ...@latest`, as the workflow had it: a vulnerability
# check wants today's database, not a pinned copy of last month's.
- go run golang.org/x/vuln/cmd/govulncheck@latest ./...
<<: *on-change
docker-build:
stage: check
image: docker:28-cli@sha256:625d9431a9f54c5a2bc90f24f0e1c3d55b1349fd857dd85035f98c2c9acbdd4d # 28-cli
needs: [web]
script:
- docker build -t ihasvpn:ci-$CI_COMMIT_SHORT_SHA .
- docker image rm ihasvpn:ci-$CI_COMMIT_SHORT_SHA
<<: *on-change
# The workflow built each platform on its own native runner and joined the two
# digests into one tag. There is a single amd64 runner here, so arm64 goes
# through QEMU instead -- slower, but this is tag-driven and the alternative is
# shipping amd64 only, which is what the account suspension already cost us
# once. TrueNAS and Unraid users pull arm64.
publish:
stage: publish
image: docker:28-cli@sha256:625d9431a9f54c5a2bc90f24f0e1c3d55b1349fd857dd85035f98c2c9acbdd4d # 28-cli
needs: [web, go]
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
- docker run --privileged --rm tonistiigi/binfmt --install arm64
- docker buildx create --use --name ci-builder --driver docker-container || docker buildx use ci-builder
script:
- |
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg IHASVPN_VERSION="$CI_COMMIT_TAG" \
--provenance=false --sbom=false \
--tag "$IMAGE:$CI_COMMIT_TAG" \
--tag "$IMAGE:latest" \
--push .
after_script:
- docker logout "$CI_REGISTRY" || true
rules:
- if: $CI_COMMIT_TAG