- name
- release-sync
- description
- Audit and update Argus external distribution surfaces (Spring Boot starter, Helm chart, Docker, install scripts, Homebrew Formula, SDKMAN, GitHub Actions, action.yml) against gradle.properties and known-good upstream versions. Detects version drift, floating image tags, deprecated K8s APIs, and outdated dependencies. Use before a release, or when the user asks to "sync release", "update distributions", "check outdated deps", "verify external integrations".
- argument-hint
- [--check] [--fix]
# 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 `argusVersion` in `gradle.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
```bash
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
```bash
grep -E '^(version|appVersion|kubeVersion):' charts/argus/Chart.yaml
```
- `version` and `appVersion` MUST equal `$ARGUS_VERSION` (appVersion in quotes).
- `kubeVersion` MUST be present, e.g. `>=1.23.0-0`.
- Grep templates for deprecated APIs:
```bash
grep -rEn 'apiVersion: (extensions/v1beta1|policy/v1beta1|networking\.k8s\.io/v1beta1)' charts/argus/templates/
```
Any hit is a P0 — those resources won't apply on K8s 1.25+.
- Verify `image.tag` in `values.yaml` matches `$ARGUS_VERSION` (or is omitted to inherit appVersion).
#### 2.2 Docker compose
```bash
grep -nE 'image:\s*[^[:space:]]+:latest' deploy/docker-compose.yml
```
Any hit is a P0 — pin to a concrete version.
#### 2.3 Dockerfiles
```bash
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
```bash
grep -nE "VERSION=\"v|fallback|Version = \"v" install.sh install.ps1
```
- Fallback string MUST be `v$ARGUS_VERSION`.
- `install.sh` example 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):
```bash
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
```bash
grep -E "(version|url|sha256)" Formula/argus.rb
```
- `version` matches `$ARGUS_VERSION`.
- `url` points at the GitHub release for that version.
- `sha256` matches the actual JAR/binary on the release; if updating version, recompute:
```bash
curl -fsSL "<url>" | shasum -a 256
```
Never invent a hash — compute or escalate.
#### 2.6 SDKMAN
```bash
grep -E '"version"|"url"' deploy/sdkman/argus-candidate.json
```
Version field equals `$ARGUS_VERSION`.
#### 2.7 GitHub Action
```bash
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
```bash
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
```bash
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
```bash
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:
```bash
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, with `argus-agent.jar`, `argus-server.jar`, `argus-cli-X.Y.Z-all.jar` listed under release assets.
- `docker.yml`: every job (`Build CLI image`, `Build agent image`) success. Manifest fetch returns HTTP 200 for both `argus` and `argus-agent`.
- `native-image.yml`: success if the project ships native binaries; if the upload step fails, `install.sh` falls 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 `failure` but 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 view` shows no assets attached (action-gh-release misconfig).
- A `latest` tag in GHCR points to a stale digest because the new push was rejected.
If a channel is missing for the current `argusVersion`:
1. Identify the failing job from `gh run view --log-failed`.
2. Fix the workflow on a branch (do not retag `vX.Y.Z` in place — Git tags are immutable on consumers' machines).
3. After the fix is merged to `master`, re-run via `workflow_dispatch` against the existing tag, or cut a `vX.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.properties` or starter `build.gradle.kts`: run `./gradlew compileJava` and `./gradlew :argus-cli:test` after 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
```bash
./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:
Ver no GitHub