Add arm64 release packages and multi-arch Docker images#94
Add arm64 release packages and multi-arch Docker images#94renecannao merged 3 commits intomasterfrom
Conversation
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.
build.sh's precheck requires GOPATH to be set; setup-go@v5 does not export it. Set it from 'go env GOPATH'.
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 48 minutes and 0 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughThis PR introduces multi-architecture (amd64, arm64) release automation by updating the GitHub Actions release workflow to build OS packages for both architectures via Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request adds arm64 support to the build script and introduces a new documentation file detailing the release and publishing workflow. The review feedback highlights an inconsistency in the darwin build target, suggests aligning runner versions for consistency, and identifies a missing rsync dependency in the local reproduction steps.
| 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)" |
There was a problem hiding this comment.
The help message now includes arm64 as a supported architecture. However, the build_binary function (line 125) still hardcodes GOARCH=amd64 for the darwin target. This results in an inconsistency where running ./build.sh -t darwin -a arm64 will produce a tarball labeled as arm64 but containing an amd64 binary. Consider updating build_binary to use the $arch variable for the darwin case as well.
|
|
||
| ### 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. |
There was a problem hiding this comment.
There is a discrepancy between the PR description (which mentions using ubuntu-24.04 for each architecture) and this documentation (which mentions ubuntu-latest for amd64). To ensure consistent build environments and GLIBC requirements across architectures, it is recommended to use an explicit version like ubuntu-24.04 for both.
| 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. | |
| Runs on ubuntu-24.04 (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. |
| -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 && |
There was a problem hiding this comment.
The build.sh script relies on rsync to copy resource artifacts (lines 163-164). The ubuntu:24.04 base image does not include rsync by default, so the local reproduction command provided here will fail. Please add rsync to the apt-get install command.
| apt-get install -y golang git ruby ruby-dev build-essential rpm && | |
| apt-get install -y golang git ruby ruby-dev build-essential rpm rsync && |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 92-100: The workflow hardcodes the image name in the "Build and
push by digest" step (outputs: ... name=ghcr.io/proxysql/orchestrator ...) which
prevents forks from pushing; update the step to derive the image name from the
current repo (e.g. use ghcr.io/${{ github.repository }} or an IMAGE_NAME
input/variable) instead of the hardcoded ghcr.io/proxysql/orchestrator string,
and propagate that variable wherever the hardcoded name is used (the other
occurrences called out in the review) so forks can push to their own GHCR
namespace or document that the name must be overridden.
In `@docs/release.md`:
- Around line 100-110: The Docker example for producing ARM64 packages is
missing GOPATH, causing build.sh to abort in precheck(); update the container
command to export GOPATH (e.g. export GOPATH="$(go env GOPATH)") before invoking
./build.sh -a arm64 so the precheck() sees a valid GOPATH; ensure the export
happens in the same shell session inside the container (or pass GOPATH via
environment in the docker run) so build.sh and its precheck() succeed.
- Around line 42-48: The docs currently claim all three package variants from
package_linux (referencing build.sh and package_linux) produce .tar.gz, .deb and
.rpm; correct this to state that only the full "orchestrator" variant emits a
.tar.gz (in addition to .deb/.rpm if applicable), while "orchestrator-cli" and
"orchestrator-client" are packaged only as .deb/.rpm; update the paragraph that
lists formats for orchestrator, orchestrator-cli, and orchestrator-client to
reflect these exact outputs and keep the note about arch-specific
names/matrix-job upload collisions unchanged.
- Around line 119-121: The doc's "Docker manifest is missing one arch" note is
incorrect about job execution: update the text to state that because the
`docker-merge` job has `needs: docker-build` and no `if: always()`, any failed
`docker-build` matrix leg will cause `docker-merge` to be skipped (even with
`fail-fast: false`), so you must re-run the failed `docker-build` job (or the
whole workflow) before `docker-merge` can produce a multi-arch manifest; mention
`imagetools create` will only produce a single-arch manifest if `docker-merge`
runs with only one successful arch and clarify that re-running the failed
`docker-build` leg (not just `docker-merge`) is required to recover.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c84563cb-1b14-46a5-b764-3ee35766b13b
📒 Files selected for processing (4)
.github/workflows/release.ymlbuild.shdocs/release.mddocs/toc.md
| - 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 | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Can a GitHub Actions workflow running in a forked repository publish to a hardcoded GHCR image in the upstream owner's namespace (for example ghcr.io/proxysql/orchestrator) using the fork's GITHUB_TOKEN, or must the image name use the fork repository/owner namespace?
💡 Result:
No, a GitHub Actions workflow running in a forked repository cannot publish to a hardcoded GHCR image in the upstream owner's namespace (e.g., ghcr.io/proxysql/orchestrator) using the fork's GITHUB_TOKEN. The GITHUB_TOKEN is scoped to the repository where the workflow runs (the fork) and only grants write access to packages in that repository's namespace (e.g., ghcr.io/forkowner/forkrepo). Publishing to the upstream namespace requires write permission on the upstream package, which the fork's GITHUB_TOKEN lacks. To publish to the upstream image: - Use a Personal Access Token (PAT) with write:packages scope for the upstream owner/org, stored as a secret in the fork. - Grant the fork repository explicit write access to the upstream package via the package settings ("Manage Actions access"). All examples and docs use ${{ github.repository }} or similar for the current repo's namespace, confirming GITHUB_TOKEN does not cross namespaces without explicit package-level grants (which forks typically don't have by default).
Citations:
- 1: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs/
- 2: GITHUB_TOKEN permissions for ghcr.io only apply to the default branch actions/runner#1039
- 3: https://docs.github.com/en/actions/publishing-packages/publishing-docker-images
- 4: https://github.com/kenmuse/gh-actions-and-ghcr/blob/main/.github/workflows/publish-image.yml
- 5: https://www.stepsecurity.io/blog/github-token-how-it-works-and-how-to-secure-automatic-github-action-tokens
- 6: https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions
- 7: https://docs.github.com/en/packages/managing-container-images-with-github-container-registry/configuring-access-control-and-visibility-for-container-images
🏁 Script executed:
# Check if docs/release.md exists and examine line 26 area
fd -t f "release.md" docs/ 2>/dev/null && \
echo "=== Checking docs/release.md around line 26 ===" && \
sed -n '20,35p' docs/release.md || echo "docs/release.md not found"Repository: ProxySQL/orchestrator
Length of output: 782
🏁 Script executed:
# Verify the actual .github/workflows/release.yml file
fd -t f "release.yml" .github/workflows/ 2>/dev/null && \
echo "=== Checking lines 92-100 ===" && \
sed -n '92,100p' .github/workflows/release.yml && \
echo -e "\n=== Checking lines 137-158 ===" && \
sed -n '137,158p' .github/workflows/release.ymlRepository: ProxySQL/orchestrator
Length of output: 1276
🏁 Script executed:
# Search for all occurrences of the hardcoded namespace
echo "=== All occurrences of 'ghcr.io/proxysql/orchestrator' ===" && \
rg "ghcr\.io/proxysql/orchestrator" .github/workflows/release.yml -nRepository: ProxySQL/orchestrator
Length of output: 446
The hardcoded GHCR namespace blocks the documented fork dry-run flow.
The documentation states that RC tags can be used to test the release pipeline end-to-end on a fork, but the hardcoded ghcr.io/proxysql/orchestrator image name at lines 99, 141, 152, and 158 prevents this from working. A fork's GITHUB_TOKEN cannot push to the upstream owner's namespace—it only has write access to its own ghcr.io/${{ github.repository }} namespace. Either derive the image name from the current repository or update the documentation to remove the fork dry-run promise.
Suggested refactor
jobs:
build-and-release:
@@
docker-build:
+ env:
+ IMAGE_NAME: ghcr.io/${{ github.repository }}
strategy:
@@
- 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
+ outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
@@
docker-merge:
+ env:
+ IMAGE_NAME: ghcr.io/${{ github.repository }}
needs: docker-build
@@
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
- images: ghcr.io/proxysql/orchestrator
+ images: ${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable=${{ !contains(github.ref_name, 'rc') }}
@@
- 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 ' *)
+ $(printf '${IMAGE_NAME}@sha256:%s ' *)
@@
- name: Inspect image
env:
VERSION: ${{ steps.meta.outputs.version }}
run: |
- docker buildx imagetools inspect "ghcr.io/proxysql/orchestrator:${VERSION}"
+ docker buildx imagetools inspect "${IMAGE_NAME}:${VERSION}"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yml around lines 92 - 100, The workflow hardcodes
the image name in the "Build and push by digest" step (outputs: ...
name=ghcr.io/proxysql/orchestrator ...) which prevents forks from pushing;
update the step to derive the image name from the current repo (e.g. use
ghcr.io/${{ github.repository }} or an IMAGE_NAME input/variable) instead of the
hardcoded ghcr.io/proxysql/orchestrator string, and propagate that variable
wherever the hardcoded name is used (the other occurrences called out in the
review) so forks can push to their own GHCR namespace or document that the name
must be overridden.
| 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. |
There was a problem hiding this comment.
Package formats are overstated in this section.
package_linux only emits a .tar.gz for the full orchestrator package. The -cli and -client variants are packaged as .deb/.rpm only, so this text will send readers looking for tarballs that are never produced.
Suggested doc fix
-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.
+The full `orchestrator` package is emitted as `.tar.gz`, `.deb`, and `.rpm`. The `-cli` and `-client` variants are emitted as `.deb` and `.rpm` only. 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.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/release.md` around lines 42 - 48, The docs currently claim all three
package variants from package_linux (referencing build.sh and package_linux)
produce .tar.gz, .deb and .rpm; correct this to state that only the full
"orchestrator" variant emits a .tar.gz (in addition to .deb/.rpm if applicable),
while "orchestrator-cli" and "orchestrator-client" are packaged only as
.deb/.rpm; update the paragraph that lists formats for orchestrator,
orchestrator-cli, and orchestrator-client to reflect these exact outputs and
keep the note about arch-specific names/matrix-job upload collisions unchanged.
| 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 | ||
| ' |
There was a problem hiding this comment.
The local arm64 recipe is missing GOPATH.
build.sh aborts in precheck() when GOPATH is unset, and the workflow already works around that by exporting go env GOPATH first. The container example should do the same or it won't reproduce CI.
Suggested doc fix
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 &&
+ export GOPATH="$(go env GOPATH)" &&
./build.sh -a arm64
'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 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 | |
| ' | |
| 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: | |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/release.md` around lines 100 - 110, The Docker example for producing
ARM64 packages is missing GOPATH, causing build.sh to abort in precheck();
update the container command to export GOPATH (e.g. export GOPATH="$(go env
GOPATH)") before invoking ./build.sh -a arm64 so the precheck() sees a valid
GOPATH; ensure the export happens in the same shell session inside the container
(or pass GOPATH via environment in the docker run) so build.sh and its
precheck() succeed.
| - **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`. |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In GitHub Actions, when a job depends on a matrix job via needs, does the dependent job get skipped if any matrix leg fails and the job has no if: always() condition?
💡 Result:
Yes, in GitHub Actions, when a job depends on a matrix job via needs, the dependent job gets skipped if any matrix leg (individual job instance from the matrix) fails, unless the dependent job has an if: always (or equivalent condition like if: ${{ !failure && !cancelled }}) condition. The official documentation states: "Use jobs..<job_id>.needs to identify any jobs that must complete successfully before this job will run. [...] If a job fails or is skipped, all jobs that need it are skipped unless the jobs use a conditional expression that causes the job to continue." A matrix job is treated as a single logical job composed of multiple legs. If any leg fails (and fail-fast: true default or explicit, or even with fail-fast: false if not all succeed), the matrix job does not complete "successfully." Thus, dependents via needs are skipped without an overriding if condition. This behavior is confirmed across Stack Overflow examples (e.g., test_finish skipped when matrix test has failures) and GitHub issues discussing propagation of failure/skip states through needs dependencies.
Citations:
- 1: https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow
- 2: https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions
- 3: https://stackoverflow.com/questions/77297980/how-to-fail-job-if-any-dependent-job-fails
- 4: https://stackoverflow.com/questions/74881221/require-only-one-specific-job-in-matrix-to-finish-for-other-dependent-job
- 5: Jobs skipped when NEEDS job ran successfully actions/runner#2205
🏁 Script executed:
# First, find the workflow file(s) referenced in release.md
find . -name "*.md" -path "*/docs/*" | xargs grep -l "docker-merge\|docker-build" | head -5Repository: ProxySQL/orchestrator
Length of output: 82
🏁 Script executed:
# List GitHub Actions workflow files
git ls-files ".github/workflows/" 2>/dev/null || fd -e ".yml" -e ".yaml" ".github/workflows"Repository: ProxySQL/orchestrator
Length of output: 213
🏁 Script executed:
# Read the release.yml workflow file to examine docker-merge and docker-build jobs
cat .github/workflows/release.ymlRepository: ProxySQL/orchestrator
Length of output: 4254
Update the Docker manifest recovery note to reflect actual job dependency behavior.
The documentation incorrectly states that docker-merge will run if only one arch succeeds. With docker-merge declared as needs: docker-build and no if: always() condition, a failed docker-build matrix leg causes the entire job to fail, which skips docker-merge. The fail-fast: false setting only allows the other arch to complete; it doesn't prevent the job from failing.
Suggested doc fix
-- **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`.
+- **Docker manifest is missing one arch.** If either `docker-build` matrix leg fails, `docker-merge` is skipped because it depends on `docker-build`. Re-run the failed `docker-build` job, then re-run `docker-merge`.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - **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`. | |
| - **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 either `docker-build` matrix leg fails, `docker-merge` is skipped because it depends on `docker-build`. Re-run the failed `docker-build` job, then re-run `docker-merge`. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/release.md` around lines 119 - 121, The doc's "Docker manifest is
missing one arch" note is incorrect about job execution: update the text to
state that because the `docker-merge` job has `needs: docker-build` and no `if:
always()`, any failed `docker-build` matrix leg will cause `docker-merge` to be
skipped (even with `fail-fast: false`), so you must re-run the failed
`docker-build` job (or the whole workflow) before `docker-merge` can produce a
multi-arch manifest; mention `imagetools create` will only produce a single-arch
manifest if `docker-merge` runs with only one successful arch and clarify that
re-running the failed `docker-build` leg (not just `docker-merge`) is required
to recover.
Summary
linux/arm64packages (tar.gz/.deb/.rpm) alongsidelinux/amd64, using a nativeubuntu-24.04-armrunner for each arch.ghcr.io/proxysql/orchestratorcovering bothlinux/amd64andlinux/arm64.docs/release.mddocumenting the tag-driven release flow, verification commands, and local reproduction; link it fromdocs/toc.md.Verification
End-to-end test via tag
v4.30.1-rc2:docker buildx imagetools inspectin the workflow confirms bothlinux/arm64andlinux/amd64platforms in the merged manifest.rctags are correctly marked prerelease and do not move the Dockerlatesttag.Test plan
-rcNtagsSummary by CodeRabbit
Release Notes
New Features
Documentation