blob: ec8d35d6812da5fa1d9007a1df47e34bfd0cab5c [file] [view]
# Package and Publish a Release
Two scripts drive a release of `apache/skywalking-helm`. They are the procedure; this page explains
what they do and what is left for a human.
| Script | Covers |
| --- | --- |
| [`tools/releasing/release.sh`](../../tools/releasing/release.sh) | everything up to and including the call for vote — build, verify, tag, upload to svn, print the vote mail |
| [`tools/releasing/release-passed.sh`](../../tools/releasing/release-passed.sh) | everything after the vote passes — promote the artifacts, create the GitHub release, print the ANNOUNCE mail |
`release.sh` reads the version out of `chart/skywalking/Chart.yaml`; `release-passed.sh` takes it as
its first argument. Both accept `--dry-run`. Both call the `Makefile`, whose per-target detail is in
[Makefile reference](#makefile-reference) at the end — reference, not procedure.
## What gets released
Two artifacts are signed, uploaded and **voted on** — the source tarball *and* the packaged chart.
That is settled by precedent: every version under
[`dist/release/skywalking/helm/`](https://dist.apache.org/repos/dist/release/skywalking/helm/)
carries six files.
| Where | Artifact | For chart `5.0.0` |
| --- | --- | --- |
| dist.apache.org | source tarball, `.asc`, `.sha512` | `skywalking-helm-5.0.0-src.tgz` |
| dist.apache.org | packaged chart, `.asc`, `.sha512` | `skywalking-helm-5.0.0.tgz` |
| Docker Hub (OCI) | the chart users install — a convenience binary, pushed **after** the vote | `oci://docker.io/apache/skywalking-helm:5.0.0` |
| ghcr.io (OCI) | `0.0.0-<sha>` snapshot of every `master` commit — **not** a release | `oci://ghcr.io/apache/skywalking-helm/skywalking-helm:0.0.0-<sha>` |
The dist path for this project is `skywalking/helm/`. The `skywalking/kubernetes/` directory on dist
is a leftover from the repository's old name (`skywalking-kubernetes`) and still holds `4.7.0`;
nothing new goes there.
The Git tag is `v$VERSION` (`v5.0.0`). Every other coordinate is the plain `$VERSION` (`5.0.0`).
`VERSION` and `CHART_NAME` are read out of `chart/skywalking/Chart.yaml` by the `Makefile` and are
never passed as arguments — see [The version is read, never passed](#the-version-is-read-never-passed).
## 1. Prepare
Everything here lands in one **"Ready to release $VERSION"** PR against `master`. Do not push to
`master` directly — that is the project's rule, not something the tooling stops you doing. The
`.asf.yaml` ruleset on the default branch restricts *deletion and force-push* only, so a direct push
would succeed.
### Milestones
Issues are disabled on this repository, so a release spans two milestones:
- [`apache/skywalking-helm` milestones](https://github.com/apache/skywalking-helm/milestones) —
the PRs of this release (`5.0.0`).
- [`apache/skywalking` milestones](https://github.com/apache/skywalking/milestones) — the
user-facing issues, filed there under `Helm - $VERSION`.
Close everything finished in both; move the rest to the next milestone, creating it if needed.
### Changelog
[`docs/changes/changes.md`](../changes/changes.md) must have a finished section for `$VERSION`,
headed `## $VERSION`, covering breaking changes, features and fixes. This is what the vote mail and
the ANNOUNCE mail link to.
### Version bump
```shell
# chart/skywalking/Chart.yaml
version: 5.0.0
```
Then sweep the prose. The chart version is quoted in `helm install` examples across nearly the whole
docs tree — at `5.0.0` that is **19 files** under `README.md` and `docs/`, not a handful. Do not work
from a remembered list; let grep produce it:
```shell
grep -rln '4\.9\.0' README.md docs/ # the version you are leaving behind
grep -rn -- '--version' README.md docs/ | grep -v '4\.9\.0\|5\.0\.0' # stragglers on other pins
```
Neither comes back empty, so read the hits rather than counting them. The first keeps the deliberate
historical references — [Upgrading](../upgrade/upgrading.md) documents the `4.9.0` → `5.0.0` bump,
[Where to Get the Chart](../install/chart-sources.md) records that `4.9.0` never reached Docker Hub,
and this page cites the dist layout.
The second command is there because not every occurrence is literal: `docs/install/quick-start.md`
passes the chart version through a `SKYWALKING_RELEASE_VERSION` shell variable, and
`docs/install/chart-sources.md` also carries `--version` for the *subchart* pins (`3.3.1` and
friends), which must **not** be bumped along with the chart. Image tags quoted in the docs
(`oap.image.tag`, `ui.image.tag`, `banyandb.image.tag`) must match
[Version Compatibility](../evaluate/version-compatibility.md) — which itself carries the chart
version in its own install example, so it is part of the sweep, not the reference standing outside
it.
### Dependency versions
Every dependency version has to resolve before you can build. BanyanDB Helm is released by
[`apache/skywalking-banyandb-helm`](https://github.com/apache/skywalking-banyandb-helm) and pushed
to the same Docker Hub OCI namespace, so ask the registry:
```shell
helm show chart oci://docker.io/apache/skywalking-banyandb-helm --version 0.7.0
```
An `Error: ... not found` means it is not published there. Cross-check the source release exists too:
<https://dist.apache.org/repos/dist/release/skywalking/banyandb-helm/>.
Audit every dependency before tagging:
```shell
grep '^ version:' chart/skywalking/Chart.yaml
```
The dependency versions are the indented `version:` lines; the chart's own `version:` starts at
column 0. Do not anchor the search to the `- name:` line above it — two entries carry an `alias:` in
between, including the BanyanDB one.
### Checklist before running the scripts
1. `version:` in `chart/skywalking/Chart.yaml` bumped, and the docs sweep done.
2. `dependencies:` in `chart/skywalking/Chart.yaml` all resolve.
3. `docs/changes/changes.md` has a finished `## $VERSION` section.
4. [E2E tests](e2e-tests.md) green on the release commit.
5. Both milestones tidy.
6. The "Ready to release $VERSION" PR merged.
## 2. Add your GPG public key to Apache svn
1. Log in to [id.apache.org](https://id.apache.org/) and submit your key fingerprint.
2. Append your GPG public key to the
[SkyWalking KEYS](https://dist.apache.org/repos/dist/release/skywalking/KEYS) file — **PMC
members only**; ask a PMC member otherwise. **Do not overwrite the existing content, append at
the end.**
The build signs with `gpg --batch --yes`, which will not prompt: the release key must be the default
secret key in your keyring, with its passphrase available through the agent.
```shell
gpg --list-secret-keys --keyid-format=long
```
## 3. Build, verify, tag, upload and call the vote — `release.sh`
**Run it from your own checkout.** Linux and macOS both work — the build was verified end to end
on each.
```shell
bash tools/releasing/release.sh --dry-run # everything except the four writes
bash tools/releasing/release.sh
```
You do not need a clean tree, because the script does not build from your tree. It clones
`apache/skywalking-helm` fresh into `tools/releasing/skywalking-helm/` and does everything there.
That is not tidiness: `make release-src` archives the working *tree*, not `HEAD`, so releasing from
a working copy ships whatever untracked files happen to be sitting in it — editor state, an agent
directory, a half-finished values file. Cloning removes the question. Your checkout is only read,
for the default version.
Note that ignoring such a directory does **not** protect you — `tar` does not read `.gitignore`, so
a gitignored directory is silently archived. That is why the clone is excluded in the Makefile *and*
`verify_artifacts` inspects the finished tarball.
It asks for both versions before doing anything, defaulting from your checkout's `Chart.yaml`:
```
=== Versions ===
release version: 5.1.0 (from your checkout's chart/skywalking/Chart.yaml)
next dev version: 5.2.0
Are these correct? [y/N]
```
Answer anything but `y` to type them in. Both must be plain `MAJOR.MINOR.PATCH` — they end up in a
git tag, an svn path and a branch name, and refusing anything else keeps shell metacharacters out of
all three.
`--dry-run` still clones, builds, signs and verifies, and still runs the svn checkout and the
next-version commit. It skips exactly four things: the tag push, the svn commit, the branch push and
the PR.
### The order matters
| Stage | What it does |
| --- | --- |
| `resolve_versions` | asks for the release and next-dev versions, defaulting from your `Chart.yaml` |
| `preflight` | refuses to start (see below) |
| `clone_repo` | fresh clone into `tools/releasing/skywalking-helm/`; everything below runs there |
| `build` | `make clean` then `make release` — six files in the clone |
| `verify_artifacts` | signature, checksum, a real render, and an inspection of the source tarball |
| `tag` | `git tag -a v$VERSION` and `git push origin v$VERSION` |
| `upload_to_svn` | sparse checkout of `dist/dev/skywalking`, then `svn add` + `svn commit` |
| `prepare_next_version` | rotates the changelog, bumps the chart, opens the next-version PR |
| `vote_mail` | prints the mail, with the real checksums and commit hash filled in |
`clone_repo` also refuses to continue unless master's `Chart.yaml` already says the release version.
This project tags master as it stands, so if the version is not already there then master is not
ready — and setting it inside the clone would tag a commit that exists nowhere else.
The tag is pushed **after** the build and the artifact checks, deliberately. A tag pushed first
survives a failed build, and preflight then refuses to re-run because `v$VERSION` exists — so the
irreversible step comes last, and a failure leaves nothing on the remote to clean up.
Preflight refuses to start when:
- any of `helm`, `gpg`, `shasum`, `svn`, `git`, `make`, `tar`, `awk` is missing. All of them are
reported in one message — finding them one at a time costs one failed run per package;
- `helm` is older than 3.8. The chart ships only as an OCI artifact, and `helm push` to an `oci://`
registry arrived in 3.8. Helm 4 is accepted: `Chart.yaml` is `apiVersion: v2`, which both majors
read;
- `gpg` holds no secret key. `make release` signs with `gpg --batch`, so without one the run would
fail *after* building and packaging everything;
- `gh` is not authenticated. It opens the next-version PR at the very end, so an unauthenticated
`gh` would otherwise surface only after the vote candidate is already staged;
- `dist/dev/skywalking` cannot be read — a network problem, or svn credentials that are not set up;
- `v$VERSION` already exists on the remote, or `dist/dev/skywalking/helm/$VERSION` already does. The second catches
a re-run after a partial upload, which would otherwise only surface at `svn commit` — after the
build, the signing and the tag push.
The point of the capability checks is *when* they fail. A missing signing key or unusable svn
credentials are both perfectly capable of stopping a release half-way through, with a tag already on
the remote; preflight is the only place where stopping is free.
`verify_artifacts` checks, for each of the two artifacts, that the file and its `.asc` and `.sha512`
are present, that `gpg --batch --verify` passes and that `shasum -a 512 -c` passes. It then runs
`helm template` over the packaged chart and requires at least one rendered `kind:` — a chart that
lints but renders nothing is a valid chart. That render uses `oap.storageType=elasticsearch` with
`elasticsearch.enabled` left at its default `true`, so it exercises the ECK path. Finally it lists
the source tarball and fails if it contains the build clone, a `.tgz`, a `charts/` directory or a
`Chart.lock`.
### The next-version PR
`prepare_next_version` runs after the candidate is staged, on a `bump-to-$NEXT` branch of the clone:
- `chart/skywalking/Chart.yaml` moves to the next dev version — with `sed`, not `yq`, because `yq`
rewrites the whole document and turns a one-line bump into a fifty-line reindent;
- `docs/changes/changes.md` becomes `docs/changes/changes-$VERSION.md`, and a fresh changelog is
rendered from `docs/changes/changes.tpl`;
- `docs/menu.yml` gains the released version, inserted directly after `Current Version` so the
in-progress changelog keeps the top of the menu and released versions stay newest-first.
Merge it once the vote thread is open. It cannot affect the artifacts under vote — those were built
from the tag, before this branch existed.
### Send the vote mail
The script prints the mail and stops; it sends nothing. Copy it, check the checksums and the commit
hash it filled in, and send it to `dev@skywalking.apache.org` from your Apache address. Confirm the
six files are visible at `https://dist.apache.org/repos/dist/dev/skywalking/helm/$VERSION/` first.
Voting stays open for at least 72 hours.
## 4. Vote check
What a voter runs. Everything happens in a scratch directory, from the staged URL, never from the
release manager's working copy.
```shell
export VERSION=5.0.0
curl -O "https://dist.apache.org/repos/dist/dev/skywalking/helm/$VERSION/skywalking-helm-$VERSION-src.tgz{,.asc,.sha512}"
curl -O "https://dist.apache.org/repos/dist/dev/skywalking/helm/$VERSION/skywalking-helm-$VERSION.tgz{,.asc,.sha512}"
```
1. **Both artifacts are present**, each with `.asc` and `.sha512` — six files.
2. **Signatures.**
```shell
curl https://downloads.apache.org/skywalking/KEYS -o KEYS && gpg --import KEYS
gpg --batch --verify skywalking-helm-$VERSION-src.tgz.asc skywalking-helm-$VERSION-src.tgz
gpg --batch --verify skywalking-helm-$VERSION.tgz.asc skywalking-helm-$VERSION.tgz
```
3. **Checksums.**
```shell
shasum -a 512 -c skywalking-helm-$VERSION-src.tgz.sha512
shasum -a 512 -c skywalking-helm-$VERSION.tgz.sha512
```
4. **`LICENSE` and `NOTICE` are inside both tarballs** — the chart tarball gets them from
`make prepare`, which copies them in from the repository root just before packaging.
```shell
tar tzf skywalking-helm-$VERSION-src.tgz | grep -E '\./(LICENSE|NOTICE)$'
tar tzf skywalking-helm-$VERSION.tgz | grep -E 'skywalking-helm/(LICENSE|NOTICE)$'
```
5. **Build from source.** Extract the source tarball and package it; this needs network access,
because `helm dep up` fetches the subcharts.
```shell
mkdir src && tar xzf skywalking-helm-$VERSION-src.tgz -C src
( cd src && make package ) # keep the build in a subshell — see below
```
Do not expect the resulting `.tgz` to checksum-match the voted one — `helm package` records file
timestamps. Compare the file listing and the rendered output instead.
**Stay out of `src/` for the steps below.** `make package` writes its own
`skywalking-helm-$VERSION.tgz` into `src/`, with exactly the name of the artifact you
downloaded. If you `cd src` and stay there, steps 6 and 7 silently inspect your local rebuild
instead of the artifact under vote — and a rebuild passes even when the staged tarball is
wrong. Run them from the scratch directory, where the downloaded file is.
6. **The chart renders — both storage paths.** `helm lint` and `helm template` both succeed on a
chart that renders nothing, so look at the output, not the exit code. This chart has three
required values, and no default backend: `oap.storageType` is `null` in `values.yaml`.
BanyanDB is opt-in, and turning it on means turning Elasticsearch off:
```shell
helm template sw skywalking-helm-$VERSION.tgz \
--set oap.image.tag=11.0.0 \
--set oap.storageType=banyandb \
--set ui.image.tag=horizon-1.0.0 \
--set elasticsearch.enabled=false \
--set banyandb.enabled=true \
--set banyandb.image.tag=0.11.0 > render-banyandb.yaml
grep -c '^kind:' render-banyandb.yaml # non-zero; 15 for 5.0.0
grep -q '^kind: Deployment$' render-banyandb.yaml
```
Elasticsearch is the default, and that is the render most users get. `elasticsearch.enabled` is
`true` in `values.yaml`, so **do not carry `--set elasticsearch.enabled=false` over from the
first command** — with it you get the first render minus BanyanDB (11 resources at `5.0.0`) and
no ECK resource whatsoever, which is what the storage-type flag alone buys you:
```shell
helm template sw skywalking-helm-$VERSION.tgz \
--set oap.image.tag=11.0.0 \
--set oap.storageType=elasticsearch \
--set ui.image.tag=horizon-1.0.0 > render-es.yaml
grep -c '^kind:' render-es.yaml # non-zero; 34 for 5.0.0
grep -q '^kind: Elasticsearch$' render-es.yaml # the ECK cluster CR
grep -q 'charts/eck-operator/templates/statefulset.yaml' render-es.yaml
grep -q 'sw-elasticsearch-es-http:9200' render-es.yaml # OAP wired to the ECK service
```
The `^kind: Elasticsearch$` anchor matters: `kind: Elasticsearch` also appears indented inside
the operator's CRD schemas, so an unanchored grep passes on a render that contains no cluster.
The three greps together are what "exercises ECK" means — the operator subchart, the
`Elasticsearch` resource it reconciles, and the OAP deployment pointing at the `-es-http`
service that ECK creates for it.
7. **The chart is stamped with the release version.** The subcharts are vendored into the tarball,
so their versions are readable without a registry:
```shell
tar xzOf skywalking-helm-$VERSION.tgz skywalking-helm/Chart.yaml | grep -E '^version:'
tar tzf skywalking-helm-$VERSION.tgz | grep -E '^skywalking-helm/charts/[^/]+/Chart.yaml$'
tar xzOf skywalking-helm-$VERSION.tgz skywalking-helm/Chart.yaml | grep -E '^ version:'
```
`version:` must be `$VERSION`. The third command prints the dependency versions actually baked
into the package — four entries at `5.0.0`, one per vendored subchart the second command lists.
Note that `helm package` re-serializes `Chart.yaml`: keys come out alphabetized and the Apache
header is gone from *that one file* inside the chart tarball. That is helm's doing and is not a
defect; the license-header check belongs to the source tarball.
8. **License headers** across the source tarball
([license-eye](https://github.com/apache/skywalking-eyes)), and a functional smoke test if you
have a cluster handy — [Quick Start](../install/quick-start.md).
The chart on Docker Hub is **not** part of the vote. It does not exist yet at this point.
Vote result rules:
1. PMC votes are +1 binding, everyone else +1 non-binding.
2. Vote passes with at least 3 binding +1 and more +1 than -1, within 72 hours.
3. **Send the closing mail**, listing the voters by name.
```text
[RESULT][VOTE] Release Apache SkyWalking Helm version $VERSION
3 days passed, we've got ($NUMBER) +1 bindings:
xxx
xxx
xxx
...
(list names)
I'll continue the release process.
```
## 5. Publish — `release-passed.sh`
**PMC members only.** Run it once the vote thread has three binding +1 and no -1.
```shell
bash tools/releasing/release-passed.sh 5.0.0 --dry-run
bash tools/releasing/release-passed.sh 5.0.0
```
The version is the argument here — this script never reads `Chart.yaml`, so it can publish a
release from a checkout that has already moved on.
Every step is irreversible on shared infrastructure, so each asks first, and **declining aborts the
run** — with the one exception marked below. That is deliberate: the steps are ordered and
dependent, and saying no to the svn promotion and then carrying on would create a GitHub release,
and publish the chart, for artifacts still sitting in the dev area. `--dry-run` takes every prompt
as a no *without* aborting, so it walks the whole plan and does none of it.
| Stage | Prompt | Declining |
| --- | --- | --- |
| `resolve_version` | confirm the version to publish | — |
| `preflight` | — | fails if `svn` / `gh` / `git` are missing, if `gh` is not authenticated, if `dist/release/skywalking` cannot be read, or if `dist/dev/skywalking/helm/$VERSION` does not exist |
| `promote_artifacts` | `svn mv` from `dist/dev` to `dist/release` | aborts |
| `remove_previous` | remove everything under `release/helm/` other than `$VERSION` | **skips and continues** — the one exception |
| `github_release` | `gh release create` — this is what publishes the chart | aborts |
| `announce_mail` | — | prints the mail, sends nothing |
| `remaining` | — | prints what is left by hand |
`gh auth status` is checked in preflight rather than left to `github_release`, because
`github_release` runs *after* `promote_artifacts` — and an `svn mv` into `dist/release` cannot be
taken back. An unauthenticated `gh` has to stop the run before that, or not at all.
`remove_previous` is the exception because skipping it is survivable: `dist/release` is meant to
hold only the current version, but a stale sibling breaks nothing. Declining prints
`remove them by hand before announcing`, and the run continues. It lists what is there and asks
rather than guessing a pattern, because that directory is not uniformly laid out — today `4.9.0` is
a version directory while `4.8.0` sits as six loose files at the top level of `helm/`.
Old releases stay available from
[archive.apache.org](https://archive.apache.org/dist/skywalking/helm/); nothing extra to do for
that. Wait for the mirrors before announcing:
```shell
curl -sI "https://downloads.apache.org/skywalking/helm/$VERSION/skywalking-helm-$VERSION-src.tgz" | head -1
```
### The GitHub release is what publishes the chart
`.github/workflows/publish-helm.yaml` listens for `release: types: [released]`, so
`github_release` is the step that puts the chart on Docker Hub; `make publish` is the
[manual fallback](#make-publish--the-manual-fallback) only. The script runs:
```shell
gh release create "v$VERSION" --repo apache/skywalking-helm --verify-tag \
--title "$VERSION" --notes "See .../blob/v$VERSION/docs/changes/changes.md"
```
`--verify-tag` is load-bearing: without it `gh` **creates** a missing tag from the default branch
head, which is not necessarily the commit the PMC voted on. The release is a full release, never a
draft or pre-release — the `released` event fires when a release is published as a full release (or
when an existing pre-release is converted into one), and never for drafts. If the release already
exists the script says so and moves on.
Before it pushes anything the workflow runs three hard guards, under a repository guard
(`github.repository == 'apache/skywalking-helm'`, so forks never publish):
| Check | Trips when | Effect |
| --- | --- | --- |
| Tag shape | the release tag is not `vMAJOR.MINOR.PATCH` | **Fails the job.** The tag is attacker-influenceable text that reaches a shell; only one shape is ever released. |
| Credentials | `DOCKERHUB_USER` / `DOCKERHUB_TOKEN` are not available to the repository | **Fails the job.** They are ASF org secrets; failing early beats an opaque `unauthorized` from the registry. |
| Version matches the tag | `version:` in `chart/skywalking/Chart.yaml` ≠ the tag minus its leading `v` | **Fails the job.** Publishing `5.0.0` from a `v5.1.0` tag would be silent and unfixable once the tag is immutable. |
It packages with `make package` (not bare `helm package`, so `NOTICE` and `LICENSE` are in the
chart), re-checks that both files are inside the tarball, and pushes to
`oci://docker.io/apache`, where `helm push` appends the chart name — landing at
`apache/skywalking-helm:$VERSION`.
Watch it and verify the result — this is not ceremony. Docker Hub currently holds
`apache/skywalking-helm` tags `4.3.0` through `4.8.0` only: **`4.9.0` was released but never reached
the registry**, which is the gap this workflow exists to close. Confirm yours landed:
```shell
gh run list --workflow publish-helm.yaml --repo apache/skywalking-helm --limit 3
helm show chart oci://docker.io/apache/skywalking-helm --version "$VERSION"
```
A `FetchReference ... not found` means the chart is not there, whatever the workflow's exit status
said.
Pushes to `master` keep doing what they always did — a `0.0.0-<sha>` snapshot to
`oci://ghcr.io/apache/skywalking-helm/skywalking-helm`, documented in
[Where to Get the Chart](../install/chart-sources.md).
### What the script leaves for you
It prints this list at the end; the detail is here.
1. **Website**, in [apache/skywalking-website](https://github.com/apache/skywalking-website):
- `data/releases.yml` — under **SkyWalking Kubernetes Helm**, replace the version block with
`$VERSION`: the `src` link through
`https://www.apache.org/dyn/closer.cgi/skywalking/helm/$VERSION/…` and the `asc` / `sha512`
links through `https://downloads.apache.org/skywalking/helm/$VERSION/…`. Set the release date.
- `data/docs.yml` — the **Kubernetes Helm** entry. It still points at tags of the old
`skywalking-kubernetes` repository; once the docs published from `docs/menu.yml` are wired into
the site, add a version entry the way `skywalking-swck` does it — `version: v$VERSION`,
`link: /docs/skywalking-helm/v$VERSION/readme/`, `commitId: <tag sha>` — and update `Latest`.
2. **The ANNOUNCE mail** the script printed, to `dev@skywalking.apache.org` and
`announce@apache.org`, from your Apache address, with the vote thread permalink filled in from
[lists.apache.org](https://lists.apache.org/list.html?dev@skywalking.apache.org).
3. **Milestones** — close `$VERSION` here and `Helm - $VERSION` on `apache/skywalking`, and open the
next pair.
4. **The next development version** — bump `version:` in `chart/skywalking/Chart.yaml`.
## `make publish` — the manual fallback
```makefile
publish: package
helm push ${CHART_NAME}-${VERSION}.tgz oci://docker.io/apache
```
It re-packages from your **working tree** and pushes to Docker Hub. Reach for it only when Actions is
unavailable or the release workflow is broken — the GitHub release created by `release-passed.sh` is
the normal path.
It has **none** of the workflow's guards:
- It does not check the chart version against any tag; it publishes whatever `Chart.yaml` says.
- It does not check credentials first; you get the registry's `unauthorized` instead.
- It builds from your working copy, not from the tag — uncommitted local edits get published, and
nothing ties the pushed chart to the voted artifact.
If you must use it: `helm registry login docker.io` first (write access to the `apache`
Docker Hub organization is required), run it from a pristine checkout of `v$VERSION`, and only after
the vote has passed and the artifacts have moved to `dist/release`. Verify with
`helm show chart oci://docker.io/apache/skywalking-helm --version $VERSION`.
---
## Makefile reference
The scripts in [3](#3-build-verify-tag-upload-and-call-the-vote--releasesh) and
[5](#5-publish--release-passedsh) call these targets. Nothing below is a step to run by hand during
a release.
### The version is read, never passed
```makefile
CHART_DIR = chart/skywalking
VERSION = $(shell cat ${CHART_DIR}/Chart.yaml | grep '^version: ' | awk '{print $$2}')
CHART_NAME = $(shell cat ${CHART_DIR}/Chart.yaml | grep '^name: ' | awk '{print $$2}')
RELEASE_SRC = ${CHART_NAME}-${VERSION}-src
```
`make release VERSION=…` does not do what it looks like: a command-line assignment overrides the make
variable but not the file `helm package` writes — that name comes from `Chart.yaml` — so the
chart-package signing step and `publish` go looking for a tarball that does not exist (the source
tarball is built under the overridden name, so only the chart half breaks). Bumping a release means
editing the `version:` line in `chart/skywalking/Chart.yaml` and committing it. `CHART_NAME` comes
from the chart's `name:` field (`skywalking-helm`), not from the directory name (`skywalking`), so at
chart version `5.0.0` the artifacts are `skywalking-helm-5.0.0.tgz` and
`skywalking-helm-5.0.0-src.tgz`.
`chart/skywalking` is the only chart in this repository, and `CHART_DIR` has only ever pointed at it,
so a release packages, signs and publishes exactly one chart.
`TMPDIR` defaults to `/tmp` and is the only variable declared with `?=`, i.e. the only one meant to be
overridden (`make release TMPDIR=/var/tmp`). The recipe shell is `/bin/bash -eo pipefail`.
### Targets at a glance
| Target | Depends on | Produces |
| --- | --- | --- |
| `prepare` | — | `NOTICE` + `LICENSE` copied into `chart/skywalking/` |
| `package` | `prepare` | `skywalking-helm-<version>.tgz` |
| `clean` | — | nothing; deletes artifacts and resolved dependencies |
| `release-src` | `clean` | `skywalking-helm-<version>-src.tgz` |
| `release` | `release-src`, `package` | both tarballs, each with `.asc` and `.sha512` |
| `publish` | `package` | the chart pushed to Docker Hub as an OCI artifact |
Do not use `make -j`: `release-src` starts by wiping the tree that `package` writes into.
### `prepare` — the NOTICE/LICENSE copy dance
```makefile
prepare:
cp -R NOTICE ${CHART_DIR}/NOTICE
cp -R LICENSE ${CHART_DIR}/LICENSE
```
`NOTICE` and `LICENSE` live at the repository root, but an Apache release artifact has to carry them
*inside* the distributed tarball. `helm package` only picks up files under the chart directory, so
they are copied in immediately before packaging and deleted immediately after — the working tree
never keeps a second copy, and neither file is tracked under `chart/skywalking/`.
If a `make package` run dies partway through, those two copies are left behind. `make clean` removes
them.
### `package`
```makefile
package: prepare
helm dep up ${CHART_DIR}
helm package ${CHART_DIR}
rm -rf ${CHART_DIR}/NOTICE
rm -rf ${CHART_DIR}/LICENSE
```
`helm dep up` resolves every entry in the `dependencies:` block of `Chart.yaml` — `eck-operator`,
`eck-elasticsearch`, `postgresql`, `skywalking-banyandb-helm` — downloading each into
`chart/skywalking/charts/` and writing `chart/skywalking/Chart.lock`. Both are gitignored. `helm
package` then rolls the chart directory, its vendored subcharts, and the just-copied `NOTICE` and
`LICENSE` into `skywalking-helm-<version>.tgz` in the repository root, and the two copies are removed.
The subcharts are baked into the package. Whatever `helm dep up` resolved at package time is what
users get.
### `clean`
Deletes, in one `rm` invocation:
- `$(TMPDIR)/skywalking-helm-<version>-src.tgz`
- `bin/`
- `chart/skywalking/NOTICE`, `chart/skywalking/LICENSE`
- `chart/skywalking/Chart.lock`, `chart/skywalking/charts/`
- `skywalking-helm-<version>.tgz` and its `.asc` / `.sha512`
- `skywalking-helm-<version>-src.tgz` and its `.asc` / `.sha512`
One `rm -rf` with one list of operands. It used to repeat `rm -rf` on each backslash-continued line,
which — there being no `&&` between them — left those tokens sitting in the argument list of a single
`rm` whose only flag was `-f`. GNU `rm` permutes arguments, so on Linux the trailing `-rf` still
applied and `-f` swallowed the stray `rm` operands, and the target did what it read like. BSD `rm` —
macOS — stops option parsing at the first operand, so `-r` never took effect: it failed with
`rm: bin/: is a directory`, left `bin/` and `chart/skywalking/charts/` behind, and exited `1`, which
aborted `make clean`, `make release-src` and `make release`. The recipe is one `rm -rf` again and
survives BSD `rm`. `release.sh` carried a Linux-only guard for as long as that recipe was broken;
with the recipe fixed the guard was obsolete, and it has been removed.
If you touch it, keep it a single `rm -rf` and re-read the whole thing rather than one line.
Because `clean` wipes `charts/` and `Chart.lock`, the next `package` re-resolves dependencies from
scratch.
### `release-src`
```makefile
release-src: clean
tar -zcvf $(TMPDIR)/$(RELEASE_SRC).tgz \
--exclude bin --exclude .git --exclude .idea \
--exclude .gitignore --exclude .DS_Store --exclude .github \
. && \
mv $(TMPDIR)/$(RELEASE_SRC).tgz .
```
It archives the whole working directory — hence the `clean` prerequisite, which removes `charts/`,
`Chart.lock` and this version's `.tgz` files before the tar runs. The tar is built in `$(TMPDIR)` and
only then moved into the repository root, so the archive never contains itself.
`clean` only knows the **current** `$(VERSION)`: it deletes `skywalking-helm-5.0.0*.tgz*`, not
`skywalking-helm-4.9.0.tgz`. A tarball left over from a previous release is not excluded by the tar
either (the exclude list has no `*.tgz`), so it would ship as a binary inside the ASF *source*
release. `release.sh` preflight refuses to start when one is present, which is the check that catches
this.
`tar .` takes the *working tree*, not `HEAD` — which is why preflight also refuses a dirty tree. Run
from a pristine checkout of the release commit: any untracked scratch file that is not in the exclude
list ships inside the ASF source release.
### `release`
```makefile
release: release-src package
gpg --batch --yes --armor --detach-sig $(RELEASE_SRC).tgz
shasum -a 512 $(RELEASE_SRC).tgz > $(RELEASE_SRC).tgz.sha512
gpg --batch --yes --armor --detach-sig $(CHART_NAME)-$(VERSION).tgz
shasum -a 512 $(CHART_NAME)-$(VERSION).tgz > $(CHART_NAME)-$(VERSION).tgz.sha512
```
The full sequence, in order: `clean` → `release-src` → `prepare` → `package` → sign and checksum.
**Both** artifacts are signed and checksummed, leaving six files in the repository root:
```text
skywalking-helm-5.0.0-src.tgz
skywalking-helm-5.0.0-src.tgz.asc
skywalking-helm-5.0.0-src.tgz.sha512
skywalking-helm-5.0.0.tgz
skywalking-helm-5.0.0.tgz.asc
skywalking-helm-5.0.0.tgz.sha512
```
Signing is armored and detached, and `--batch --yes` means GPG will not prompt.
### `publish`
See [`make publish` — the manual fallback](#make-publish--the-manual-fallback).