Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33

Open
lilleman wants to merge 966 commits from worktree-todo-relative-dates-exact-counts into main
10 changed files with 135 additions and 43 deletions
Showing only changes of commit cc7d4fb972 - Show all commits
+41 -1
View File
@@ -6,6 +6,13 @@ on:
tag:
type: string
required: true
secrets:
TAP_GITHUB_TOKEN:
required: true
AUR_KEY:
required: true
CLOUDSMITH_API_KEY:
required: true
workflow_dispatch:
inputs:
tag:
@@ -16,6 +23,10 @@ on:
permissions:
contents: read
concurrency:
group: packages
cancel-in-progress: false
jobs:
packages:
name: Binaries, packages and image for ${{ inputs.tag }}
@@ -36,6 +47,26 @@ jobs:
with:
path: tooling
persist-credentials: false
- name: The Go the gate tests
id: go
run: sed -n 's/^ARG GO_VERSION=/version=/p' tooling/Dockerfile >> "$GITHUB_OUTPUT"
- uses: actions/setup-go@v7
with:
go-version: ${{ steps.go.outputs.version }}
cache: false
# The tap, the bucket, the AUR and the image's latest tag each hold one version, so
# only the latest release updates them.
- name: Channels holding one version
id: latest
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
run: |
if [ "$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName --jq .tagName)" = "$TAG" ]; then
echo "skip=" >> "$GITHUB_OUTPUT"
else
echo "skip=--skip=homebrew,scoop,aur,docker" >> "$GITHUB_OUTPUT"
fi
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
with:
@@ -46,7 +77,7 @@ jobs:
with:
version: v2.18.3
workdir: src
args: release --clean --config ../tooling/release-tooling/goreleaser.yaml
args: release --clean --config ../tooling/release-tooling/goreleaser.yaml ${{ steps.latest.outputs.skip }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
@@ -59,3 +90,12 @@ jobs:
for f in dist/*.deb; do cloudsmith push deb --republish larvit/fejkdata/any-distro/any-version "$f"; done
for f in dist/*.rpm; do cloudsmith push rpm --republish larvit/fejkdata/any-distro/any-version "$f"; done
for f in dist/*.apk; do cloudsmith push alpine --republish larvit/fejkdata/alpine/any-version "$f"; done
- name: The built binary and the published image print the tag
env:
TAG: ${{ inputs.tag }}
IMAGE: ${{ steps.latest.outputs.skip == '' }}
run: |
test "$(dist/fejkdata_linux_amd64_v1/fejkdata --version)" = "fejkdata $TAG"
if [ "$IMAGE" = true ]; then
test "$(docker run --rm "ghcr.io/larvit/fejkdata:${TAG#v}" --version)" = "fejkdata $TAG"
fi
+4 -1
View File
@@ -81,4 +81,7 @@ jobs:
uses: ./.github/workflows/packages.yml
with:
tag: ${{ needs.release.outputs.tag }}
secrets: inherit
secrets:
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
+1
View File
@@ -122,6 +122,7 @@ In [docs/decisions.md](docs/decisions.md):
- Goal 7.2 holds while a first template can be written with no escape; a literal brace still takes `{{` or `}}`
- The locales are Ethnologue 2026's ten most-spoken languages, each in the country with the most speakers of it, the Nordic countries and Germany
- A country with no open address register covering it whole ships its places and postal codes without streets
- The deb, rpm and apk repositories live on Cloudsmith, and the image on GHCR
# Scoring run
+1 -1
View File
@@ -18,7 +18,7 @@ RUN python3 -m unittest discover -s /release-tooling -p '*_test.py' && touch /te
FROM goreleaser/goreleaser:v2.18.3 AS packaging
WORKDIR /check
COPY release-tooling/goreleaser.yaml ./
RUN git init -q && goreleaser check --config goreleaser.yaml && touch /tested
RUN git init -q && git remote add origin https://github.com/larvit/fejkdata.git && goreleaser check --config goreleaser.yaml && touch /tested
FROM portable
# BuildKit skips a stage nothing copies from.
+7 -3
View File
@@ -985,7 +985,8 @@ changed. A consumer's data, code and scripts keep working across a minor or a pa
minor only adds, and a major is the only release that changes what exists. Seeded output
is the exception: any release, a patch too, may change what a seed draws. Where you
compare seeded output, install one exact version
(`go install github.com/larvit/fejkdata/cmd/fejkdata@vX.Y.Z`; `go.mod` already pins one),
(`go install github.com/larvit/fejkdata/cmd/fejkdata@vX.Y.Z`, the image
`ghcr.io/larvit/fejkdata:X.Y.Z` or a release's binary; `go.mod` already pins one),
and regenerate seeded fixtures and expected values when you raise that version.
| Surface | Major | Minor |
@@ -1255,8 +1256,11 @@ publishes nothing. The new release then runs the Packages workflow, which attach
binaries and packages, pushes the image, uploads to Cloudsmith and updates the Homebrew
tap, the Scoop bucket and the AUR. It reads the repository secrets `TAP_GITHUB_TOKEN`,
a token that can push to `larvit/homebrew-tap` and `larvit/scoop-bucket`, `AUR_KEY`,
the AUR account's SSH key, and `CLOUDSMITH_API_KEY`. To package a release again, run
the workflow by hand with its tag; it replaces what it uploaded before.
the AUR account's SSH key, and `CLOUDSMITH_API_KEY`; the image's GHCR package must be
public. To package a release again, run the workflow by hand with its tag; it replaces
what it uploaded before. For a tag older than the latest release it updates only that
release's assets and Cloudsmith, since the tap, the bucket, the AUR and the image's
`latest` each hold one version.
## License
+13
View File
@@ -962,3 +962,16 @@ inherit both terms. So that country's `address` prints no street, and the README
section says why beside each such country. This works against goal 2.1 until
`todo.md` item 218 gives those countries streets. Valid until the maintainer accepts ODbL
data, or the country opens a register.
## The deb, rpm and apk repositories live on Cloudsmith, and the image on GHCR
2026-10-13, decided by the maintainer. Serves goal 1.1; technical principle: prefer the
vendor-neutral option, sidestepped deliberately (`~/.claude/principles/technical-principles.md`).
A repository apt, dnf or apk can upgrade from needs signed indexes for three formats.
Self-hosting them on GitHub Pages would put two signing keys and the tooling that rebuilds
every index into this repository. Cloudsmith signs and indexes all three, for free to open
source, so the tie buys that tooling, not a capability only Cloudsmith has. Leaving it costs
each Linux user one new setup line. Every release's assets carry the same packages, so
nothing is lost in a move. GHCR sits beside the code and takes CI's own token; the image is
OCI, so leaving it changes only the image path. Valid while Cloudsmith's open-source plan
stays free.
+16 -19
View File
@@ -36,19 +36,16 @@ changelog:
disable: true
release:
github:
owner: larvit
name: fejkdata
mode: keep-existing
replace_existing_artifacts: true
nfpms:
- package_name: fejkdata
vendor: Larvit
homepage: https://github.com/larvit/fejkdata
maintainer: Lilleman auf Larv <lilleman@larvit.se>
description: Render locale-aware fake data from JSON templates.
license: MIT
homepage: &homepage https://github.com/larvit/fejkdata
maintainer: &maintainer Lilleman auf Larv <lilleman@larvit.se>
description: &description Render locale-aware fake data from JSON templates
license: &license MIT
formats: [deb, rpm, apk]
contents:
- src: LICENSE
@@ -64,16 +61,16 @@ dockers_v2:
- latest
dockerfile: ../tooling/release-tooling/image.Dockerfile
labels:
org.opencontainers.image.source: https://github.com/larvit/fejkdata
org.opencontainers.image.source: *homepage
org.opencontainers.image.version: "{{ .Version }}"
org.opencontainers.image.licenses: MIT
org.opencontainers.image.licenses: *license
homebrew_casks:
- name: fejkdata
binaries: [fejkdata]
homepage: https://github.com/larvit/fejkdata
description: Render locale-aware fake data from JSON templates
license: MIT
homepage: *homepage
description: *description
license: *license
repository:
owner: larvit
name: homebrew-tap
@@ -87,9 +84,9 @@ homebrew_casks:
scoops:
- name: fejkdata
homepage: https://github.com/larvit/fejkdata
description: Render locale-aware fake data from JSON templates.
license: MIT
homepage: *homepage
description: *description
license: *license
repository:
owner: larvit
name: scoop-bucket
@@ -97,11 +94,11 @@ scoops:
aurs:
- name: fejkdata-bin
homepage: https://github.com/larvit/fejkdata
description: Render locale-aware fake data from JSON templates
license: MIT
homepage: *homepage
description: *description
license: *license
maintainers:
- Lilleman auf Larv <lilleman@larvit.se>
- *maintainer
private_key: "{{ .Env.AUR_KEY }}"
git_url: ssh://aur@aur.archlinux.org/fejkdata-bin.git
commit_author:
+16 -9
View File
@@ -26,23 +26,24 @@ def request(path: str, data: dict | None = None):
return json.load(resp)
def main() -> int:
def publish() -> tuple[int, str | None]:
"""Publish the release, returning the exit code and the tag it published, if any."""
with open("CHANGELOG.md", encoding="utf-8") as f:
top = HEADING.search(f.read())
if top is None:
print("CHANGELOG.md has no `## [...]` heading", file=sys.stderr)
return 1
return 1, None
version, body = top.group(1), top.group(2).strip()
if version == "Unreleased":
print("top heading is Unreleased; nothing to publish")
return 0
return 0, None
if not re.fullmatch(r"\d+\.\d+\.\d+", version):
print(f"top heading `[{version}]` is neither Unreleased nor X.Y.Z", file=sys.stderr)
return 1
return 1, None
tag = f"v{version}"
try:
print(f"{tag} already published: {request(f'releases/tags/{tag}')['html_url']}")
return 0
return 0, None
except urllib.error.HTTPError as e:
if e.code != 404:
raise
@@ -53,15 +54,21 @@ def main() -> int:
at = request(f"git/tags/{at['sha']}")["object"]
if at["sha"] != sha:
print(f"{tag} exists at {at['sha']}, not {sha}; the version is burnt, bump the heading", file=sys.stderr)
return 1
return 1, None
except urllib.error.HTTPError as e:
if e.code != 404:
raise
release = request("releases", {"body": body, "name": tag, "tag_name": tag, "target_commitish": sha})
print(release["html_url"])
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
f.write(f"tag={tag}\n")
return 0
return 0, tag
def main() -> int:
code, tag = publish()
if tag:
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
f.write(f"tag={tag}\n")
return code
if __name__ == "__main__":
+4 -9
View File
@@ -25,8 +25,7 @@ class TagGuard(unittest.TestCase):
self.addCleanup(os.chdir, cwd)
with open("CHANGELOG.md", "w", encoding="utf-8") as f:
f.write("# Changelog\n\n## [1.2.3]\n\n- A change.\n")
open("output", "w").close()
env = mock.patch.dict(os.environ, {"SHA": SHA, "GITHUB_OUTPUT": os.path.abspath("output")})
env = mock.patch.dict(os.environ, {"SHA": SHA})
env.start()
self.addCleanup(env.stop)
@@ -44,13 +43,9 @@ class TagGuard(unittest.TestCase):
with mock.patch.object(publish_release, "request", request), \
redirect_stdout(io.StringIO()), redirect_stderr(io.StringIO()):
code = publish_release.main()
code, self.tag = publish_release.publish()
return code, created
def output(self):
with open("output", encoding="utf-8") as f:
return f.read()
def test_a_tag_at_another_commit_publishes_nothing(self):
code, created = self.publish({"git/ref/tags/v1.2.3": {"object": {"sha": OTHER, "type": "commit"}}})
self.assertEqual((code, created), (1, []))
@@ -73,11 +68,11 @@ class TagGuard(unittest.TestCase):
code, created = self.publish({})
self.assertEqual(code, 0)
self.assertEqual(created, [{"body": "- A change.", "name": "v1.2.3", "tag_name": "v1.2.3", "target_commitish": SHA}])
self.assertEqual(self.output(), "tag=v1.2.3\n")
self.assertEqual(self.tag, "v1.2.3")
def test_a_published_release_publishes_nothing(self):
code, created = self.publish({"releases/tags/v1.2.3": {"html_url": "https://forge.example/release"}})
self.assertEqual((code, created, self.output()), (0, [], ""))
self.assertEqual((code, created, self.tag), (0, [], None))
if __name__ == "__main__":
+32
View File
@@ -11,9 +11,41 @@
"matchStrings": [
"image: (?<depName>golang):\\$\\{GO_VERSION:-(?<currentValue>[^}]+)\\}"
]
},
{
"customType": "regex",
"datasourceTemplate": "docker",
"depNameTemplate": "goreleaser/goreleaser",
"description": "the GoReleaser the Packages workflow runs, which must match the image the gate checks its config with",
"managerFilePatterns": [
"/^\\.github/workflows/packages\\.yml$/"
],
"matchStrings": [
"goreleaser/goreleaser-action@\\S+\\s+with:\\s+version: (?<currentValue>\\S+)"
]
},
{
"customType": "regex",
"datasourceTemplate": "pypi",
"depNameTemplate": "cloudsmith-cli",
"description": "the Cloudsmith CLI the Packages workflow installs",
"managerFilePatterns": [
"/^\\.github/workflows/packages\\.yml$/"
],
"matchStrings": [
"cloudsmith-cli==(?<currentValue>\\S+)"
]
}
],
"extends": [
"config:recommended"
],
"packageRules": [
{
"groupName": "GoReleaser",
"matchPackageNames": [
"goreleaser/goreleaser"
]
}
]
}