From e655912e373f819b8e05d70c99ac3e00bd6c4de3 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Thu, 16 Apr 2026 07:09:54 +0000 Subject: [PATCH 1/3] Add arm64 release packages and multi-arch Docker images Matrix the release workflow over amd64 and arm64 native GitHub runners (ubuntu-latest, ubuntu-24.04-arm). Each arch runs build.sh to produce tar.gz/.deb/.rpm (full, -cli, -client) and uploads to the same Release. Docker build is split into per-arch push-by-digest jobs plus a merge job that creates a multi-arch manifest on ghcr.io/proxysql/orchestrator. docs/release.md documents the tag-triggered release flow. --- .github/workflows/release.yml | 116 +++++++++++++++++++++++++++----- build.sh | 2 +- docs/release.md | 121 ++++++++++++++++++++++++++++++++++ docs/toc.md | 1 + 4 files changed, 222 insertions(+), 18 deletions(-) create mode 100644 docs/release.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 432ca93c..47269794 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,15 @@ permissions: jobs: build-and-release: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + goarch: amd64 + - runner: ubuntu-24.04-arm + goarch: arm64 + runs-on: ${{ matrix.runner }} steps: - uses: actions/checkout@v4 @@ -22,28 +30,99 @@ jobs: go-version: '1.25.7' cache: true - - name: Build binary - run: go build -o bin/orchestrator ./go/cmd/orchestrator + - name: Install fpm and rpmbuild + run: | + sudo apt-get update + sudo apt-get install -y ruby ruby-dev build-essential rpm + sudo gem install --no-document fpm - - name: Create tar.gz archive + - name: Build and package env: TAG_NAME: ${{ github.ref_name }} - run: tar czf "orchestrator-${TAG_NAME}-linux-amd64.tar.gz" -C bin orchestrator + GOARCH: ${{ matrix.goarch }} + run: | + # strip leading 'v' from tag for package versions + export RELEASE_VERSION="${TAG_NAME#v}" + ./build.sh -a "$GOARCH" + + - name: Collect artifacts + run: | + mkdir -p dist + shopt -s nullglob + cp /tmp/orchestrator-release/*.tar.gz dist/ 2>/dev/null || true + cp /tmp/orchestrator-release/*.deb dist/ 2>/dev/null || true + cp /tmp/orchestrator-release/*.rpm dist/ 2>/dev/null || true + ls -la dist/ - name: Upload release assets uses: softprops/action-gh-release@v2 with: - files: orchestrator-*.tar.gz + files: dist/* generate_release_notes: true draft: false prerelease: ${{ contains(github.ref_name, 'rc') }} - docker: - runs-on: ubuntu-latest + docker-build: + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + platform: linux/amd64 + arch: amd64 + - runner: ubuntu-24.04-arm + platform: linux/arm64 + arch: arm64 + runs-on: ${{ matrix.runner }} steps: - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + context: . + file: docker/Dockerfile + platforms: ${{ matrix.platform }} + outputs: type=image,name=ghcr.io/proxysql/orchestrator,push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + env: + DIGEST: ${{ steps.build.outputs.digest }} + run: | + mkdir -p /tmp/digests + touch "/tmp/digests/${DIGEST#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.arch }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + docker-merge: + needs: docker-build + runs-on: ubuntu-latest + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + pattern: digests-* + path: /tmp/digests + merge-multiple: true + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -64,12 +143,15 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=raw,value=latest,enable=${{ !contains(github.ref_name, 'rc') }} - - name: Build and push - uses: docker/build-push-action@v6 - with: - push: true - context: . - file: docker/Dockerfile - platforms: linux/amd64 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf 'ghcr.io/proxysql/orchestrator@sha256:%s ' *) + + - name: Inspect image + env: + VERSION: ${{ steps.meta.outputs.version }} + run: | + docker buildx imagetools inspect "ghcr.io/proxysql/orchestrator:${VERSION}" diff --git a/build.sh b/build.sh index 0d79d8b5..d30261fe 100755 --- a/build.sh +++ b/build.sh @@ -28,7 +28,7 @@ usage() { echo "-h Show this screen" echo "-t (linux|darwin) Target OS Default:(linux)" echo "-i (systemd|sysv) Target init system Default:(systemd)" - echo "-a (amd64|386) Arch Default:(amd64)" + echo "-a (amd64|386|arm64) Arch Default:(amd64)" echo "-d debug output" echo "-b build only, do not generate packages" echo "-N do not build; use existing ./build/bin/orchestrator binary" diff --git a/docs/release.md b/docs/release.md new file mode 100644 index 00000000..b1e58db7 --- /dev/null +++ b/docs/release.md @@ -0,0 +1,121 @@ +# Releasing and publishing packages + +`orchestrator` release artifacts are built and published automatically by the [`Release` workflow](../.github/workflows/release.yml). The workflow fires on any pushed tag matching `v*` and produces: + +- `tar.gz`, `.deb`, and `.rpm` packages attached to a GitHub Release +- A multi-arch Docker image pushed to `ghcr.io/proxysql/orchestrator` + +Both artifact sets cover `linux/amd64` and `linux/arm64`. + +## Triggering a release + +``` + git tag v4.30.1 + git push origin v4.30.1 +``` + +That's the whole trigger. The workflow picks up the tag name as the release version, strips the leading `v`, and uses `4.30.1` for package versions and Docker tags. + +### Prerelease (release candidate) tags + +Tags that contain `rc` (e.g. `v4.30.1-rc1`) are handled specially: + +- The GitHub Release is marked as a prerelease. +- The Docker `latest` tag is **not** updated — only the specific version tags are pushed. + +Use this for testing the release pipeline end-to-end on a fork before cutting a real release. + +## What runs + +The workflow has three jobs: + +### 1. `build-and-release` (matrix: amd64, arm64) + +Runs on `ubuntu-latest` (amd64) and `ubuntu-24.04-arm` (arm64) — GitHub's native ARM runners, free for public repos. No QEMU, no cross-compilation: each job builds on its own native architecture, so CGO (`go-sqlite3`) works without any special toolchain. + +Each matrix job: + +1. Installs Go, `fpm`, and `rpmbuild`. +2. Runs `./build.sh -a ` with `RELEASE_VERSION` set from the tag. +3. Collects everything `build.sh` writes to `/tmp/orchestrator-release/` and uploads it to the GitHub Release. + +Per arch, `build.sh` produces three variants (see `package_linux` in [`build.sh`](../build.sh)): + +- `orchestrator` — full package (binary + web resources + sample configs + systemd unit) +- `orchestrator-cli` — binary only +- `orchestrator-client` — the `orchestrator-client` shell script only + +Each variant is emitted as `.tar.gz`, `.deb`, and `.rpm`. Package names differ by arch (`_amd64.deb` / `_arm64.deb`, `.x86_64.rpm` / `.aarch64.rpm`), so the two matrix jobs don't collide when uploading to the same Release. + +Both matrix jobs call `softprops/action-gh-release@v2` — the action is idempotent and will attach to the existing Release created by whichever job finishes first. + +### 2. `docker-build` (matrix: linux/amd64, linux/arm64) + +Each arch builds [`docker/Dockerfile`](../docker/Dockerfile) natively on its own runner and pushes **by digest** to `ghcr.io/proxysql/orchestrator` (no tag yet). The digest is uploaded as a workflow artifact for the merge job to consume. + +### 3. `docker-merge` + +Downloads both digests, runs `docker/metadata-action` to compute tags from the git tag: + +- `type=semver,pattern={{version}}` — e.g. `4.30.1` +- `type=semver,pattern={{major}}.{{minor}}` — e.g. `4.30` +- `type=raw,value=latest` — only when the tag does **not** contain `rc` + +Then uses `docker buildx imagetools create` to assemble a multi-arch manifest under all those tags, and inspects the result. + +## Verifying a release + +### GitHub Release page + +After the workflow completes, the Release page should list (for version `X.Y.Z`): + +``` +orchestrator-X.Y.Z-linux-amd64.tar.gz +orchestrator-X.Y.Z-linux-arm64.tar.gz +orchestrator_X.Y.Z-1_amd64.deb +orchestrator_X.Y.Z-1_arm64.deb +orchestrator-X.Y.Z-1.x86_64.rpm +orchestrator-X.Y.Z-1.aarch64.rpm +``` + +Plus the `-cli` and `-client` variants in `.deb` and `.rpm` form for each arch. + +### Docker manifest + +``` + docker buildx imagetools inspect ghcr.io/proxysql/orchestrator:X.Y.Z +``` + +The output should list both `linux/amd64` and `linux/arm64` entries. + +Pull and smoke-test each arch: + +``` + docker run --rm --platform linux/amd64 ghcr.io/proxysql/orchestrator:X.Y.Z orchestrator --version + docker run --rm --platform linux/arm64 ghcr.io/proxysql/orchestrator:X.Y.Z orchestrator --version +``` + +## Local reproduction + +The release workflow does the same thing you can do locally with `build.sh` — see [Building and testing](build.md) and [`build.sh`](../build.sh). To produce ARM64 packages on a non-ARM host (outside of CI), run inside an arm64 container: + +``` + docker run --rm -it --platform linux/arm64 \ + -v $PWD:/src -w /src \ + ubuntu:24.04 bash -c ' + apt-get update && + apt-get install -y golang git ruby ruby-dev build-essential rpm && + gem install --no-document fpm && + ./build.sh -a arm64 + ' +``` + +## Permissions and secrets + +The workflow relies on `GITHUB_TOKEN` with `contents: write` (for the Release) and `packages: write` (for GHCR) — both declared in [`release.yml`](../.github/workflows/release.yml). No additional secrets are required. + +## Troubleshooting + +- **Workflow didn't run after tagging.** The tag must start with `v` (see the `on.push.tags` filter). Tags pushed without `git push --tags` or without pushing the specific ref won't trigger it. +- **A matrix job failed mid-way and part of the release is missing.** The workflow uses `fail-fast: false`, so the other arch still completes. Re-running only the failed job from the Actions UI is safe — the GitHub Release and GHCR both accept re-uploads (fpm uses `-f` to overwrite, `action-gh-release` replaces files of the same name). +- **Docker manifest is missing one arch.** If `docker-build` succeeded for only one arch, `docker-merge` will still run but `imagetools create` will produce a single-arch manifest. Re-run the failed `docker-build` job, then re-run `docker-merge`. diff --git a/docs/toc.md b/docs/toc.md index 53c77c56..c96e7c9d 100644 --- a/docs/toc.md +++ b/docs/toc.md @@ -46,6 +46,7 @@ #### Developers - [Understanding CI](ci.md) - [Building and testing](build.md) +- [Releasing and publishing packages](release.md) - [System test environment](ci-env.md) - [Docker](docker.md) - [Contributions](contributions.md) From 7011113f2d6fbc4bfc835324bff43cfe9ad75038 Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Thu, 16 Apr 2026 07:12:53 +0000 Subject: [PATCH 2/3] Export GOPATH in release workflow build.sh's precheck requires GOPATH to be set; setup-go@v5 does not export it. Set it from 'go env GOPATH'. --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 47269794..24f43102 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,6 +43,7 @@ jobs: run: | # strip leading 'v' from tag for package versions export RELEASE_VERSION="${TAG_NAME#v}" + export GOPATH="$(go env GOPATH)" ./build.sh -a "$GOARCH" - name: Collect artifacts From 2b203a3195e843c5e745e2de8b355aeeb66a578c Mon Sep 17 00:00:00 2001 From: Rene Cannao Date: Thu, 16 Apr 2026 07:40:13 +0000 Subject: [PATCH 3/3] Sync docs/README.md with docs/toc.md --- docs/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/README.md b/docs/README.md index 53c77c56..c96e7c9d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -46,6 +46,7 @@ #### Developers - [Understanding CI](ci.md) - [Building and testing](build.md) +- [Releasing and publishing packages](release.md) - [System test environment](ci-env.md) - [Docker](docker.md) - [Contributions](contributions.md)