release-sync
Keep every Argus distribution channel aligned with the project version and free of stale dependencies.
When to use
- Before tagging a release
- After bumping
argusVersioningradle.properties - Periodic outdated-dependency sweep (Netty CVEs, Spring Boot, Micrometer)
- User says: "sync release", "release-sync", "check outdated", "verify distributions", "/release-sync"
Authoritative sources
| Truth | Path |
|---|---|
| Project version | gradle.properties → argusVersion |
| Pinned framework versions | gradle.properties → nettyVersion, junitVersion |
| Module list | settings.gradle.kts |
| Java baseline | root build.gradle.kts toolchain |
Targets
Channel inventory
| Channel | Files | Version surface |
|---|---|---|
| Maven Central (starter) | argus-spring-boot-starter/build.gradle.kts |
Spring Boot, Spring Context, Micrometer, configuration-processor versions |
| Helm chart | charts/argus/Chart.yaml, charts/argus/values.yaml, charts/argus/templates/*.yaml, charts/argus/README.md |
version, appVersion, kubeVersion, image tag, K8s API versions |
| Docker compose | deploy/docker-compose.yml |
image tags (must NOT be :latest) |
| Dockerfiles | Dockerfile, deploy/docker/Dockerfile.* (production only; *.example files use :latest deliberately as templates) |
base image tag (e.g., eclipse-temurin:21-jre-alpine) |
| Install (Unix) | install.sh |
VERSION fallback, ASPROF_VERSION, checksum verification logic |
| Install (Windows) | install.ps1 |
Version fallback, example block |
| Homebrew | Formula/argus.rb |
version, url, sha256, depends_on JDK version |
| SDKMAN | deploy/sdkman/argus-candidate.json, deploy/sdkman/README.md |
candidate version |
| GitHub Action | action/action.yml |
version input default |
| CI workflows | .github/workflows/{ci,release,docker,native-image,pages}.yml |
actions/checkout@v?, actions/setup-java@v?, JDK version, runner |
Upstream "known-good" baselines (update this table when bumping)
These are the current stable minima. Refresh only when the user asks for a sweep.
| Library / image | Minimum acceptable | Why |
|---|---|---|
| Netty | 4.1.115.Final |
CVE-2024-47535 patched here |
| JUnit Jupiter | 5.11.x |
Bug fixes vs 5.10 |
| Spring Boot (compileOnly) | 3.2.0 (LTS-equivalent OK) |
Argus targets Spring Boot 3.2+ |
| Micrometer | 1.12.0 (1.13.x+ preferred for VT metrics) |
Virtual Thread metric surface |
actions/checkout |
v4 |
v3 deprecated track |
actions/setup-java |
v4 |
v3 deprecated track |
eclipse-temurin base |
21-jre-alpine |
Project Java baseline |
prom/prometheus |
a pinned vX.Y.Z, never latest |
reproducibility |
grafana/grafana |
a pinned X.Y.Z, never latest |
reproducibility |
| Kubernetes API | apps/v1, networking.k8s.io/v1, monitoring.coreos.com/v1 |
K8s 1.25+ removed *beta1 variants |
| async-profiler | install-time and runtime versions MUST match — see §2.4 cross-source check | install.sh and AsProfDownloader write to the same ~/.argus/lib/async-profiler/ directory; a mismatch silently makes one of the two payloads dead weight |
Procedure
1. Resolve truth
ARGUS_VERSION="$(awk -F= '/^argusVersion=/{print $2}' gradle.properties)"
NETTY="$(awk -F= '/^nettyVersion=/{print $2}' gradle.properties)"
JUNIT="$(awk -F= '/^junitVersion=/{print $2}' gradle.properties)"
echo "TRUTH version=$ARGUS_VERSION netty=$NETTY junit=$JUNIT"
2. Per-channel drift checks
2.1 Helm chart
grep -E '^(version|appVersion|kubeVersion):' charts/argus/Chart.yaml
versionandappVersionMUST equal$ARGUS_VERSION(appVersion in quotes).kubeVersionMUST be present, e.g.>=1.23.0-0.- Grep templates for deprecated APIs:
Any hit is a P0 — those resources won't apply on K8s 1.25+.grep -rEn 'apiVersion: (extensions/v1beta1|policy/v1beta1|networking\.k8s\.io/v1beta1)' charts/argus/templates/ - Verify
image.taginvalues.yamlmatches$ARGUS_VERSION(or is omitted to inherit appVersion).
2.2 Docker compose
grep -nE 'image:\s*[^[:space:]]+:latest' deploy/docker-compose.yml
Any hit is a P0 — pin to a concrete version.
2.3 Dockerfiles
grep -rEn '^FROM ' Dockerfile deploy/docker/
Confirm base image is eclipse-temurin:21-jre-alpine (or the agreed baseline). Flag any divergence.
2.4 install.sh / install.ps1
grep -nE "VERSION=\"v|fallback|Version = \"v" install.sh install.ps1
- Fallback string MUST be
v$ARGUS_VERSION. install.shexample URLs in the comment header use a recent version.
async-profiler cross-source check (this is the trap that the v1.4.0 verify pass caught — install.sh's ASPROF_VERSION="3.0" shipped for releases while runtime was already pinned to 4.4, making the 267 KB install-time download dead weight):
INSTALL_ASPROF=$(awk -F'"' '/^ASPROF_VERSION=/{print $2}' install.sh)
RUNTIME_ASPROF_DL=$(grep -E '^\s*private static final String ASPROF_VERSION' \
argus-cli/src/main/java/io/argus/cli/provider/jdk/AsProfDownloader.java \
| awk -F'"' '{print $2}')
RUNTIME_ASPROF_CAP=$(grep -E '^\s*public static final String ASPROF_VERSION' \
argus-cli/src/main/java/io/argus/cli/provider/jdk/AsProfCapabilities.java \
| awk -F'"' '{print $2}')
echo "install.sh = $INSTALL_ASPROF"
echo "AsProfDownloader.java = $RUNTIME_ASPROF_DL"
echo "AsProfCapabilities.java = $RUNTIME_ASPROF_CAP"
[ "$INSTALL_ASPROF" = "$RUNTIME_ASPROF_DL" ] && [ "$RUNTIME_ASPROF_DL" = "$RUNTIME_ASPROF_CAP" ] \
&& echo "OK — all three async-profiler version constants agree" \
|| echo "DRIFT — install.sh and runtime async-profiler versions disagree (P1)"
All three values MUST be identical. They share ~/.argus/lib/async-profiler/, so a mismatch means whichever source ran last wins and the other download was wasted bandwidth — or worse, the binary on disk lies about its version. If runtime side bumps, install.sh MUST follow in the same release.
2.5 Homebrew Formula
grep -E "(version|url|sha256)" Formula/argus.rb
versionmatches$ARGUS_VERSION.urlpoints at the GitHub release for that version.sha256matches the actual JAR/binary on the release; if updating version, recompute:
Never invent a hash — compute or escalate.curl -fsSL "<url>" | shasum -a 256
2.6 SDKMAN
grep -E '"version"|"url"' deploy/sdkman/argus-candidate.json
Version field equals $ARGUS_VERSION.
2.7 GitHub Action
grep -nE '(version|default):' action/action.yml
version input default should be $ARGUS_VERSION or latest — confirm with user which convention this repo uses.
2.8 CI workflows
grep -rnE 'uses: [a-z-]+/[a-z-]+@v[0-9]+' .github/workflows/
grep -rnE 'java-version: ' .github/workflows/
Compare action @vN against the baseline table. JDK version should match the project toolchain (21).
2.9 Spring Boot starter dependencies
grep -E "compileOnly|annotationProcessor|api\(" argus-spring-boot-starter/build.gradle.kts
Compare against baseline minima above. Spring Boot can stay at 3.2.0 if intentional, but flag < 3.2.0 as a hard fail.
2.10 Outdated framework versions in gradle.properties
cat gradle.properties
Compare to baseline minima. Netty < 4.1.115.Final is a P0 (CVE).
2.11 Post-release artifact verification
A green release.yml run is not proof every distribution channel succeeded. Each tag-triggered workflow has independent jobs, and one can fail silently while docs continue to reference the URL/tag that was supposed to be produced. Always cross-check the artifacts, not just the run status.
For the most recent published tag (or the tag passed by the user), verify each channel:
LATEST_TAG="$(gh release view --json tagName -q .tagName)"
# 1. Per-workflow conclusion for that tag
for WF in release.yml docker.yml native-image.yml; do
echo "=== $WF ==="
gh run list --workflow "$WF" --branch "$LATEST_TAG" --limit 3 \
--json conclusion,headBranch,event,name,databaseId,createdAt
done
# 2. Per-job conclusion (catch the case where the run is "failure" but only one job)
RUN_ID=$(gh run list --workflow docker.yml --branch "$LATEST_TAG" --limit 1 --json databaseId -q '.[0].databaseId')
gh run view "$RUN_ID" --json jobs -q '.jobs[] | "\(.name): \(.conclusion)"'
# 3. GHCR images actually exist (anonymous manifest fetch — works for public packages)
VERSION="${LATEST_TAG#v}"
OWNER_LC=$(echo "$GITHUB_REPOSITORY_OWNER" | tr 'A-Z' 'a-z') # GHCR is case-sensitive lowercase
for IMAGE in argus argus-agent; do
TOKEN=$(curl -s "https://ghcr.io/token?scope=repository:${OWNER_LC}/${IMAGE}:pull" | jq -r .token)
HTTP=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json" \
"https://ghcr.io/v2/${OWNER_LC}/${IMAGE}/manifests/${VERSION}")
echo "ghcr.io/${OWNER_LC}/${IMAGE}:${VERSION} -> HTTP $HTTP"
done
# 200 = exists, 404 = missing → P0 if docs/Helm reference it.
# 4. GitHub Release artifacts present
gh release view "$LATEST_TAG" --json assets -q '.assets[].name'
Expected for a healthy 1.x release:
release.yml: success, withargus-agent.jar,argus-server.jar,argus-cli-X.Y.Z-all.jarlisted under release assets.docker.yml: every job (Build CLI image,Build agent image) success. Manifest fetch returns HTTP 200 for bothargusandargus-agent.native-image.yml: success if the project ships native binaries; if the upload step fails,install.shfalls back to JAR (which is fine but flag it so the user knows their native artifact is missing).
Red flags this check is meant to catch:
- A run is marked
failurebut only one of N jobs failed, and the published-version assumption silently breaks (e.g., agent image missing while CLI was fine). - Workflow succeeded but
gh release viewshows no assets attached (action-gh-release misconfig). - A
latesttag in GHCR points to a stale digest because the new push was rejected.
If a channel is missing for the current argusVersion:
- Identify the failing job from
gh run view --log-failed. - Fix the workflow on a branch (do not retag
vX.Y.Zin place — Git tags are immutable on consumers' machines). - After the fix is merged to
master, re-run viaworkflow_dispatchagainst the existing tag, or cut avX.Y.(Z+1)patch release.
3. Fix policy
- Pure version swaps with a clear baseline (Helm appVersion, install fallback, compose pin): edit directly.
- Dependency bumps that touch
gradle.propertiesor starterbuild.gradle.kts: run./gradlew compileJavaand./gradlew :argus-cli:testafter the edit; revert if it breaks. - Homebrew
sha256: never guess. Either fetch the release artifact and compute, or escalate. - Deprecated K8s APIs: replace per K8s migration guide; do not silently delete a resource.
- GitHub Action default version: ask the user — some projects pin to specific tags, some prefer
latest.
4. Verify
./gradlew compileJava --quiet # bumps don't break compilation
./gradlew :argus-cli:test --quiet # tests still pass
helm lint charts/argus # if helm is on PATH
docker compose -f deploy/docker-compose.yml config >/dev/null # compose syntax OK
Output format
release-sync report
===================
Truth: version=1.2.0 netty=4.1.115.Final junit=5.11.4
Channel matrix:
starter : OK (Spring Boot 3.2.0 compileOnly, Micrometer 1.12.0)
helm : FIXED Chart.yaml +kubeVersion: ">=1.23.0-0"
docker : FIXED prom/prometheus:latest → :v2.55.0; grafana/grafana:latest → 11.3.0
install.sh : FIXED fallback v1.1.0 → v1.2.0
install.ps1 : FIXED fallback v0.4.0 → v1.2.0; example block
formula : OK
sdkman : OK
action.yml : OK (default version: latest, by design)
ci-workflows : OK (actions @v4, JDK 21)
Dep bumps:
netty 4.1.104.Final → 4.1.115.Final (CVE-2024-47535)
junit 5.10.1 → 5.11.4
Verification:
./gradlew compileJava → exit 0
./gradlew :argus-cli:test → exit 0
Released-tag verification (v1.2.0):
release.yml : success
docker.yml / Build CLI image : success
docker.yml / Build agent image : FAILURE ← P0
native-image.yml : success
ghcr.io/<owner>/argus:1.2.0 : 200 OK
ghcr.io/<owner>/argus-agent:1.2.0 : 404 missing ← P0
release assets : argus-cli-1.2.0-all.jar, argus-agent.jar, argus-server.jar
Escalated to user:
- docker.yml build-agent job failed for v1.2.0 — agent image is not on GHCR even though docs reference it. Fix workflow, then re-run via workflow_dispatch on the v1.2.0 tag or cut v1.2.1.
- Homebrew Formula sha256 — release artifact not yet uploaded; rerun after release publish.
Project rules to honor
- Never commit to master directly — finish on a branch and propose a PR.
- Don't push tags; the release workflow handles that on a tag push.
- Don't bump
argusVersionfrom this skill — that belongs to the release flow. - Never write
--no-verifyor skip hooks. - Don't invent SHA256 or release URLs; compute them or escalate.
Out of scope
- Editing user-facing prose / READMEs / site copy → use
docs-sync. - Publishing to Maven Central (signing, Sonatype upload) — release workflow only.
- Generating SBOM / SLSA attestation — separate concern; flag if missing but don't implement here.