changelog: release notes from the commit messages, in every format a release needs

The commits since the previous tag of the same kind become notes.md, notes.txt, a
CHANGELOG.md of the last releases, an nfpm changelog for the deb and rpm, notes.json
and 500-character store notes; with release: 'true' they also go onto the tag's
release on the Releases tab, inside a marked block that leaves the rest of the text
alone. The same file reads the history from Gitea's API, for the packages publisher.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-11-01 18:05:18 +01:00
1 parent 34af870f6a
commit 3ca302921f
3 files changed
+433 -4

No files matched your search

+58 -4
View File
@@ -1,13 +1,15 @@
# app-stores
Two Gitea Actions that publish an app from any project's release workflow: `play` for Google Play and `apple` for
the App Store. Each is one Python file talking to the store's own web API: Google's Play Developer API and Apple's
App Store Connect API. Nothing else is needed on the runner beyond Python 3.9 or newer and `openssl`, which every
Three Gitea Actions for any project's release workflow: `play` publishes to Google Play, `apple` to the App Store,
and `changelog` writes what changed since the previous release from the commit messages, in every format a release
needs. Each is one Python file; the store ones talk to the store's own web API (Google's Play Developer API, Apple's
App Store Connect API). Nothing else is needed on the runner beyond Python 3.9 or newer and `openssl`, which every
Linux and Mac runner has. No fastlane, no Ruby, no service in between.
```yaml
- uses: https://git.thepra.dev/Arasaka/app-stores/play@v1
- uses: https://git.thepra.dev/Arasaka/app-stores/apple@v1
- uses: https://git.thepra.dev/Arasaka/app-stores/changelog@v1
```
> Status: checked against fakes of both stores built from their published API definitions (Google's discovery
@@ -148,7 +150,59 @@ Neither store lets an API create an app, so the first steps happen in their cons
6. Create the TestFlight groups. An external group needs the Beta App Review information (contact and notes)
filled in once.
## Notes
## Release notes: `changelog`
**The commit messages are the changelog** (owner, 1 November 2026: every commit between two releases, the whole
message, in every place a release is shown). The action takes the commits since the previous tag of the same kind
(`prefix`: `v`, `app-v`, `server-v`; the previous one is the highest lower version among those tags), leaves out merges
and the `Co-Authored-By`, `Signed-off-by` and Claude attribution lines, and writes, into `output`:
| File | For |
|---|---|
| `notes.md` | "What's new in <name> <version>": each commit's subject as a heading, its body below; the Releases tab, a page |
| `notes.txt` | the same as plain text: Velopack's `--releaseNotes`, an update dialog, F-Droid |
| `CHANGELOG.md` | the last `history` releases, newest first |
| `changelog.yml` | the same releases for nfpm's `changelog:` (chglog), so the deb carries `changelog.Debian.gz` and the rpm its `%changelog`; bodies become one line per paragraph |
| `notes.json` | everything above as data |
| `store/en-US.txt` | the subjects, cut to 500 characters, for the `notes` folder of `play` and `apple` |
With `release: 'true'` it also writes the notes onto this tag's release on the Releases tab: it creates the release
when there is none, and otherwise replaces only what sits between `<!-- changelog -->` and `<!-- /changelog -->`
(adding that block at the end the first time), so a release text written by the project itself stays. Give the job
`permissions: contents: write`.
```yaml
- uses: actions/checkout@v4
- name: Release notes
id: notes
uses: https://git.thepra.dev/Arasaka/app-stores/changelog@v1
with:
prefix: app-v
name: SimpleSchool
packager: Scuola di lingua Cinese di Varese <info@scuolacinese.org>
output: release-notes
release: 'true'
- run: echo "the notes are in ${{ steps.notes.outputs.dir }}, the previous release was ${{ steps.notes.outputs.previous }}"
```
| Input | | |
|---|---|---|
| `prefix` | `v` | the tags of this kind of release |
| `tag` | the tag that started the run | the release being made; empty on a branch, which writes everything since the newest tag as `unreleased` |
| `name` | the repository's name | the product's name in headings |
| `strip` | | a regular expression removed from the start of each subject (me-web? removes its `“me-web?” 0.15.4: `) |
| `packager` | `Arasaka di Hvozdikov Yevheniy <packages@arasaka.software>` | `Name <address>` for the deb and rpm changelogs: the package's own maintainer |
| `history` | `20` | how many releases `CHANGELOG.md` and the package changelog carry |
| `output` | `release-notes` | the folder |
| `release` | `false` | `true` writes the notes onto the Releases tab |
| `token` | the job token | only with `release` |
A shallow checkout is fetched in full, with its tags, before anything is read. A first release lists its newest 100
commits. The same file reads the history from Gitea's API instead of a checkout (`--gitea URL --repository
owner/name`, token in `READ_TOKEN`); that is how `thepra/packages.arasaka.software` writes the notes for every app it
publishes, so the two can never tell a release differently.
## Store notes
One file per language, the file name being the code: Google uses language and region (`en-US`, `it-IT`), Apple
its own list (`en-US`, `it`, `de-DE`). Empty files are skipped. Apple takes no release notes for an app's first