vaultik cannot currently cut a tagged release. Three independent problems.
1. goreleaser is configured for GitHub, but the repo is on Gitea
git remote -v is git@git.eeqj.de:sneak/vaultik.git. .goreleaser.yaml
has a release: block (line 54) with no gitea_urls: section, so
goreleaser defaults to the GitHub API and goreleaser release will fail
or publish to the wrong place. This is the hardest blocker on the release
path.
2. No tags exist, and the version string is contradictory
git tag -l is empty, so goreleaser cannot derive {{.Version}} and
only --snapshot mode can run today. Meanwhile four sources disagree:
Source
Value
Makefile:4
VERSION := 1.0.0-rc.1
TODO.md:54
"cut v0.1.0"
TODO.md:13
status "pre-1.0"
internal/globals/globals.go:10-19
defaults to dev
make vaultik bakes 1.0.0-rc.1 into every local build regardless of git
state, so a dev binary misreports its own version.
3. No release automation
.gitea/workflows/ contains only check.yml. Releases would be cut by
hand from a laptop. goreleaser is also not installed by script/bootstrap.
Definition of done
.goreleaser.yaml has a correct gitea_urls: block pointing at https://git.eeqj.de/api/v1 (and the matching download URL), so goreleaser release publishes to the Gitea release API.
Version derives from the git tag rather than a hardcoded Makefile
constant. An untagged build reports something honest (dev plus commit
sha), not a fabricated 1.0.0-rc.1. vaultik version on a tagged
build prints that tag.
TODO.md, the Makefile, and internal/globals no longer contradict
each other about the version.
A tag-triggered .gitea/workflows/release.yml runs goreleaser with a
scoped Gitea token, so releases are reproducible from CI rather than a
workstation. Document the required secret.
script/bootstrap installs goreleaser, and make release / make release-snapshot become thin script/ shims like every other
target, per the scripts-to-rule-them-all pattern the README describes
at :559-587. They are currently the only Makefile targets that are not
shims.
make release-snapshot succeeds end to end, producing archives for linux,darwin x amd64,arm64 with checksums.
make check green.
Explicitly out of scope
Artifact signing, SBOM generation, and reproducible-build settings — file
separately if wanted for 1.0. Shipping shell completions and a man page in
the release archive is covered by its own issue.
vaultik cannot currently cut a tagged release. Three independent problems.
## 1. goreleaser is configured for GitHub, but the repo is on Gitea
`git remote -v` is `git@git.eeqj.de:sneak/vaultik.git`. `.goreleaser.yaml`
has a `release:` block (line 54) with **no `gitea_urls:` section**, so
goreleaser defaults to the GitHub API and `goreleaser release` will fail
or publish to the wrong place. This is the hardest blocker on the release
path.
## 2. No tags exist, and the version string is contradictory
`git tag -l` is empty, so goreleaser cannot derive `{{.Version}}` and
only `--snapshot` mode can run today. Meanwhile four sources disagree:
| Source | Value |
| --- | --- |
| `Makefile:4` | `VERSION := 1.0.0-rc.1` |
| `TODO.md:54` | "cut v0.1.0" |
| `TODO.md:13` | status "pre-1.0" |
| `internal/globals/globals.go:10-19` | defaults to `dev` |
`make vaultik` bakes `1.0.0-rc.1` into every local build regardless of git
state, so a dev binary misreports its own version.
## 3. No release automation
`.gitea/workflows/` contains only `check.yml`. Releases would be cut by
hand from a laptop. `goreleaser` is also not installed by
`script/bootstrap`.
## Definition of done
1. `.goreleaser.yaml` has a correct `gitea_urls:` block pointing at
`https://git.eeqj.de/api/v1` (and the matching download URL), so
`goreleaser release` publishes to the Gitea release API.
2. Version derives from the git tag rather than a hardcoded `Makefile`
constant. An untagged build reports something honest (`dev` plus commit
sha), not a fabricated `1.0.0-rc.1`. `vaultik version` on a tagged
build prints that tag.
3. `TODO.md`, the `Makefile`, and `internal/globals` no longer contradict
each other about the version.
4. A tag-triggered `.gitea/workflows/release.yml` runs goreleaser with a
scoped Gitea token, so releases are reproducible from CI rather than a
workstation. Document the required secret.
5. `script/bootstrap` installs `goreleaser`, and `make release` /
`make release-snapshot` become thin `script/` shims like every other
target, per the scripts-to-rule-them-all pattern the README describes
at :559-587. They are currently the only Makefile targets that are not
shims.
6. `make release-snapshot` succeeds end to end, producing archives for
`linux,darwin x amd64,arm64` with checksums.
7. `make check` green.
## Explicitly out of scope
Artifact signing, SBOM generation, and reproducible-build settings — file
separately if wanted for 1.0. Shipping shell completions and a man page in
the release archive is covered by its own issue.
clawbot
added this to the 1.0.0 milestone 2026-08-09 03:41:28 +02:00
Implementation plan (branch fix-release-pipeline, PR against main):
.goreleaser.yaml — add a top-level gitea_urls: block (api: https://git.eeqj.de/api/v1, download: https://git.eeqj.de) so the release publishes through the Gitea release API instead of defaulting to GitHub.
Version from git, not a constant — new script/version (a sibling of script/projectname) is the single source of the version string:
HEAD is exactly on a tag: print that tag with a leading v stripped, so a make build and a goreleaser build of the same commit report the identical string (goreleaser's .Version strips it, and the archive names use it).
otherwise: dev-<12-char sha>, plus -dirty when the tree is dirty. Nothing fabricated.
Makefile:4VERSION := 1.0.0-rc.1 becomes VERSION := $(shell script/version). internal/cli/version.go currently gates its "this is a development build" notice on globals.Version == "dev" exactly; that becomes a globals.IsDevBuild() predicate so the notice still fires for dev-<sha>. Goreleaser's snapshot.version_template is changed from {{ incpatch .Version }}-next (which invents a version number from a tag that does not exist) to the same honest dev-<shortcommit> shape.
Consistency — with the Makefile deriving from git, the remaining contradiction is TODO.md, which says both "cut v0.1.0" and "pre-1.0" while the issue milestone is 1.0.0. TODO.md gets updated in the same commit per its own Workflow section, and its next step will name the milestone rather than a second, different version number. internal/globals keeps dev/unknown defaults, which are already honest.
.gitea/workflows/release.yml — triggered on push: of v* tags, actions/checkout pinned by commit sha with fetch-depth: 0 (goreleaser needs full history and tags), then script/release with GITEA_TOKEN from a repo secret. The required secret name will be documented in README.md along with the token scopes it needs.
script/bootstrap installs goreleaser; make release / make release-snapshot become shims. Per REPO_POLICIES.md, the install is a specific GitHub release archive verified against a hardcoded sha256 — never curl | sh, never @latest. script/bootstrap hard-fails without Docker (by design, since it gates script/lint), and the release runner should not need Docker, so the installer lives in its own idempotent script/install-goreleaser that script/bootstrap calls and the release workflow calls directly. script/release and script/release-snapshot resolve the binary the same way script/lint resolves its linter: an exact-pinned-version goreleaser on PATH is used, otherwise the repo-local installed copy, otherwise a loud failure naming script/bootstrap. /dist/ gets gitignored.
Verification — make release-snapshot end to end (4 archives + checksums.txt), vaultik version from an untagged build, make check and script/cibuild.
Out of scope per the issue and confirmed with the requester: signing, SBOM, reproducible-build settings, completions, man page. No git tag will be created or pushed; tagging is the owner's call, so version derivation is exercised via --snapshot and by inspecting the resolved ldflags.
Implementation plan (branch `fix-release-pipeline`, PR against `main`):
1. **`.goreleaser.yaml`** — add a top-level `gitea_urls:` block (`api: https://git.eeqj.de/api/v1`, `download: https://git.eeqj.de`) so the release publishes through the Gitea release API instead of defaulting to GitHub.
2. **Version from git, not a constant** — new `script/version` (a sibling of `script/projectname`) is the single source of the version string:
- `HEAD` is exactly on a tag: print that tag with a leading `v` stripped, so a `make` build and a goreleaser build of the same commit report the identical string (goreleaser's `.Version` strips it, and the archive names use it).
- otherwise: `dev-<12-char sha>`, plus `-dirty` when the tree is dirty. Nothing fabricated.
`Makefile:4` `VERSION := 1.0.0-rc.1` becomes `VERSION := $(shell script/version)`. `internal/cli/version.go` currently gates its "this is a development build" notice on `globals.Version == "dev"` exactly; that becomes a `globals.IsDevBuild()` predicate so the notice still fires for `dev-<sha>`. Goreleaser's `snapshot.version_template` is changed from `{{ incpatch .Version }}-next` (which invents a version number from a tag that does not exist) to the same honest `dev-<shortcommit>` shape.
3. **Consistency** — with the `Makefile` deriving from git, the remaining contradiction is `TODO.md`, which says both "cut v0.1.0" and "pre-1.0" while the issue milestone is 1.0.0. `TODO.md` gets updated in the same commit per its own Workflow section, and its next step will name the milestone rather than a second, different version number. `internal/globals` keeps `dev`/`unknown` defaults, which are already honest.
4. **`.gitea/workflows/release.yml`** — triggered on `push:` of `v*` tags, `actions/checkout` pinned by commit sha with `fetch-depth: 0` (goreleaser needs full history and tags), then `script/release` with `GITEA_TOKEN` from a repo secret. The required secret name will be documented in `README.md` along with the token scopes it needs.
5. **`script/bootstrap` installs goreleaser; `make release` / `make release-snapshot` become shims.** Per `REPO_POLICIES.md`, the install is a specific GitHub release archive verified against a hardcoded sha256 — never `curl | sh`, never `@latest`. `script/bootstrap` hard-fails without Docker (by design, since it gates `script/lint`), and the release runner should not need Docker, so the installer lives in its own idempotent `script/install-goreleaser` that `script/bootstrap` calls and the release workflow calls directly. `script/release` and `script/release-snapshot` resolve the binary the same way `script/lint` resolves its linter: an exact-pinned-version `goreleaser` on `PATH` is used, otherwise the repo-local installed copy, otherwise a loud failure naming `script/bootstrap`. `/dist/` gets gitignored.
6. **Verification** — `make release-snapshot` end to end (4 archives + `checksums.txt`), `vaultik version` from an untagged build, `make check` and `script/cibuild`.
Out of scope per the issue and confirmed with the requester: signing, SBOM, reproducible-build settings, completions, man page. No git tag will be created or pushed; tagging is the owner's call, so version derivation is exercised via `--snapshot` and by inspecting the resolved ldflags.
Implemented in PR #104 (branch fix-release-pipeline, one commit).
All seven definition-of-done items are done. The full writeup is on the PR; the short version:
.goreleaser.yaml has gitea_urls: (api: https://git.eeqj.de/api/v1, download: https://git.eeqj.de).
New script/version derives the version from git — exact tag with a leading v stripped, else dev-<12-char sha>, with -dirty on modified tracked files. Makefile:4 now calls it. goreleaser's snapshot template stopped inventing a release number too ({{ incpatch .Version }}-next became the same dev-<sha>).
Makefile, internal/globals and TODO.md now agree; TODO.md's next step names one version target instead of a third one.
.gitea/workflows/release.yml triggers on v* tags with fetch-depth: 0, and needs one repository Actions secret: RELEASE_TOKEN, a Gitea token with write:repository scope, passed to goreleaser as GITEA_TOKEN. Documented in the new README.md "releasing" section.
script/install-goreleaser installs the pinned v2.17.1 from a sha256-verified archive and is called by script/bootstrap; make release / make release-snapshot are now shims to script/release / script/release-snapshot.
make release-snapshot succeeds: four archives (linux,darwin x amd64,arm64) plus checksums.txt.
make check green (14 ok, none cached, 0 issues.), and script/cibuild exits 0 with the three check layers actually executing.
One consequence found while doing this and worth a look on review: internal/cli/version.go gated its development-build notice on the version being exactly dev, so as soon as untagged builds carried a commit sha the notice would have gone quiet and an unreleased binary would have read as a release. That gate is now a tested predicate.
No tag was created or pushed — tag handling was exercised in a throwaway repository instead, since cutting the tag is yours to do.
Implemented in [PR #104](https://git.eeqj.de/sneak/vaultik/pulls/104) (branch `fix-release-pipeline`, one commit).
All seven definition-of-done items are done. The full writeup is on the PR; the short version:
1. `.goreleaser.yaml` has `gitea_urls:` (`api: https://git.eeqj.de/api/v1`, `download: https://git.eeqj.de`).
2. New `script/version` derives the version from git — exact tag with a leading `v` stripped, else `dev-<12-char sha>`, with `-dirty` on modified tracked files. `Makefile:4` now calls it. goreleaser's snapshot template stopped inventing a release number too (`{{ incpatch .Version }}-next` became the same `dev-<sha>`).
3. `Makefile`, `internal/globals` and `TODO.md` now agree; `TODO.md`'s next step names one version target instead of a third one.
4. `.gitea/workflows/release.yml` triggers on `v*` tags with `fetch-depth: 0`, and needs one repository Actions secret: **`RELEASE_TOKEN`**, a Gitea token with `write:repository` scope, passed to goreleaser as `GITEA_TOKEN`. Documented in the new `README.md` "releasing" section.
5. `script/install-goreleaser` installs the pinned v2.17.1 from a sha256-verified archive and is called by `script/bootstrap`; `make release` / `make release-snapshot` are now shims to `script/release` / `script/release-snapshot`.
6. `make release-snapshot` succeeds: four archives (`linux,darwin` x `amd64,arm64`) plus `checksums.txt`.
7. `make check` green (14 `ok`, none cached, `0 issues.`), and `script/cibuild` exits 0 with the three check layers actually executing.
One consequence found while doing this and worth a look on review: `internal/cli/version.go` gated its development-build notice on the version being exactly `dev`, so as soon as untagged builds carried a commit sha the notice would have gone quiet and an unreleased binary would have read as a release. That gate is now a tested predicate.
No tag was created or pushed — tag handling was exercised in a throwaway repository instead, since cutting the tag is yours to do.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
vaultik cannot currently cut a tagged release. Three independent problems.
1. goreleaser is configured for GitHub, but the repo is on Gitea
git remote -visgit@git.eeqj.de:sneak/vaultik.git..goreleaser.yamlhas a
release:block (line 54) with nogitea_urls:section, sogoreleaser defaults to the GitHub API and
goreleaser releasewill failor publish to the wrong place. This is the hardest blocker on the release
path.
2. No tags exist, and the version string is contradictory
git tag -lis empty, so goreleaser cannot derive{{.Version}}andonly
--snapshotmode can run today. Meanwhile four sources disagree:Makefile:4VERSION := 1.0.0-rc.1TODO.md:54TODO.md:13internal/globals/globals.go:10-19devmake vaultikbakes1.0.0-rc.1into every local build regardless of gitstate, so a dev binary misreports its own version.
3. No release automation
.gitea/workflows/contains onlycheck.yml. Releases would be cut byhand from a laptop.
goreleaseris also not installed byscript/bootstrap.Definition of done
.goreleaser.yamlhas a correctgitea_urls:block pointing athttps://git.eeqj.de/api/v1(and the matching download URL), sogoreleaser releasepublishes to the Gitea release API.Makefileconstant. An untagged build reports something honest (
devplus commitsha), not a fabricated
1.0.0-rc.1.vaultik versionon a taggedbuild prints that tag.
TODO.md, theMakefile, andinternal/globalsno longer contradicteach other about the version.
.gitea/workflows/release.ymlruns goreleaser with ascoped Gitea token, so releases are reproducible from CI rather than a
workstation. Document the required secret.
script/bootstrapinstallsgoreleaser, andmake release/make release-snapshotbecome thinscript/shims like every othertarget, per the scripts-to-rule-them-all pattern the README describes
at :559-587. They are currently the only Makefile targets that are not
shims.
make release-snapshotsucceeds end to end, producing archives forlinux,darwin x amd64,arm64with checksums.make checkgreen.Explicitly out of scope
Artifact signing, SBOM generation, and reproducible-build settings — file
separately if wanted for 1.0. Shipping shell completions and a man page in
the release archive is covered by its own issue.
Implementation plan (branch
fix-release-pipeline, PR againstmain):.goreleaser.yaml— add a top-levelgitea_urls:block (api: https://git.eeqj.de/api/v1,download: https://git.eeqj.de) so the release publishes through the Gitea release API instead of defaulting to GitHub.Version from git, not a constant — new
script/version(a sibling ofscript/projectname) is the single source of the version string:HEADis exactly on a tag: print that tag with a leadingvstripped, so amakebuild and a goreleaser build of the same commit report the identical string (goreleaser's.Versionstrips it, and the archive names use it).dev-<12-char sha>, plus-dirtywhen the tree is dirty. Nothing fabricated.Makefile:4VERSION := 1.0.0-rc.1becomesVERSION := $(shell script/version).internal/cli/version.gocurrently gates its "this is a development build" notice onglobals.Version == "dev"exactly; that becomes aglobals.IsDevBuild()predicate so the notice still fires fordev-<sha>. Goreleaser'ssnapshot.version_templateis changed from{{ incpatch .Version }}-next(which invents a version number from a tag that does not exist) to the same honestdev-<shortcommit>shape.Consistency — with the
Makefilederiving from git, the remaining contradiction isTODO.md, which says both "cut v0.1.0" and "pre-1.0" while the issue milestone is 1.0.0.TODO.mdgets updated in the same commit per its own Workflow section, and its next step will name the milestone rather than a second, different version number.internal/globalskeepsdev/unknowndefaults, which are already honest..gitea/workflows/release.yml— triggered onpush:ofv*tags,actions/checkoutpinned by commit sha withfetch-depth: 0(goreleaser needs full history and tags), thenscript/releasewithGITEA_TOKENfrom a repo secret. The required secret name will be documented inREADME.mdalong with the token scopes it needs.script/bootstrapinstalls goreleaser;make release/make release-snapshotbecome shims. PerREPO_POLICIES.md, the install is a specific GitHub release archive verified against a hardcoded sha256 — nevercurl | sh, never@latest.script/bootstraphard-fails without Docker (by design, since it gatesscript/lint), and the release runner should not need Docker, so the installer lives in its own idempotentscript/install-goreleaserthatscript/bootstrapcalls and the release workflow calls directly.script/releaseandscript/release-snapshotresolve the binary the same wayscript/lintresolves its linter: an exact-pinned-versiongoreleaseronPATHis used, otherwise the repo-local installed copy, otherwise a loud failure namingscript/bootstrap./dist/gets gitignored.Verification —
make release-snapshotend to end (4 archives +checksums.txt),vaultik versionfrom an untagged build,make checkandscript/cibuild.Out of scope per the issue and confirmed with the requester: signing, SBOM, reproducible-build settings, completions, man page. No git tag will be created or pushed; tagging is the owner's call, so version derivation is exercised via
--snapshotand by inspecting the resolved ldflags.Implemented in PR #104 (branch
fix-release-pipeline, one commit).All seven definition-of-done items are done. The full writeup is on the PR; the short version:
.goreleaser.yamlhasgitea_urls:(api: https://git.eeqj.de/api/v1,download: https://git.eeqj.de).script/versionderives the version from git — exact tag with a leadingvstripped, elsedev-<12-char sha>, with-dirtyon modified tracked files.Makefile:4now calls it. goreleaser's snapshot template stopped inventing a release number too ({{ incpatch .Version }}-nextbecame the samedev-<sha>).Makefile,internal/globalsandTODO.mdnow agree;TODO.md's next step names one version target instead of a third one..gitea/workflows/release.ymltriggers onv*tags withfetch-depth: 0, and needs one repository Actions secret:RELEASE_TOKEN, a Gitea token withwrite:repositoryscope, passed to goreleaser asGITEA_TOKEN. Documented in the newREADME.md"releasing" section.script/install-goreleaserinstalls the pinned v2.17.1 from a sha256-verified archive and is called byscript/bootstrap;make release/make release-snapshotare now shims toscript/release/script/release-snapshot.make release-snapshotsucceeds: four archives (linux,darwinxamd64,arm64) pluschecksums.txt.make checkgreen (14ok, none cached,0 issues.), andscript/cibuildexits 0 with the three check layers actually executing.One consequence found while doing this and worth a look on review:
internal/cli/version.gogated its development-build notice on the version being exactlydev, so as soon as untagged builds carried a commit sha the notice would have gone quiet and an unreleased binary would have read as a release. That gate is now a tested predicate.No tag was created or pushed — tag handling was exercised in a throwaway repository instead, since cutting the tag is yours to do.