Files
Miles WardandClaude Fable 5.1 6fa37315d8
Build and Test / Build and Test (push) Successful in 1m12s
publish: force gzip on every layer so no blob exceeds Cloudflare's 100 MB
bitnami/minideb ships its base layer as an uncompressed 133 MB tar; with
the containerd image store docker push sends it as-is and Cloudflare in
front of Gitea answers 413. Build with force-compression=true (base layer
becomes ~43 MB) and document why.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-17 19:27:05 -04:00

197 lines
8.4 KiB
Markdown

# Publishing to Gitea
Releases are cut by hand. Each one needs a Gitea **personal access token**
with the `write:repository` and `write:package` scopes, which is why none of
this is automated: the token should only ever exist in your own shell or
keychain.
On a Mac that has pushed before, git already holds the token in the login
keychain and nothing needs exporting. Elsewhere, create one at **Gitea →
Settings → Applications → Generate New Token** and export it once per shell
session:
```bash
export GITEA_TOKEN='<paste-your-token>'
```
There is no Java or Maven on the Mac; every build step below runs in a
container, so Docker is the only local requirement.
## 0. Where the code comes from
`origin` is this Gitea repository; `upstream` is
<https://github.com/purnell569/MusicBot>, the fork this copy was taken from.
```bash
git remote -v
# origin https://gitea.milesward.dev/mward4/MusicBot.git
# upstream https://github.com/purnell569/MusicBot.git
```
To pick up a new upstream version:
```bash
git fetch upstream --tags
git merge upstream/main
```
Upstream's own tags are kept in this repository under `upstream/<tag>`
(`upstream/v0.7.1`, …) so they never collide with this repository's release
tags. After a fetch, copy any new upstream tag across before tagging here:
```bash
git tag upstream/v0.7.2 v0.7.2 && git tag -d v0.7.2
```
## 1. Cut the version
The version is `<version>` in `pom.xml` and tracks upstream's (`0.7.1`). Do
not bump it independently; a merge from upstream brings the new number with
it. Then update the `IMAGE_TAG` and `GIT_REF` defaults in
`scripts/build-and-publish-unraid.sh`, the pinned tag in the README, and
commit. Tag with single quotes — the name starts with a dollar sign:
```bash
git tag -a v0.7.1 -m '$uicide Music Bot v0.7.1'
```
Tags are `vMAJOR.MINOR.PATCH` and match the pom version; the image carries the
same number without the `v` (`0.7.1`) plus a moving `latest`.
## 2. Push the code and the tags
```bash
git push origin main --follow-tags && git push origin 'refs/tags/upstream/*'
```
With a token in the environment rather than the keychain, prefix each git
command with `git -c "http.extraHeader=Authorization: token $GITEA_TOKEN"`.
## 3. Validate
Runs the same thing upstream's CI runs: 676 unit tests and 48 integration
tests, then packages the JAR. The named volume keeps the Maven repository
between runs so the second run is a couple of minutes.
```bash
docker run --rm -v "$PWD":/build -w /build -v musicbot-m2:/root/.m2 maven:3.9.12-eclipse-temurin-25-alpine mvn -B -ntp verify
```
Expect `BUILD SUCCESS` and `target/JMusicBot-0.7.1-All.jar`. This is the full
JAR (all platforms' natives), the one to attach to the release; the Docker
build uses the `docker` Maven profile, which strips the Windows and macOS
natives.
## 4. Build the image
The Maven stage runs on the builder's native architecture (the JAR is
architecture-neutral), so only the jlink and runtime stages run under
emulation when building `linux/amd64` on Apple Silicon. Two flags matter:
- `--provenance=false --sbom=false`: Unraid's Docker cannot load the
attestation manifests BuildKit attaches by default.
- `--output type=docker,compression=gzip,force-compression=true`:
`bitnami/minideb` publishes its base layer as an **uncompressed 133 MB
tar**. A daemon using the containerd image store pushes blobs exactly as
stored, and Gitea sits behind Cloudflare, which rejects any request body
over 100 MB with `413 Payload Too Large`. Forcing gzip on every layer takes
that one to about 43 MB; the JAR layer (61 MB) and the JRE layer (41 MB) are
already under the limit.
```bash
docker buildx build --pull --platform linux/amd64 --provenance=false --sbom=false --build-arg "GIT_SHA=$(git rev-parse HEAD)" --build-arg VERSION=0.7.1 --build-arg "BUILD_TIMESTAMP=$(git log -1 --format=%cI)" -t gitea.milesward.dev/mward4/musicbot:0.7.1 -t gitea.milesward.dev/mward4/musicbot:latest --output type=docker,compression=gzip,force-compression=true .
```
Smoke test: an empty, writable data directory must produce a `config.txt` and
the "edit it and start again" message, exit code 0. A named volume is used
because Colima only shares `$HOME` into the VM; a bind mount of a path outside
it silently becomes an empty root-owned directory inside the VM.
```bash
docker volume create musicbot-smoke && docker run --rm -v musicbot-smoke:/musicbot alpine:3.20 sh -c 'touch /musicbot/.keep && chown -R 99:100 /musicbot' && docker run --rm --platform linux/amd64 --user 99:100 -v musicbot-smoke:/musicbot gitea.milesward.dev/mward4/musicbot:0.7.1; docker run --rm -v musicbot-smoke:/musicbot alpine:3.20 ls -la /musicbot; docker volume rm musicbot-smoke
```
## 5. Push the image
**Never run `docker login` or `docker logout` for `gitea.milesward.dev` on a
Mac where git keeps the token in the keychain.** Docker's `osxkeychain`
credential helper maps to the same keychain item as git's: `login` fails with
"The specified item already exists in the keychain (-25299)", and `logout`
silently deletes the credential git was using, so the next `git push` fails
with a bare 401. Write the auth into a throwaway config instead — a config
that already contains an `auths` entry makes the CLI skip the helper:
```bash
export DOCKER_CONFIG="$(mktemp -d)" && printf '{"auths":{"gitea.milesward.dev":{"auth":"%s"}}}' "$(printf 'mward4:%s' "$GITEA_TOKEN" | base64 | tr -d '\n')" > "$DOCKER_CONFIG/config.json" && docker push gitea.milesward.dev/mward4/musicbot:0.7.1 && docker push gitea.milesward.dev/mward4/musicbot:latest; rm -rf "$DOCKER_CONFIG"; unset DOCKER_CONFIG
```
On a Colima host, `export DOCKER_HOST="unix://$HOME/.colima/default/docker.sock"`
first: an isolated `DOCKER_CONFIG` has no `currentContext`, and the CLI would
otherwise look for `/var/run/docker.sock`.
On a fresh machine, `scripts/build-and-publish-unraid.sh` does the clone,
build and push in one pass using that same private-config approach:
```bash
GITEA_TOKEN="$GITEA_TOKEN" ./scripts/build-and-publish-unraid.sh
```
## 6. Verify the pull
The repo and package are public, so this needs no credentials — which is
exactly how Unraid will fetch it. An empty `DOCKER_CONFIG` keeps the keychain
out of the test:
```bash
export DOCKER_CONFIG="$(mktemp -d)" && docker pull --platform linux/amd64 gitea.milesward.dev/mward4/musicbot:0.7.1; rm -rf "$DOCKER_CONFIG"; unset DOCKER_CONFIG
```
## 7. Create the release
One release per version, on the `vX.Y.Z` tag, named `$uicide Music Bot vX.Y.Z`,
with the pull command and what changed. Gitea only accepts the attachment
types in its allow-list (`.zip`, `.txt`, … but not `.jar`), so the JAR ships
zipped alongside a checksum file:
```bash
cd target && cp JMusicBot-0.7.1-All.jar JMusicBot-0.7.1.jar && zip -q JMusicBot-0.7.1.zip JMusicBot-0.7.1.jar && shasum -a 256 JMusicBot-0.7.1.jar JMusicBot-0.7.1.zip > SHA256SUMS.txt && cd ..
```
Create the release and upload the two files (the release `id` comes back in
the first response):
```bash
curl -s -X POST https://gitea.milesward.dev/api/v1/repos/mward4/MusicBot/releases -H "Authorization: token $GITEA_TOKEN" -H 'Content-Type: application/json' -d @release.json
curl -s -X POST "https://gitea.milesward.dev/api/v1/repos/mward4/MusicBot/releases/<id>/assets?name=JMusicBot-0.7.1.zip" -H "Authorization: token $GITEA_TOKEN" -F attachment=@target/JMusicBot-0.7.1.zip
curl -s -X POST "https://gitea.milesward.dev/api/v1/repos/mward4/MusicBot/releases/<id>/assets?name=SHA256SUMS.txt" -H "Authorization: token $GITEA_TOKEN" -F attachment=@target/SHA256SUMS.txt
```
`release.json` is `{"tag_name":"v0.7.1","target_commitish":"main","name":"$uicide Music Bot v0.7.1","body":"…","draft":false,"prerelease":false}`.
## 8. Deploy on Unraid
Add a container with Repository set to:
```
gitea.milesward.dev/mward4/musicbot:0.7.1
```
(or `:latest`). Map `/musicbot` to `/mnt/user/appdata/musicbot`, no ports,
bridge networking, and `--user 99:100` in Extra Parameters so the bot runs as
Unraid's usual `nobody:users` and the appdata folder needs no chown. Or import
`deploy/unraid/musicbot.xml` into
`/boot/config/plugins/dockerMan/templates-user/` to get all of that prefilled.
First start writes `config.txt` into the appdata folder and stops. Set `token`
and `owner` in it, start again. To upgrade an existing container, change the
tag and apply; `config.txt`, `serversettings.json` and `Playlists/` all live in
the appdata mount and carry over.
## Afterwards
If a token was exported, make sure it is not left in the shell:
```bash
unset GITEA_TOKEN
```