Skip to content

Commit f899cb3

Browse files
authored
Chwa1 sort dist (#235)
<!-- markdownlint-disable-next-line first-line-heading --> ## Description dist/ folder is not getting checked in ## Context Currently calls for this action are failing ## Type of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. --> - [ ] Refactoring (non-breaking change) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would change existing functionality) - [X] Bug fix (non-breaking change which fixes an issue) ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [X] I am familiar with the [contributing guidelines](../docs/CONTRIBUTING.md) - [X] I have followed the code style of the project - [ ] I have added tests to cover my changes - [X] I have updated the documentation accordingly - [ ] This PR is a result of pair or mob programming --- ## Sensitive Information Declaration To ensure the utmost confidentiality and protect your and others privacy, we kindly ask you to NOT including [PII (Personal Identifiable Information) / PID (Personal Identifiable Data)](https://digital.nhs.uk/data-and-information/keeping-data-safe-and-benefitting-the-public) or any other sensitive data in this PR (Pull Request) and the codebase changes. We will remove any PR that do contain any sensitive information. We really appreciate your cooperation in this matter. - [X] I confirm that neither PII/PID nor sensitive data are included in this PR and the codebase changes.
1 parent 5635f68 commit f899cb3

13 files changed

Lines changed: 1130 additions & 11 deletions

File tree

.github/workflows/cicd-1-pull-request.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,33 @@ on:
99
pull_request:
1010
types: [opened, reopened]
1111

12+
permissions:
13+
contents: read
14+
pull-requests: write
15+
issues: write
16+
id-token: write
17+
1218
jobs:
19+
guard-dist:
20+
name: "Guard: prevent committed dist changes"
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: "Checkout code"
24+
uses: actions/checkout@v4
25+
- name: "Fail if dist/ contains changes"
26+
run: |
27+
if git ls-files --error-unmatch dist 2>/dev/null; then
28+
echo "dist/ directory is committed. This repository expects dist to be added only by release automation." >&2
29+
echo "Remove dist/ changes from your PR. They will be generated during release." >&2
30+
exit 1
31+
else
32+
echo "No committed dist/ directory detected. Proceeding.";
33+
fi
1334
metadata:
1435
name: "Set CI/CD metadata"
1536
runs-on: ubuntu-latest
1637
timeout-minutes: 1
38+
needs: [guard-dist]
1739
outputs:
1840
build_datetime_london: ${{ steps.variables.outputs.build_datetime_london }}
1941
build_datetime: ${{ steps.variables.outputs.build_datetime }}
@@ -27,11 +49,25 @@ jobs:
2749
steps:
2850
- name: "Checkout code"
2951
uses: actions/checkout@v4
52+
with:
53+
fetch-depth: 0
54+
- name: "Set up Node.js"
55+
uses: actions/setup-node@v4
56+
with:
57+
node-version: 16
58+
cache: npm
59+
- name: "Install dependencies"
60+
run: npm ci
61+
- name: "Build action bundle (dry-run context)"
62+
run: npm run package
3063
- name: "Semantic Release"
3164
uses: cycjimmy/semantic-release-action@v4.1.1
3265
id: semantic
3366
with:
3467
dry_run: true
68+
extra_plugins: |
69+
@semantic-release/git
70+
@semantic-release/npm
3571
env:
3672
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3773
- name: "Set CI/CD variables"

.github/workflows/cicd-2-publish.yaml

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ on:
66
branches: [ "main" ]
77
types: ["completed"]
88

9+
permissions:
10+
contents: write
11+
issues: write
12+
pull-requests: write
13+
914
jobs:
1015
metadata:
1116
name: "Set CI/CD metadata"
@@ -50,13 +55,77 @@ jobs:
5055
steps:
5156
- name: "Checkout code"
5257
uses: actions/checkout@v4
58+
with:
59+
fetch-depth: 0
60+
- name: "Generate GitHub App token"
61+
uses: actions/create-github-app-token@v1
62+
id: app-token
63+
with:
64+
app-id: ${{ secrets.APP_ID }}
65+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
66+
- name: "Import GPG key and configure signing"
67+
if: ${{ secrets.APP_SIGNING_KEY != '' }}
68+
env:
69+
APP_SIGNING_KEY: ${{ secrets.APP_SIGNING_KEY }}
70+
GPG_KEY_ID: ${{ secrets.APP_SIGNING_KEY_ID }}
71+
APP_SIGNING_KEY_PASSPHRASE: ${{ secrets.APP_SIGNING_KEY_PASSPHRASE }}
72+
run: |
73+
# Import the GPG signing key
74+
echo "$APP_SIGNING_KEY" | gpg --batch --import
75+
76+
# Mark repository as safe (defensive measure for git operations)
77+
git config --global --add safe.directory "$GITHUB_WORKSPACE"
78+
79+
# Configure git identity (will show as GitHub App)
80+
git config --global user.name "notify-msteams-action release bot"
81+
git config --global user.email "noreply@github.com"
82+
83+
# Configure commit signing with custom GPG wrapper (from repository)
84+
git config --global user.signingkey "$GPG_KEY_ID"
85+
git config --global commit.gpgsign true
86+
git config --global tag.gpgSign true
87+
git config --global gpg.program "$GITHUB_WORKSPACE/scripts/gpg-wrapper.sh"
88+
89+
# Configure GPG for non-interactive mode
90+
mkdir -p ~/.gnupg
91+
echo 'pinentry-mode loopback' >> ~/.gnupg/gpg.conf
92+
echo 'allow-loopback-pinentry' >> ~/.gnupg/gpg-agent.conf || true
93+
chmod 700 ~/.gnupg
94+
95+
# Set GPG_TTY for terminal interaction
96+
export GPG_TTY=$(tty)
97+
98+
# Log key fingerprint for audit (not the key itself)
99+
gpg --list-keys --with-colons | awk -F: '/^fpr/ {print "Using GPG key: " $10; exit}'
100+
- name: "Set up Node.js"
101+
uses: actions/setup-node@v4
102+
with:
103+
node-version: 16
104+
cache: npm
105+
- name: "Install dependencies"
106+
run: npm ci
107+
- name: "Build action bundle"
108+
run: npm run package
53109
- name: "Semantic Release"
54110
uses: cycjimmy/semantic-release-action@v4.1.1
55111
id: semantic
56112
with:
57113
dry_run: ${{github.event_name == 'pull_request'}}
114+
extra_plugins: |
115+
@semantic-release/git
116+
@semantic-release/npm
58117
env:
59-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
118+
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
119+
- name: "Update major tag"
120+
if: steps.semantic.outputs.new_release_published == 'true'
121+
run: |
122+
version="${{ steps.semantic.outputs.new_release_version }}"
123+
major_tag="v${version%%.*}"
124+
git config user.name "github-actions"
125+
git config user.email "github-actions@users.noreply.github.com"
126+
git fetch --tags origin
127+
git tag -f "${major_tag}" "v${version}"
128+
git push origin -f "${major_tag}"
60129
- name: "Output new release details"
61130
if: steps.semantic.outputs.new_release_published == 'true'
62131
run: |

.gitleaksignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# SEE: https://github.com/gitleaks/gitleaks/blob/master/README.md#gitleaksignore
22

33
cd9c0efec38c5d63053dd865e5d4e207c0760d91:docs/guides/Perform_static_analysis.md:generic-api-key:37
4+
a1b549dd793a0aa43eddc53455d6e08c6f8ab37f:docs/developer-guides/Release_Signing_and_GitHub_App.md:generic-api-key:146

.releaserc

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
{
2+
"branches": ["main"],
3+
"tagFormat": "v${version}",
24
"plugins": [
35
"@semantic-release/commit-analyzer",
46
"@semantic-release/release-notes-generator",
7+
["@semantic-release/changelog", { "changelogFile": "CHANGELOG.md" }],
8+
["@semantic-release/npm", { "npmPublish": false }],
9+
["@semantic-release/git", {
10+
"assets": [
11+
"CHANGELOG.md",
12+
"package.json",
13+
"package-lock.json",
14+
"dist/**",
15+
"licenses.txt",
16+
"VERSION"
17+
],
18+
"message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
19+
}],
520
"@semantic-release/github"
6-
],
7-
"branches": [
8-
"main"
921
]
1022
}

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Conventional Commits](https://www.conventionalcommits.org/) and is generated automatically by **semantic-release**.
6+
7+
## [Unreleased]
8+
9+
- Pending changes not yet released.

README.md

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ This action uses typescript and is built from the [typescript-action](https://gi
1919

2020
This action can be called as part of your [GitHub action](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) workflows. to achieve this follow these steps:
2121

22-
Add the following section to your existing workflow file:
22+
Add the following section to your existing workflow file (pin to a released major tag; the moving `v1` tag will always point at the latest backward-compatible release within the same major):
2323

2424
```yml
25-
- name: Testing action to notify Teams
26-
uses: nhs-england-tools/notify-msteams-action@v0.0.4
25+
- name: Notify Teams
26+
uses: nhs-england-tools/notify-msteams-action@v1
2727
with:
2828
github-token: ${{ secrets.GITHUB_TOKEN }}
2929
teams-webhook-url: ${{ secrets.TEAMS_WEBHOOK_URL }}
3030
message-title: "Replace with an appropriate title"
3131
message-text: "Replace with appropriate text"
32-
link: https://google.com
32+
link: https://example.org
3333
```
3434
3535
Follow the instructions [to add an Incoming Webhook](https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook?tabs=dotnet) to the Teams channel of your choice.
@@ -76,6 +76,58 @@ An example of how the notification could appear in Microsoft Teams is provided:
7676

7777
![Microsoft Teams notification showing the adaptive card being displayed to the user for a "Pull request opened" event](docs/images/msteams-action-notification.png)
7878

79+
### Versioning & Release Process
80+
81+
This repository uses [semantic-release](https://semantic-release.gitbook.io/) to automatically:
82+
83+
- Determine the next version from Conventional Commit messages.
84+
- Generate release notes and create a GitHub Release with tag `vX.Y.Z`.
85+
- Build and bundle the Action (TypeScript -> single `dist/index.js`).
86+
- Commit the generated `dist/` assets, the updated `package.json` (and `package-lock.json`) version metadata, and the `VERSION` file as part of the release commit.
87+
- Update the moving major tag (e.g. `v1`) to point to the latest release of that major.
88+
- Update the consolidated changelog in `CHANGELOG.md`.
89+
90+
Consumers SHOULD depend on a major tag (e.g. `@v1`) or a fully qualified tag (e.g. `@v1.2.3`). The `main` branch is not guaranteed to contain built artefacts and must not be referenced directly in external workflows.
91+
92+
For information about signed release commits, using a GitHub App for least-privilege release automation, key rotation, and revocation, see: [Release Signing & GitHub App](docs/developer-guides/Release_Signing_and_GitHub_App.md).
93+
94+
### Developer Guides Index
95+
96+
Centralised list of developer-facing guides in this repository:
97+
98+
| Guide | Description |
99+
|-------|-------------|
100+
| [Bash and Make](docs/developer-guides/Bash_and_Make.md) | Conventions and patterns for Make targets and supporting Bash scripts, including target signatures and reuse. |
101+
| [Scripting Docker](docs/developer-guides/Scripting_Docker.md) | Opinionated Docker workflows: build, test, version (CalVer patterns), push and image management conventions. |
102+
| [Scripting Terraform](docs/developer-guides/Scripting_Terraform.md) | Terraform automation via Make wrappers, structure, state handling, quality enforcement and CI integration. |
103+
| [Release Process](docs/developer-guides/Release_Process.md) | End-to-end semantic-release driven publish pipeline and rationale for build-at-release strategy. |
104+
| [Release Signing & GitHub App](docs/developer-guides/Release_Signing_and_GitHub_App.md) | Secure release provenance: GitHub App token usage, GPG key generation, rotation and revocation procedures. |
105+
| [Sign Git commits](docs/user-guides/Sign_Git_commits.md) | How to configure and use GPG or SSH for commit signature verification locally and in CI. |
106+
107+
### Contributing (Important Policy About `dist/`)
108+
109+
Do not commit the `dist/` directory in pull requests. The CI pipeline will fail the PR if `dist/` is present. The release workflow rebuilds and commits `dist/` automatically. This keeps review noise low and ensures reproducible builds.
110+
111+
To test local modifications, run:
112+
113+
```bash
114+
npm ci
115+
npm run package
116+
```
117+
118+
Then you can reference the action locally in a workflow within this repository using `uses: ./` (which will use the freshly generated `dist/`).
119+
120+
### Conventional Commits
121+
122+
To trigger proper version bumps, use the Conventional Commits format, for example:
123+
124+
- `feat: add support for message colour`
125+
- `fix: correct Teams webhook error handling`
126+
- `chore: update dependencies`
127+
- `docs: clarify usage section`
128+
129+
Breaking changes must include `!` after the type or the phrase `BREAKING CHANGE:` in the footer.
130+
79131
## Contacts
80132

81133
Provide a way to contact the owners of this project. It can be a team, an individual or information on the means of getting in touch via active communication channels, e.g. opening a GitHub discussion, raising an issue, etc.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Release Process
2+
3+
This repository uses **semantic-release** to fully automate versioning and publishing of the GitHub Action.
4+
5+
For details on securely signing release commits and using a GitHub App token, see the companion guide: [Release Signing & GitHub App](./Release_Signing_and_GitHub_App.md).
6+
7+
## Overview
8+
9+
1. Developers raise pull requests with source changes only (no `dist/` directory committed).
10+
2. The pull request workflow performs a semantic-release dry run to preview the next version.
11+
3. Once changes are merged to `main`, the publish workflow:
12+
13+
- Checks out the code.
14+
- Installs dependencies and builds the production bundle with `npm run package` (emits `dist/index.js` + `licenses.txt`).
15+
- Runs `semantic-release` which:
16+
- Calculates the next version from Conventional Commits.
17+
- Updates release notes and creates a Git tag `vX.Y.Z`.
18+
- Commits the generated `dist/` assets, updated `package.json` / `package-lock.json` version metadata, and the `VERSION` file via the `@semantic-release/git` plugin.
19+
- Creates a GitHub Release.
20+
- Updates the moving major tag (e.g. `v1`) to the new version tag.
21+
- (If repository secrets `GITHUB_APP_SIGNING_KEY`, `GITHUB_APP_SIGNING_KEY_ID`, and `GITHUB_APP_SIGNING_KEY_PASSPHRASE` are configured) imports the GPG key and signs the release commit and tag using a GitHub App.
22+
23+
## Rationale
24+
25+
Bundling only at release time avoids large, noisy diffs in pull requests and guarantees that the published artefact matches the exact commit that semantic-release tags.
26+
27+
## Usage for Consumers
28+
29+
Reference the Action via a tag, ideally the major tag:
30+
31+
```yaml
32+
uses: nhs-england-tools/notify-msteams-action@v1
33+
```
34+
35+
Pin to a specific minor/patch if you require full immutability:
36+
37+
```yaml
38+
uses: nhs-england-tools/notify-msteams-action@v1.2.3
39+
```
40+
41+
Avoid using `@main` externally, because the branch does not contain a guaranteed built `dist/` bundle.
42+
43+
## Developer Workflow
44+
45+
```bash
46+
# Install dependencies
47+
npm ci
48+
# Run tests & quality gates
49+
npm test
50+
npm run lint
51+
# Build bundle for local testing (not committed)
52+
npm run package
53+
```
54+
55+
You can validate the action locally by creating a temporary workflow that uses `uses: ./`.
56+
57+
## Conventional Commit Examples
58+
59+
| Type | Purpose | Example |
60+
|-------|-----------------------------------------|------------------------------------------------|
61+
| `feat` | New feature (minor bump) | `feat: support custom message colour` |
62+
| `fix` | Bug fix (patch bump) | `fix: correct adaptive card JSON schema` |
63+
| `chore` | Build/tooling changes | `chore: update eslint config` |
64+
| `docs` | Documentation updates | `docs: add release process guide` |
65+
| `perf` | Performance improvement | `perf: reduce bundle size` |
66+
| `refactor` | Non-functional code change | `refactor: simplify card builder` |
67+
68+
Breaking changes: add `!` (`feat!:`) or a `BREAKING CHANGE:` footer.
69+
70+
## Failing Conditions
71+
72+
The guard job fails a pull request if `dist/` is present. Remove it before requesting review.
73+
74+
## Manual Intervention
75+
76+
If a release must be re-run (rare):
77+
78+
1. Revert the release commit if necessary.
79+
2. Amend commit messages to adjust semantic meaning.
80+
3. Push to `main` again and allow the publish workflow to execute.
81+
82+
Avoid editing tags directly; let semantic-release manage them.
83+
84+
## Future Improvements
85+
86+
- Add changelog generation (`@semantic-release/changelog`) if a persistent `CHANGELOG.md` is desired.
87+
- Add an integration test workflow that consumes the just-published tag.
88+
89+
---
90+
Maintainers: ensure `permissions: contents: write` is preserved in the publish workflow for tag/commit operations.
91+
92+
### Commit Signing with GitHub App
93+
94+
If branch protection requires signed commits, this repository uses GitHub App native commit signing:
95+
96+
1. Create a GitHub App with commit signing enabled and generate both authentication and signing keys.
97+
2. Configure the following repository secrets:
98+
- `GITHUB_APP_ID`: GitHub App ID for authentication
99+
- `GITHUB_APP_PRIVATE_KEY`: App private key (PEM format) for token generation
100+
- `GITHUB_APP_SIGNING_KEY_ID`: GPG key ID for commit signing
101+
- `GITHUB_APP_SIGNING_KEY`: ASCII-armoured GPG private key content
102+
- `GITHUB_APP_SIGNING_KEY_PASSPHRASE`: Passphrase for the GPG signing key
103+
104+
On publish, the workflow:
105+
106+
1. Generates a GitHub App installation token for authentication.
107+
2. Creates a GPG wrapper script that handles passphrase injection automatically.
108+
3. Imports the GPG signing key and configures git to use the wrapper script.
109+
4. Runs semantic-release which creates signed commits and tags attributed to the GitHub App.
110+
111+
If the signing secrets are absent, semantic-release proceeds with unsigned commits (which will fail if branch protection mandates signatures—therefore the secrets must be present in that case).
112+
113+
Complete setup procedures (key generation, GitHub App configuration, rotation, revocation) are documented in [Release Signing & GitHub App](./Release_Signing_and_GitHub_App.md).

0 commit comments

Comments
 (0)