Files
InvoiceShelf/.github/workflows/release.yaml
Darko Gjorgjijoski 1a403984fe ci: check the release notes before building (#716)
The CHANGELOG.md section was read after composer, pnpm and the frontend
build had all run, so tagging without notes spent a full build before
failing on something knowable in the first seconds. It only needs PHP, so
it now runs directly after the PHP setup.

Noticed while dry-running a tag with no section against #715: the run
correctly refused to publish, but took a whole build to say so.
2026-07-29 15:56:29 +02:00

98 lines
3.4 KiB
YAML

name: Release
# Tagging is the trigger. Both spellings are accepted because every tag to date
# is bare (3.0.0-alpha.1, 2.4.2) while this workflow previously listened only for
# "v*" — so it had never once fired, and tagging by the established convention
# produced silence.
on:
push:
tags:
- '[0-9]*'
- 'v[0-9]*'
permissions:
contents: write
# Which major owns GitHub's "Latest release" pointer. Same value and same meaning
# as in docker.yaml, which gates the moving :latest image tags on it — bump both
# on this branch, and docker.yaml on 2.x, when 3.0.0 GA is tagged.
env:
LATEST_MAJOR: "2"
jobs:
tests:
uses: ./.github/workflows/tests.yaml
release:
name: Build & Release
needs:
- tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.4
extensions: bcmath, curl, dom, gd, imagick, json, libxml, mbstring, pcntl, pdo, pdo_mysql, zip
tools: composer
- name: Read the release notes from CHANGELOG.md
env:
TAG: ${{ github.ref_name }}
run: |
if ! php .github/scripts/changelog-section.php "$TAG" > /tmp/notes.md; then
echo "::error::No CHANGELOG.md section for $TAG — add one before tagging."
exit 1
fi
echo "Using the CHANGELOG.md section for $TAG"
- name: Install pnpm
uses: pnpm/action-setup@v6
- name: Use Node.js 24
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
# `make dist` owns the artifact: it installs composer/pnpm dependencies,
# builds the frontend, assembles the package and generates the manifest.
# This workflow used to hand-copy the same file list a second time, with
# docker.yaml then overwriting the result — two copies of one list, and job
# ordering deciding what users received.
- name: Build the release package
run: make clean dist
# Created as a draft with the package already attached, then published as a
# separate step. `release: published` therefore fires only once the tests
# have passed and the asset is in place, so downstream registration can
# never race the upload — and a failed run leaves no release at all rather
# than a published one nobody can download.
- name: Create the draft release
uses: softprops/action-gh-release@v3
with:
files: InvoiceShelf.zip
body_path: /tmp/notes.md
draft: true
prerelease: ${{ contains(github.ref_name, '-') }}
- name: Publish it
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
# GitHub's "Latest release" pointer, gated exactly as docker.yaml gates
# its moving image tags — so a 3.0.0 alpha cannot displace 2.4.x as the
# release users are shown first while 2.x is still the stable line.
IS_LATEST: ${{ !contains(github.ref_name, '-') && startsWith(github.ref_name, format('{0}.', env.LATEST_MAJOR)) }}
run: |
if [ "$IS_LATEST" = "true" ]; then
gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --draft=false --latest
else
gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --draft=false --latest=false
fi
echo "Published $TAG (latest=$IS_LATEST)"