Skip to content

Publish verified Boatstack release #489

Publish verified Boatstack release

Publish verified Boatstack release #489

Workflow file for this run

# Boatstack-owned control plane.
name: Publish verified Boatstack release
on:
schedule:
- cron: "0 2 * * *"
workflow_dispatch:
permissions:
actions: read
contents: read
concurrency:
group: auto-release-boatstack
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false
- name: Verify exact current main and CI
id: source
env:
GH_TOKEN: ${{ github.token }}
RELEASE_SOURCE: ${{ github.sha }}
shell: bash
run: |
[[ "$GITHUB_REF" == "refs/heads/main" ]] || {
echo "BLOCKED: stable releases must run from main, not $GITHUB_REF." >&2
exit 2
}
checked_out="$(git rev-parse HEAD)"
[[ "$checked_out" == "$RELEASE_SOURCE" ]] || {
echo "BLOCKED: checked-out source $checked_out does not match $RELEASE_SOURCE." >&2
exit 2
}
remote_main="$(git ls-remote origin refs/heads/main | awk 'NR == 1 {print $1}')"
[[ -n "$remote_main" && "$remote_main" == "$RELEASE_SOURCE" ]] || {
echo "BLOCKED: main moved from $RELEASE_SOURCE to ${remote_main:-unknown}." >&2
exit 2
}
verified_sha="$(gh run list \
--repo "$GITHUB_REPOSITORY" \
--workflow .github/workflows/ci.yml \
--branch main \
--event push \
--commit "$RELEASE_SOURCE" \
--status success \
--limit 1 \
--json headSha \
--jq '.[0].headSha // ""')"
[[ "$verified_sha" == "$RELEASE_SOURCE" ]] || {
echo "BLOCKED: exact source $RELEASE_SOURCE has no successful main push CI run." >&2
exit 2
}
echo "sha=$RELEASE_SOURCE" >> "$GITHUB_OUTPUT"
- name: Detect pending release-bearing changes
id: classify
env:
RELEASE_SOURCE: ${{ steps.source.outputs.sha }}
run: >-
python3 .github/scripts/release_candidate.py
--repo .
--source "$RELEASE_SOURCE"
--github-output "$GITHUB_OUTPUT"
- name: Report current release state
if: steps.classify.outputs.release_required != 'true'
run: echo "Boatstack has no verified unreleased changes; no release was created."
- name: Create repository automation token
if: steps.classify.outputs.release_required == 'true'
id: app-token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.BOATSTACK_APP_CLIENT_ID }}
private-key: ${{ secrets.BOATSTACK_APP_PRIVATE_KEY }}
owner: operatorstack
repositories: boatstack
permission-contents: write
- uses: actions/checkout@v7
if: steps.classify.outputs.release_required == 'true'
with:
ref: ${{ steps.source.outputs.sha }}
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
- name: Create next verified patch tag
if: steps.classify.outputs.release_required == 'true'
env:
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
EXPECTED_LATEST_TAG: ${{ steps.classify.outputs.latest_tag }}
EXPECTED_NEXT_TAG: ${{ steps.classify.outputs.next_tag }}
RELEASE_SOURCE: ${{ steps.source.outputs.sha }}
shell: bash
run: |
checked_out="$(git rev-parse HEAD)"
[[ "$checked_out" == "$RELEASE_SOURCE" ]] || {
echo "BLOCKED: checked-out source changed before publication." >&2
exit 2
}
remote_main="$(git ls-remote origin refs/heads/main | awk 'NR == 1 {print $1}')"
[[ -n "$remote_main" && "$remote_main" == "$RELEASE_SOURCE" ]] || {
echo "BLOCKED: main moved before publication; retry against the new head." >&2
exit 2
}
git fetch --force --tags origin
candidate="$(python3 .github/scripts/release_candidate.py --repo . --source "$RELEASE_SOURCE")"
release_required="$(jq -r .release_required <<< "$candidate")"
latest_tag="$(jq -r .latest_tag <<< "$candidate")"
next_tag="$(jq -r .next_tag <<< "$candidate")"
[[ "$release_required" == true ]] || {
echo "BLOCKED: no unreleased change remains after refreshing tags." >&2
exit 2
}
[[ "$latest_tag" == "$EXPECTED_LATEST_TAG" && "$next_tag" == "$EXPECTED_NEXT_TAG" ]] || {
echo "BLOCKED: stable release tags changed during this run." >&2
exit 2
}
if git ls-remote --exit-code --tags origin "refs/tags/$next_tag" >/dev/null 2>&1; then
echo "BLOCKED: tag already exists: $next_tag" >&2
exit 2
fi
git config user.name "${APP_SLUG}[bot]"
git config user.email "${APP_SLUG}[bot]@users.noreply.github.com"
git tag -a "$next_tag" -m "Boatstack $next_tag" "$RELEASE_SOURCE"
git push origin "refs/tags/$next_tag"
echo "Published verified release tag $next_tag from $RELEASE_SOURCE."