mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Step 5 of Nightly only (Ludovic, 2026-10-28). release.yml keeps its triggers and its decision (5 player-facing pull requests settled 5 minutes, or the oldest 6 hours, release-now, skip-release, RELEASES_PAUSED, release-blocker, a commit where checks and parity-main passed), counted since the commit the published Nightly was built on. When it decides, it starts `nightly build` on that commit (`base`) instead of building vX.Y.Z: - release_train: the last release of the game is the published `nightly`; one Nightly build at a time and at most 12 a day (nightly_set's cap), except for release-now and force (`urgent`). - nightly-build.yml: an `urgent` input passes the cap; the notes say the launcher installs it. - docs: release-flow (Game releases, Nightly only, Notes, Rolling back), versioning. The last vX.Y.Z stays GitHub's latest for launchers and servers from before the switch. Co-Authored-By: Claude Opus 5.5 <[email protected]>
91 lines
4.7 KiB
YAML
91 lines
4.7 KiB
YAML
name: release
|
|
# Automatic game releases (docs/release-flow.md). Horizon ships only the Nightly since 2026-10-28 (Ludovic): this
|
|
# workflow is the batch train that decides when a new Nightly goes out, and `nightly build` (nightly-build.yml) builds,
|
|
# signs and publishes it. After every green `checks` run on main, every 10 minutes (so a batch waiting for merges to
|
|
# settle goes out without another merge) and by hand, tools/release_train.py decides, for a commit whose checks and
|
|
# parity-main (the vanilla harnesses on main) both passed: a Nightly when 5 pull requests merged since the published
|
|
# Nightly changed what players run and the newest merged 5 minutes ago, or when the oldest has waited 6 hours, or at
|
|
# once for a pull request labelled release-now. RELEASES_PAUSED=1 (repository variable) stops it; so does an open issue
|
|
# labelled release-blocker, forced runs too (nightly-build.yml checks again before publishing). One Nightly build at a
|
|
# time, at most 12 a day (release-now and force=true pass the cap). No `vX.Y.Z` game release is made any more.
|
|
# gh workflow run release --ref <branch> # dry run: decides, starts nothing
|
|
# gh workflow run release -f dry_run=false -f force=true # on main: a Nightly now, whatever the rules say
|
|
on:
|
|
workflow_run:
|
|
# A game release ships a commit both passed on (parity-main runs on batches: the newest commit it passed on).
|
|
workflows: [checks, parity-main]
|
|
types: [completed]
|
|
branches: [main]
|
|
schedule:
|
|
- cron: '2-59/10 * * * *'
|
|
workflow_dispatch:
|
|
inputs:
|
|
dry_run:
|
|
description: Decide only; start no Nightly build
|
|
type: boolean
|
|
default: true
|
|
force:
|
|
description: A Nightly even without player-facing changes, before merges settle, while paused or past the day's cap
|
|
type: boolean
|
|
default: false
|
|
permissions:
|
|
contents: read
|
|
# The open release-blocker issues (decide).
|
|
issues: read
|
|
# The checks, parity-main and nightly build runs (decide).
|
|
actions: read
|
|
concurrency:
|
|
# One release at a time. A hand-started dry run publishes nothing, so it can't race a real release: it gets a
|
|
# group of its own. In the shared group, a release queued behind it replaced it as the pending run and GitHub
|
|
# cancelled it (#2779's dry run, 2026-10-27), which happens all day while releases go out in batches.
|
|
group: game-release${{ github.event_name == 'workflow_dispatch' && inputs.dry_run && '-dry-run' || '' }}
|
|
cancel-in-progress: false
|
|
jobs:
|
|
decide:
|
|
if: >-
|
|
github.event_name != 'workflow_run' ||
|
|
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push')
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
outputs:
|
|
release: ${{ steps.decide.outputs.release }}
|
|
head: ${{ steps.decide.outputs.head }}
|
|
urgent: ${{ steps.decide.outputs.urgent }}
|
|
dry_run: ${{ steps.mode.outputs.dry_run }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
- id: mode
|
|
# A dry run never publishes; a hand-started run off main is always one.
|
|
run: echo "dry_run=${{ github.event_name == 'workflow_dispatch' && (inputs.dry_run || github.ref != 'refs/heads/main') }}" >> "$GITHUB_OUTPUT"
|
|
- id: decide
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
RELEASES_PAUSED: ${{ vars.RELEASES_PAUSED }}
|
|
# The commit whose checks or parity-main just passed; a dry run builds the branch it was started on;
|
|
# otherwise main's newest commit with green checks and parity-main.
|
|
HEAD_SHA: ${{ github.event.workflow_run.head_sha || (steps.mode.outputs.dry_run == 'true' && github.sha) || '' }}
|
|
FORCE: ${{ inputs.force && 'yes' || '' }}
|
|
run: |
|
|
python3 tools/release_train.py decide --kind game ${HEAD_SHA:+--head "$HEAD_SHA"} ${FORCE:+--force} | tee decision.json
|
|
echo "### $(jq -r '.reason' decision.json)" >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
nightly:
|
|
# The Nightly is the release: nightly-build.yml builds the decided commit plus the reviewers' picks, signs it for
|
|
# the Nightly channel and publishes it once its smoke test passed and no release-blocker is open.
|
|
needs: decide
|
|
if: needs.decide.outputs.release == 'true' && needs.decide.outputs.dry_run != 'true'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions:
|
|
actions: write # starts `nightly build`
|
|
steps:
|
|
- env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
HEAD_SHA: ${{ needs.decide.outputs.head }}
|
|
URGENT: ${{ needs.decide.outputs.urgent }}
|
|
run: |
|
|
gh workflow run nightly-build.yml --repo "$GITHUB_REPOSITORY" --ref main -f base="$HEAD_SHA" -f urgent="$URGENT"
|
|
echo "### Started \`nightly build\` on ${HEAD_SHA:0:9}" >> "$GITHUB_STEP_SUMMARY"
|