Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- Keep trigger shapes intentional. This repo currently uses pull request triggers such as `opened`, `synchronize`, `reopened`, and `ready_for_review` where needed.
- Preserve `persist-credentials: false` on checkout steps unless there is a concrete reason to change authentication behavior.
- Reuse local composite actions in `.github/actions/` when they already capture setup shared across workflows.
- Treat release behavior as merge-driven: pushes to `master` and manual `workflow_dispatch` runs feed the current release flow documented in `.github/RELEASING.md`.
- Treat release behavior as split by stability: pushes to `master` update the rolling prerelease, and manual `workflow_dispatch` runs create protected stable releases as documented in `.github/RELEASING.md`.

## Verification

Expand Down
161 changes: 51 additions & 110 deletions .github/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,132 +7,73 @@ Recommended versioning for the next release line:
- Start the next stable release at `v0.10.0`.
- Keep the binary's embedded version in source as a normal semver such as `0.10.0`.
- Let merge-driven builds update a single rolling `prerelease` tag.
- Reserve semver tags like `v0.10.x` for stable releases.
- Reserve stable semver tags like `v0.10.0` for protected stable releases.

### 1. New Feature or Breaking‑Change Release (Minor/Major)
### Prerelease

1. **Merge & Verify**
1. Merge to `master`.
2. The `Prerelease` workflow runs tests, builds the seven supported binaries,
assembles `gomud-ALL-datafiles.zip` and `SHA256SUMS.txt`, generates
attestations, and updates the mutable `prerelease` GitHub prerelease.
3. The workflow may move the `prerelease` tag and clobber existing prerelease
assets. This is intentional so the rolling prerelease remains mutable.
4. The prerelease is marked as a prerelease and is not marked as `Latest`.

- Merge all feature or breaking‑change PRs into `master`.
- Ensure CI (tests, linter, codegen) all pass on `master`.
Pull requests do not publish release binaries. The generic `CI` workflow runs
the PR test gate; `master` release testing happens inside the `Prerelease`
workflow to avoid duplicate full race-test runs on merge.

1. **Determine Version Bump**
### Stable Release

- **Major** (`X.0.0`) when you make incompatible changes
- **Minor** (`0.Y.0`) when you add functionality in a backward compatible manner
- **Patch** (`0.0.Z`) when you make backward compatible bug fixes
1. Confirm the source version in `main.go` is correct.
2. Run the `Stable Release` workflow manually with a new stable semver
`release_tag`, such as `v0.10.0`.
3. The workflow fails before publishing if the requested tag or GitHub release
already exists.
4. The `publish` job uses the `stable-release` GitHub Environment. Configure
required reviewers on that environment in repository settings.
5. After environment approval, the workflow creates a draft release, uploads
assets, attaches release notes, then publishes the stable release.

1. **Merge to `master`**
- Merging to `master` triggers the `Release` workflow automatically.
Stable release workflow policy does not move tags, use `--clobber`, or replace
existing releases. Keep repository-wide immutable releases disabled so the
rolling `prerelease` release can remain mutable.

Or, for a manual test without merging:
- Run the `Release` workflow with `workflow_dispatch`.
- Optionally set `release_tag` if you want the run to upload assets to a
specific existing or new release tag instead of the default prerelease tag.
- Run `Release Latest Assets` when you want the Actions UI path that updates
the repo's current latest GitHub release tag without any manual input.
### Assets

2. **Monitor Release**
- GitHub Actions will:
- Run `go generate ./...`
- Build per-platform binaries with `main.version` set from `main.go`
- Replace the rolling `prerelease` tag and GitHub prerelease
- Archive `_datafiles` as `gomud-ALL-datafiles-prerelease.zip`
- Generate `gomud-prerelease-SHA256SUMS.txt`
- Leave the release unmarked as `Latest`
Prerelease and stable release assets are defined by
`.github/scripts/release-assets.sh`. Each release includes the supported
OS/architecture binaries, `gomud-ALL-datafiles.zip`, and `SHA256SUMS.txt`.

3. **Announce**
- After review, a repo owner can edit the release in GitHub and promote it to
`Latest`.
- Share the release link with the team or via configured notifications.
Release notes are generated by `.github/scripts/release-notes.sh` and include `Overview`,
`Downloads`, `Install From Source`, `Manual Binary Install`, `Verify Provenance`,
and `Changes`. The `Changes` section appends GitHub auto-generated notes from
`releases/generate-notes`.

---
### Verification

### 2. Merge-Driven Prerelease Policy
After a release workflow succeeds:

1. **Pull requests do not publish release binaries**
- PRs should run normal CI only.
- Confirm the expected assets are attached.
- Confirm `SHA256SUMS.txt` verifies downloaded assets with `sha256sum -c`.
- Confirm binaries and `SHA256SUMS.txt` have artifact attestations.
- For stable releases, confirm the release tag points at the intended commit and
the release is no longer a draft.

2. **Merges to `master` do publish release binaries**
- A push to `master` runs the `Release` workflow and replaces the rolling
`prerelease`.

3. **Manual test runs can also publish prereleases**
- `workflow_dispatch` can be used to refresh the rolling `prerelease`
without merging.
- If you provide `release_tag`, the run publishes to that tag instead.
- `Release Latest Assets` is the no-input manual workflow that resolves the
current latest GitHub release tag and publishes to it.

4. **Rolling release naming**
- The release tag is always `prerelease`.
- The release notes record the commit SHA and publish time for the current build.
- Numbered releases such as `v0.10.0` remain the permanent download history.

---

### 3. Manual Test Release Flow

1. **Run the release workflow manually**
- Use `workflow_dispatch` when you want a test release
without merging to `master`.
- Leave `release_tag` blank to use the default manual prerelease naming.
- Set `release_tag` when you want the run to upload assets to a specific
existing or new release tag.
- Run `Release Latest Assets` when you want the no-input Actions UI path
that uploads assets to whichever tag GitHub currently considers the latest
release.

2. **Verify the GitHub release**
- Confirm the workflow succeeds.
- Confirm the targeted release now points at the expected commit.
- Confirm the per-platform binaries are attached.
- Confirm the `_datafiles` zip asset is attached.
- Confirm the checksum manifest asset is attached.
- For default manual prerelease runs, confirm GitHub marks the release as a
prerelease and does not mark it as `Latest`.

3. **Clean up if needed**
- No cleanup is normally required for the rolling `prerelease` path because
the next successful run replaces it.
- Tag-targeted manual runs use the tag you requested, so cleanup is only
needed if you created a temporary release tag for testing.

---

### FAQ / Guidelines
### FAQ

- **Does every merge to `master` trigger a release?**
Yes - every push to `master` runs the release workflow and publishes a prerelease.
That prerelease is the rolling `prerelease` entry, not a newly named release.

- **Is auto-tagging enabled?**
Stable semver tags are not generated automatically. The merge-driven workflow
updates the rolling `prerelease` tag instead.

- **Can I create a test release without merging to `master`?**
Yes - run the `Release` workflow manually with `workflow_dispatch`. That keeps PR
submissions clean while still allowing an on-demand refresh of `prerelease`.
Set `release_tag` when you want the run to publish assets to a specific tag.
Run `Release Latest Assets` when you want the no-input path that targets the
current latest release.

- **Are workflow-created releases stable releases?**
No - the workflow creates prereleases. A repo owner must manually promote a release
to `Latest` in GitHub when it is approved.

- **What assets should a release include?**
Each release should include separate per-platform binaries, a `_datafiles` zip,
and a checksum manifest so testers can download only what they need and still
verify the assets.
Yes. Every push to `master` runs the `Prerelease` workflow and updates the
rolling `prerelease`.

- **What tag format should we use going forward?**
Keep the source/binary version on the `0.10.x` line. Use `prerelease` for the
rolling master build and semver tags for stable releases.
- **Can the workflow publish a stable release from an existing tag?**
No. Stable releases must use a new semver tag. Existing tags and releases fail
the workflow.

- **When should I bump minor vs. patch?**
- **Minor** for new, backward‑compatible features.
- **Patch** for bug fixes or documentation tweaks.
- **Can stable assets be replaced?**
No. The stable workflow does not clobber assets. Publish a new patch release
tag instead.

- **What about `go generate` directives?**
The workflow runs `go generate ./...` automatically before each build.
Release workflows run `go generate ./...` before tests and before each
cross-compiled build.
1 change: 1 addition & 0 deletions .github/act/actrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Repo-local act config placeholder.
3 changes: 0 additions & 3 deletions .github/act/push_tag.json

This file was deleted.

6 changes: 6 additions & 0 deletions .github/act/stable_release.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"ref": "refs/heads/master",
"inputs": {
"release_tag": "v0.0.0"
}
}
40 changes: 0 additions & 40 deletions .github/actions/codegen-and-test/action.yml

This file was deleted.

12 changes: 12 additions & 0 deletions .github/actions/go-checks/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: "Go Checks"
description: "Refresh generated code and run the race-enabled Go test suite"
runs:
using: "composite"
steps:
- name: Generate code
run: go generate ./...
shell: bash
- name: Run Go tests
run: go test -race ./...
shell: bash
2 changes: 1 addition & 1 deletion .github/actions/setup-go/action.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: "Setup Go"
description: "Setup Go using go.mod"
description: "Install the Go toolchain pinned by go.mod and enable module cache"
runs:
using: "composite"
steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ updates:
- "*"

- package-ecosystem: "docker"
directory: "/docker/provisioning"
directory: "/provisioning"
schedule:
interval: "monthly"
time: "06:00"
Expand All @@ -62,7 +62,7 @@ updates:
- "patch"

- package-ecosystem: "docker"
directory: "/docker/terminal"
directory: "/provisioning/terminal"
schedule:
interval: "monthly"
time: "06:00"
Expand Down
21 changes: 21 additions & 0 deletions .github/scripts/assemble-release-assets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd -- "${script_dir}/../.." && pwd)"
bin_dir="${RELEASE_BIN_DIR:-bin}"
datafiles_archive="${DATAFILES_ARCHIVE:-gomud-ALL-datafiles.zip}"
checksums_file="${CHECKSUMS_FILE:-SHA256SUMS.txt}"

cd "$repo_root"

mapfile -t checksum_assets < <(
DATAFILES_ARCHIVE="$datafiles_archive" \
CHECKSUMS_FILE="$checksums_file" \
"${script_dir}/release-assets.sh" checksum-names
)

zip -qr "${bin_dir}/${datafiles_archive}" _datafiles

cd "$bin_dir"
sha256sum "${checksum_assets[@]}" >"$checksums_file"
32 changes: 32 additions & 0 deletions .github/scripts/build-release-binaries.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail

script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd -- "${script_dir}/../.." && pwd)"
dist_dir="${RELEASE_DIST_DIR:-dist}"
binary_version="${BINARY_VERSION:-}"

cd "$repo_root"
mkdir -p "$dist_dir"

while IFS='|' read -r _label goos goarch goarm asset; do
target="${goos}/${goarch}"
if [ -n "$goarm" ]; then
target="${target} GOARM=${goarm}"
fi

build_args=()
if [ -n "$binary_version" ]; then
build_args+=(-ldflags "-X main.version=${binary_version}")
fi

echo "::group::Build ${asset} (${target})"
if [ -n "$goarm" ]; then
env GOOS="$goos" GOARCH="$goarch" GOARM="$goarm" \
go build "${build_args[@]}" -o "${dist_dir}/${asset}" .
else
env GOOS="$goos" GOARCH="$goarch" \
go build "${build_args[@]}" -o "${dist_dir}/${asset}" .
fi
echo "::endgroup::"
done < <("${script_dir}/release-assets.sh" targets)
38 changes: 38 additions & 0 deletions .github/scripts/ci-local-act.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail

script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd -- "${script_dir}/../.." && pwd)"

ACT_FLAGS="${ACT_FLAGS:---pull=false -P ubuntu-24.04=catthehacker/ubuntu:act-latest}"
ACT_DRYRUN_SECRETS="${ACT_DRYRUN_SECRETS:--s DISCORD_WEBHOOK_URL=https://example.invalid/webhook}"
XDG_CONFIG_HOME="${ACT_CONFIG_HOME:-${repo_root}/.github}"
export XDG_CONFIG_HOME

mkdir -p "${XDG_CONFIG_HOME}/act"
touch "${XDG_CONFIG_HOME}/act/actrc"

run_act() {
local event="$1"
local event_file="$2"
local workflow="$3"
shift 3

act ${ACT_FLAGS:-} --dryrun "$event" "$@" \
-e "$event_file" \
-W "$workflow"
}

# CI combines the old lint and PR test workflows. Dry-run both event shapes
# because pull requests cancel superseded runs while pushes to master do not.
run_act push .github/act/push_master.json .github/workflows/ci.yml
run_act pull_request .github/act/pull_request.json .github/workflows/ci.yml
run_act pull_request .github/act/pull_request.json \
.github/workflows/discord-notify.yml ${ACT_DRYRUN_SECRETS:-}
run_act push .github/act/push_master.json .github/workflows/prerelease.yml
run_act workflow_dispatch .github/act/stable_release.json \
.github/workflows/stable-release.yml
run_act push .github/act/push_master.json \
.github/workflows/docker-package.yml ${ACT_DRYRUN_SECRETS:-}
run_act pull_request .github/act/pull_request.json \
.github/workflows/docker-package.yml ${ACT_DRYRUN_SECRETS:-}
Loading