Skip to content

Commit cebb0dd

Browse files
committed
Refactor draft release workflow: Implement shared release engine with categorised changelog generation
- Introduced a composite action for draft releases, consolidating the release process. - Added a new script to compile release notes from a shared changelog template. - Created a structured YAML template for changelog categories and exclusions. - Updated README to reflect new structure and usage instructions. - Removed previously implemented reusable workflow and streamlined the workflow for better maintainability.
1 parent b7d296f commit cebb0dd

5 files changed

Lines changed: 198 additions & 262 deletions

File tree

.github/workflows/draft-release.yaml

Lines changed: 0 additions & 116 deletions
This file was deleted.

draft-release/README.md

Lines changed: 78 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
1-
# Draft Release
2-
3-
A reusable GitHub Actions [workflow](../.github/workflows/draft-release.yaml)
4-
that automates the creation of a draft GitHub release with an auto-generated
5-
changelog, driven by a centralised shared release template.
6-
7-
## Features
8-
9-
- **Zero Boilerplate:** Fetches a centralised configuration template from
10-
[MetOffice/growss](https://github.com/MetOffice/growss) and injects it into
11-
the calling repository at runtime. Downstream repositories do not need to
12-
maintain a local `.github/release.yml` file.
13-
- **Strict Label Prioritisation:** Generates a structured changelog dynamically
14-
grouped by PR labels using the centralised
15-
[release template](../.github/templates/release.yml).
16-
- **Safe Review Gate:** Creates the release in **draft** state, allowing
17-
maintainers to audit or adjust notes before going live.
18-
- **Secure Architecture:** Native platform authentication passes `GITHUB_TOKEN`
19-
seamlessly to handle both public and private repository cross-boundary
20-
checkouts securely.
21-
- **Skip Empty Releases:** Automatically skips draft release creation if no
22-
merged PRs are found within the release window.
23-
- Tags the draft release with the triggering Git ref name (e.g. `v1.2.3`).
1+
# Draft Release - a Shared Global Release Engine
2+
3+
Composite [action](action.yml) that generates categorised draft release notes
4+
from merged pull requests and updates (or creates) a draft GitHub Release for
5+
the current tag.
6+
7+
## Location
8+
9+
This action is intentionally located at repository root level and has the
10+
following components:
11+
12+
- `draft-release/action.yml` (composite action)
13+
- `draft-release/bin/compile-release-notes.sh` (compiler script)
14+
- `draft-release/templates/release.yml` (release category template)
15+
- `draft-release/README.md` (this document)
16+
17+
The changelog template source of truth is `draft-release/templates/release.yml`.
2418

2519
## Changelog Categories for Simulation Systems
2620

@@ -36,10 +30,15 @@ for comprehensive changelog organisation.
3630
| ⚠️ Deprecations | `deprecated` | Features or APIs that are being phased out, but still functional. |
3731
| 🐛 Bug Fixes | `bugfix` | Code corrections or hotfixes resolving functional issues. |
3832
| ✨ New Features | `feature` | Customer-facing features, enhancements, or structural additions. |
39-
| 🔬 Scientific & Algorithmic Updates | `science`, `technical` | **science:** Domain-specific mathematical changes or model updates.<br>**technical:** Deep algorithmic optimisations or background logic shifts. |
33+
| 🔬 Scientific & Algorithmic Updates | `science`, `technical` | **science**: Domain-specific mathematical changes or model updates.<br>**technical**: Deep algorithmic optimisations or background logic shifts. |
4034
| 📚 Documentation | `documentation` | Changes isolated to READMEs, inline code docstrings, scientific documentation, working practices, or other non-functional documentation updates. |
4135
| ⚡ Performance Improvements | `optimisation` | Direct speed execution metrics, runtime improvements, memory, storage, or other resource optimisations. |
4236
| ♻️ Refactoring | `refactor` | Code cleanup, modularisation, or other internal improvements without behavior changes. |
37+
| 🛠️ Maintenance | `build`, `chore`, `ci` | **build**: Changes affecting build tools or external compiler toolchains.<br>**chore**: General housekeeping, licence updates, or minor administrative tasks.<br>**ci**: Changes to GitHub Actions workflows, CI/CD pipelines, or other automation.|
38+
39+
> [!NOTE]
40+
> First time contributors are added automatically when the merged PR has an
41+
> `author_association` of `FIRST_TIME_CONTRIBUTOR`.
4342
4443
### Excluded Labels
4544

@@ -48,109 +47,85 @@ output entirely, regardless of any other labels they carry:
4847

4948
| Label | Purpose |
5049
| ----- | ------- |
51-
| `build` | Changes affecting build tools or external compiler toolchains. |
52-
| `chore` | General housekeeping, license updates, or minor administrative tasks. |
53-
| `ci` | Modifications to GitHub Actions workflows or automation systems. |
5450
| `ignore-changelog` | Escape-hatch label to manually suppress a specific PR from the logs. |
5551
| `test` | Changes related to testing frameworks or test cases. |
5652
| `wip` | Work in progress PRs that are not ready for release. |
5753

58-
## Permissions
59-
60-
The calling execution job block must explicitly declare `contents: write` to
61-
authorise the native GitHub CLI runner to write assets and publish release
62-
footprints:
63-
64-
```yaml
65-
permissions:
66-
contents: write
67-
pull-requests: read
68-
```
69-
70-
## Usage
54+
## How It Works
7155

72-
To ensure deterministic behaviour across versions, use the same reference for
73-
both the workflow and `template-ref`.
56+
1. Checks out the caller repository into `local-code` with full tag history.
57+
2. Runs `draft-release/bin/compile-release-notes.sh` to:
58+
- find release commits,
59+
- map merged PRs to changelog categories from
60+
`draft-release/templates/release.yml`,
61+
- write `release-notes.md` and set `has_commits` output.
62+
3. If commits exist, creates or updates a draft release for the current tag.
7463

75-
### Variant A: Production Tag Auto-Trigger (Recommended)
64+
```mermaid
65+
graph TD
66+
%% Define Node Styles for Scannability
67+
classDef trigger style fill:#f9f,stroke:#333,stroke-width:2px,font-weight:bold;
68+
classDef step style fill:#bbf,stroke:#333,stroke-width:1px;
69+
classDef desc style fill:#fff,stroke:#666,stroke-width:1px,stroke-dasharray: 5 5,font-size:12px;
7670
77-
```yaml
78-
name: Draft Release Deployment
71+
%% Workflow Connections
72+
Trigger([Tag Push Event]) --> Step1[1. Checkout Caller Repository]
73+
Step1 --> Step2[2. Compile Release Notes]
74+
Step2 --> Step3{Has Merged PRs?}
75+
Step3 -->|Yes| Step4[3. Create or Update Draft Release]
76+
Step3 -->|No| Skip[Skip Release Creation]
7977
80-
on:
81-
push:
82-
tags:
83-
- "v*" # Triggers automatically for semantic production tags
78+
%% Step Explanations (Side-nodes)
79+
Step1 -.-> Desc1[Checks out the calling repository into <code>local-code</code><br>with full history and tags]
80+
Step2 -.-> Desc2[Executes <code>compile-release-notes.sh</code><br>using action-local template <code>release.yml</code>]
81+
Step4 -.-> Desc3[Executes <code>gh release create/edit</code><br>with categorised changelog notes in draft state]
82+
Skip -.-> Desc4[Workflow completes early<br>if no merged PRs found in release window]
8483
85-
jobs:
86-
release:
87-
uses: MetOffice/growss/.github/workflows/draft-release.yaml@main
88-
with:
89-
template-ref: "main"
90-
permissions:
91-
contents: write
92-
pull-requests: read
84+
%% Assign Classes to Nodes
85+
class Trigger trigger;
86+
class Step1,Step2,Step3,Step4,Skip step;
87+
class Desc1,Desc2,Desc3,Desc4 desc;
9388
```
9489

95-
### Variant B: Hybrid Trigger (Tag Push + Manual Run)
90+
## Usage
9691

9792
```yaml
98-
name: Draft Release Deployment
93+
name: Automated Release Notes
9994

10095
on:
101-
push:
102-
tags:
103-
- "v*"
104-
workflow_dispatch:
96+
push:
97+
tags:
98+
- "v*"
10599

106100
jobs:
107-
release:
108-
uses: MetOffice/growss/.github/workflows/draft-release.yaml@main
109-
with:
110-
template-ref: "main"
111-
permissions:
112-
contents: write
101+
release:
102+
runs-on: ubuntu-slim
103+
permissions:
104+
contents: write
105+
pull-requests: read
106+
steps:
107+
- name: Draft Release
108+
uses: MetOffice/growss/draft-release@main # or tag or sha
113109
```
114110
115-
> [!WARNING] **Important Trigger Caveat:** The draft release title, tag mapping,
116-
> and PR delta history bounds are determined dynamically from
117-
> `${{ github.ref_name }}`.
118-
>
119-
> - Triggering via **Tag Push** ensures the title matches the target version tag
120-
> (e.g. `v1.2.3`).
121-
> - Triggering via **Workflow Dispatch** uses the active branch name (e.g.
122-
> `main`) as the release name target, which will include all historical
123-
> unreleased commits instead of a bounded tag delta window.
111+
## Required Permissions
124112
125-
## How It Works
113+
- `contents: write` to create/edit draft releases.
114+
- `pull-requests: read` to read merged PR metadata for changelog generation.
126115

127-
```mermaid
128-
graph TD
129-
%% Define Node Styles for Scannability
130-
classDef trigger style fill:#f9f,stroke:#333,stroke-width:2px,font-weight:bold;
131-
classDef step style fill:#bbf,stroke:#333,stroke-width:1px;
132-
classDef desc style fill:#fff,stroke:#666,stroke-width:1px,stroke-dasharray: 5 5,font-size:12px;
116+
## Outputs
133117

134-
%% Workflow Connections
135-
Trigger([Tag Push Event]) --> Step1[1. Fetch Shared Template]
136-
Step1 --> Step2[2. Fetch Target Workspace]
137-
Step2 --> Step3[3. Compile Release Notes]
138-
Step3 --> Step4{Has Merged PRs?}
139-
Step4 -->|Yes| Step5[4. Create Draft Release]
140-
Step4 -->|No| Skip[Skip Release Creation]
118+
The compile step exposes:
141119

142-
%% Step Explanations (Side-nodes)
143-
Step1 -.-> Desc1[Checks out <code>MetOffice/growss</code> to parse <code>.github/templates/release.yml</code>]
144-
Step2 -.-> Desc2[Checks out the calling repository's<br>workspace directory branch with full history]
145-
Step3 -.-> Desc3[Executes <code>.github/bin/compile-release-notes.sh</code><br>to extract merged PRs and categorise by labels]
146-
Step5 -.-> Desc4[Executes <code>gh release create/edit</code><br>with categorised changelog notes in draft state]
147-
Skip -.-> Desc5[Workflow completes early<br>if no merged PRs found in release window]
120+
- `has_commits`: `true` when release notes were generated from commits,
121+
otherwise `false`.
148122

149-
%% Assign Classes to Nodes
150-
class Trigger trigger;
151-
class Step1,Step2,Step3,Step4,Step5,Skip step;
152-
class Desc1,Desc2,Desc3,Desc4,Desc5 desc;
153-
```
123+
## Notes
124+
125+
- The action uses `${{ github.token }}` internally for `gh` API commands.
126+
- `release-notes.md` is generated in the GitHub Actions workspace root.
127+
- The release body is grouped by labels defined in
128+
`draft-release/templates/release.yml`.
154129

155130
## Licence
156131

draft-release/action.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# ------------------------------------------------------------------------------
2+
# (c) Crown copyright Met Office. All rights reserved.
3+
# The file LICENCE, distributed with this code, contains details of the terms
4+
# under which the code may be used.
5+
# ------------------------------------------------------------------------------
6+
7+
name: Shared Global Release Engine
8+
description: Automates draft releases using centralised assets
9+
10+
runs:
11+
using: "composite"
12+
steps:
13+
- name: Checkout caller repository with full history
14+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
15+
with:
16+
path: local-code
17+
fetch-depth: 0
18+
fetch-tags: true
19+
persist-credentials: false
20+
21+
- name: Compile categorised release notes from template labels
22+
id: compile-release-notes
23+
shell: bash
24+
env:
25+
GH_TOKEN: ${{ github.token }}
26+
CALLER_REPO: ${{ github.repository }}
27+
GITHUB_REF_NAME: ${{ github.ref_name }}
28+
GITHUB_SHA: ${{ github.sha }}
29+
run: |
30+
set -euo pipefail
31+
bash "${{ github.action_path }}/bin/compile-release-notes.sh" \
32+
"$CALLER_REPO" "$GITHUB_SHA" "$GITHUB_REF_NAME"
33+
34+
- if: steps.compile-release-notes.outputs.has_commits == 'true'
35+
shell: bash
36+
working-directory: local-code
37+
env:
38+
GH_TOKEN: ${{ github.token }}
39+
GITHUB_REF_NAME: ${{ github.ref_name }}
40+
run: |
41+
set -euo pipefail
42+
TITLE="Release ${GITHUB_REF_NAME} (Draft)"
43+
if gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then
44+
gh release edit "${GITHUB_REF_NAME}" --draft --title "${TITLE}" \
45+
--notes-file ../release-notes.md
46+
else
47+
gh release create "${GITHUB_REF_NAME}" --draft --title "${TITLE}" \
48+
--notes-file ../release-notes.md
49+
fi

0 commit comments

Comments
 (0)