# 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='' ``` 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 , 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/` (`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 `` 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//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//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 ```