Skip to content

Commit b4be3f9

Browse files
authored
feat: Add GitHub Actions workflow for automated npm publishing (#13)
* feat: enhance publish workflow with semantic versioning and release branches * chore: tidied up * chore(release): MINOR * chore: update npm_tag description and set default value for non-standard branches * feat: enhance publish workflow with dynamic path determination and version bump handling * fix: improve error handling for major version mismatch in publish workflow * docs: update README to enhance clarity on Forms-Engine-Plugin features and publishing process * lint: fix prettier error * chore: clean up whitespace in publish workflow for improved readability * lint: add newline for README formatting * feat: implement modular scripts for publish workflow management, including dynamic workflow path determination, version bump validation, and npm publishing logic * docs: update README and script to reflect changes in version bump keywords from MINOR/MAJOR to #minor/#major * chore: clean up whitespace and improve test mode messaging in publish scripts * chore: update script execution in publish workflow to use bash for improved compatibility * chore: remove temporary dry-run flags from npm publish script for finalization * docs: update README and publish script to change default tag from 'latest' to 'beta' for main branch publishing
1 parent a03b201 commit b4be3f9

File tree

5 files changed

+273
-20
lines changed

5 files changed

+273
-20
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Arguments
5+
EVENT_NAME="$1"
6+
INPUT_VERSION_BUMP="$2"
7+
INPUT_NPM_TAG="$3"
8+
9+
# Default values
10+
WORKFLOW_PATH="unknown"
11+
SHOULD_BUILD="false"
12+
VERSION_BUMP="patch"
13+
NPM_TAG=""
14+
15+
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
16+
echo "🔵 Manual workflow trigger detected"
17+
WORKFLOW_PATH="manual"
18+
SHOULD_BUILD="true"
19+
VERSION_BUMP="$INPUT_VERSION_BUMP"
20+
NPM_TAG="$INPUT_NPM_TAG"
21+
22+
elif [[ "$EVENT_NAME" == "push" ]]; then
23+
COMMIT_MSG=$(git log -1 --pretty=%B)
24+
if [[ "$COMMIT_MSG" == *"#minor"* || "$COMMIT_MSG" == *"#major"* ]]; then
25+
echo "🟢 Version bump commit detected!"
26+
WORKFLOW_PATH="version-bump"
27+
SHOULD_BUILD="true"
28+
29+
if [[ "$COMMIT_MSG" == *"#minor"* ]]; then
30+
echo "Minor version bump"
31+
VERSION_BUMP="minor"
32+
elif [[ "$COMMIT_MSG" == *"#major"* ]]; then
33+
echo "Major version bump"
34+
VERSION_BUMP="major"
35+
fi
36+
37+
elif git diff --name-only HEAD^ HEAD | grep -v "\.test\." | grep -q -E "(^\.browserslistrc$|^babel\.config\.|^src/)"; then
38+
echo "🟠 Relevant file changes detected"
39+
WORKFLOW_PATH="file-changes"
40+
SHOULD_BUILD="true"
41+
42+
else
43+
echo "⚪ No publishing-relevant changes detected"
44+
WORKFLOW_PATH="skip"
45+
fi
46+
fi
47+
48+
echo "workflow-path=$WORKFLOW_PATH" >> $GITHUB_OUTPUT
49+
echo "should-build=$SHOULD_BUILD" >> $GITHUB_OUTPUT
50+
echo "version-bump=$VERSION_BUMP" >> $GITHUB_OUTPUT
51+
echo "npm-tag=$NPM_TAG" >> $GITHUB_OUTPUT
52+
53+
echo "==========================================="
54+
echo "Workflow Path: $WORKFLOW_PATH"
55+
echo "Should Build: $SHOULD_BUILD"
56+
echo "Version Bump: $VERSION_BUMP"
57+
echo "NPM Tag: ${NPM_TAG:-<auto-detect>}"
58+
echo "==========================================="
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/bin/bash
2+
set -e
3+
4+
BRANCH_NAME="$1"
5+
WORKFLOW_PATH="$2"
6+
NPM_TAG="$3"
7+
DRY_RUN="$4"
8+
9+
NEW_VERSION=$(npm pkg get version | tr -d \")
10+
PUBLISH_ARGS="--access public"
11+
12+
USING_DEFAULT_TAG=false
13+
if [[ "$WORKFLOW_PATH" == "manual" && "$NPM_TAG" == "beta" ]]; then
14+
USING_DEFAULT_TAG=true
15+
fi
16+
17+
if [[ -n "$NPM_TAG" && "$USING_DEFAULT_TAG" == "false" ]]; then
18+
DIST_TAG="$NPM_TAG"
19+
PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG"
20+
echo "Publishing v$NEW_VERSION with custom tag '$DIST_TAG'"
21+
elif [[ "$BRANCH_NAME" == "main" ]]; then
22+
DIST_TAG="beta"
23+
PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG"
24+
echo "Publishing v$NEW_VERSION from main -> using 'beta' tag"
25+
elif [[ "$BRANCH_NAME" =~ release/v([0-9]+) ]]; then
26+
MAJOR_VERSION="${BASH_REMATCH[1]}"
27+
DIST_TAG="${MAJOR_VERSION}x"
28+
PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG"
29+
echo "Publishing v$NEW_VERSION from $BRANCH_NAME -> using tag '$DIST_TAG'"
30+
else
31+
if [[ "$WORKFLOW_PATH" == "manual" && "$USING_DEFAULT_TAG" == "true" ]]; then
32+
DIST_TAG="beta"
33+
PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG"
34+
echo "⚠️ WARNING: Publishing from non-standard branch '$BRANCH_NAME' with default 'beta' tag"
35+
else
36+
echo "Branch $BRANCH_NAME doesn't match expected patterns, using default publishing"
37+
fi
38+
fi
39+
40+
if [[ "$DRY_RUN" == "true" ]]; then
41+
PUBLISH_ARGS="$PUBLISH_ARGS --dry-run"
42+
echo "DRY RUN MODE - No actual publishing will occur"
43+
fi
44+
45+
# Temporary guards for testing
46+
# PUBLISH_ARGS="$PUBLISH_ARGS --dry-run"
47+
# echo "⚠️ TEST MODE: Force using --dry-run flag. Remove before merging to main! ⚠️"
48+
49+
npm publish $PUBLISH_ARGS
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
set -e
3+
4+
BRANCH_NAME="$1"
5+
VERSION_TYPE="$2"
6+
7+
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
8+
9+
if [[ "$BRANCH_NAME" =~ release/v([0-9]+) ]]; then
10+
MAJOR_VERSION="${BASH_REMATCH[1]}"
11+
12+
if [[ "$VERSION_TYPE" == "major" ]]; then
13+
echo "::error::⛔ MAJOR VERSION BUMP NOT ALLOWED ON RELEASE BRANCH"
14+
echo "::error::Branch release/v${MAJOR_VERSION} is locked to major version ${MAJOR_VERSION}."
15+
echo "::error::To publish a new major version, create a new branch named release/v$((MAJOR_VERSION+1))."
16+
exit 1
17+
fi
18+
19+
CURRENT_VERSION=$(npm pkg get version | tr -d \")
20+
CURRENT_MAJOR=$(echo $CURRENT_VERSION | cut -d. -f1)
21+
22+
if [[ "$CURRENT_MAJOR" != "$MAJOR_VERSION" ]]; then
23+
echo "::error::🚫 Major version mismatch: package.json version $CURRENT_VERSION does not match release/v${MAJOR_VERSION} branch."
24+
echo "::error::Please update the package.json manually to match major version ${MAJOR_VERSION}, or rename the branch if you intended a different major."
25+
exit 1
26+
fi
27+
fi
28+
29+
echo "VERSION_TYPE=$VERSION_TYPE" >> $GITHUB_ENV

.github/workflows/publish.yml

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,70 @@
1-
name: Publish Engine plugin
1+
name: Publish Engine Plugin
22

33
on:
4-
workflow_dispatch: {}
4+
workflow_dispatch:
5+
inputs:
6+
version_bump:
7+
description: 'Type of version bump to perform'
8+
required: true
9+
default: 'patch'
10+
type: choice
11+
options:
12+
- patch
13+
- minor
14+
- major
15+
npm_tag:
16+
description: 'Custom npm tag (only needed for non-standard branches; release branches will use their major version tag automatically)'
17+
required: false
18+
default: 'beta'
19+
type: string
20+
dry_run:
21+
description: 'Dry run (no actual publishing)'
22+
required: false
23+
default: true
24+
type: boolean
525

626
push:
727
branches:
828
- main
9-
paths:
10-
- '.browserslistrc'
11-
- 'babel.config.*'
12-
- 'src/**'
13-
- '!**/*.test.*'
29+
- 'release/v[0-9]*'
1430

1531
concurrency:
16-
group: publish-engine-plugin
32+
group: publish-engine-plugin-${{ github.ref }}
1733

1834
permissions:
1935
contents: write
2036
packages: write
2137

2238
jobs:
39+
determine-path:
40+
name: Determine Workflow Path
41+
runs-on: ubuntu-24.04
42+
outputs:
43+
workflow-path: ${{ steps.check-path.outputs.workflow-path }}
44+
version-bump: ${{ steps.check-path.outputs.version-bump }}
45+
npm-tag: ${{ steps.check-path.outputs.npm-tag }}
46+
47+
steps:
48+
- name: Check out code
49+
uses: actions/checkout@v4
50+
with:
51+
fetch-depth: 2
52+
53+
- name: Determine workflow path
54+
id: check-path
55+
run: bash .github/scripts/publish/determine-workflow-path.sh "${{ github.event_name }}" "${{ github.event.inputs.version_bump }}" "${{ github.event.inputs.npm_tag }}"
56+
2357
build:
2458
name: Build
59+
needs: [determine-path]
60+
if: needs.determine-path.outputs.workflow-path != 'skip'
2561
runs-on: ubuntu-24.04
2662

2763
steps:
2864
- name: Check out code
2965
uses: actions/checkout@v4
66+
with:
67+
fetch-depth: 0
3068

3169
- name: Cache dependencies
3270
uses: actions/cache@v4
@@ -60,30 +98,35 @@ jobs:
6098

6199
publish:
62100
name: Publish
101+
needs: [determine-path, build]
102+
if: needs.determine-path.outputs.workflow-path != 'skip'
63103
runs-on: ubuntu-24.04
64-
needs: [build]
65104
environment: production
66105

67106
steps:
68107
- name: Check out code
69108
uses: actions/checkout@v4
70109
with:
71-
fetch-depth: 0
72-
ref: main
110+
fetch-depth: 2
111+
ref: ${{ github.ref }}
112+
113+
- name: Display workflow path
114+
run: |
115+
echo "⭐ Executing ${{ needs.determine-path.outputs.workflow-path }} workflow path"
116+
echo "Version bump type: ${{ needs.determine-path.outputs.version-bump }}"
117+
echo "NPM Tag: ${{ needs.determine-path.outputs.npm-tag || '<auto-detect>' }}"
73118
74119
- name: Restore dependencies
75120
uses: actions/cache/restore@v4
76121
with:
77122
enableCrossOsArchive: true
78-
fail-on-cache-miss: true
79123
key: npm-install-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
80124
path: node_modules
81125

82-
- name: Restore build
126+
- name: Restore build artifacts
83127
uses: actions/cache/restore@v4
84128
with:
85129
enableCrossOsArchive: true
86-
fail-on-cache-miss: true
87130
key: npm-build-${{ runner.os }}-${{ github.sha }}
88131
path: |
89132
.server
@@ -96,16 +139,23 @@ jobs:
96139
registry-url: https://registry.npmjs.org
97140
scope: '@defra'
98141

99-
- name: Update package versions
100-
run: npm version patch --git-tag-version false --save
142+
- name: Determine version bump details
143+
id: version-details
144+
run: bash .github/scripts/publish/version-bump-details.sh "${{ github.ref_name }}" "${{ needs.determine-path.outputs.version-bump }}"
145+
146+
- name: Update package version
147+
run: |
148+
echo "Bumping version: $VERSION_TYPE"
149+
npm version $VERSION_TYPE --git-tag-version false --save
101150
102151
- name: Commit and push updates
103152
run: |
104153
git config user.name github-actions
105154
git config user.email github-actions@github.com
106-
git commit -am "v$(npm pkg get version | tr -d \") [skip ci]" && git push
155+
NEW_VERSION=$(npm pkg get version | tr -d \")
156+
git commit -am "v$NEW_VERSION [skip ci]" && git push
107157
108-
- name: Publish to npm
109-
run: npm publish --access public
158+
- name: Publish to npm with appropriate dist-tag
159+
run: bash .github/scripts/publish/publish-to-npm.sh "${{ github.ref_name }}" "${{ needs.determine-path.outputs.workflow-path }}" "${{ needs.determine-path.outputs.npm-tag }}" "${{ github.event.inputs.dry_run }}"
110160
env:
111161
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}

README.md

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,33 @@ The `@defra/forms-engine-plugin` is a [plugin](https://hapi.dev/tutorials/plugin
44

55
It is designed to be embedded in the frontend of a digital service and provide a convenient, configuration driven approach to building forms that are aligned to [GDS Design System](https://design-system.service.gov.uk/) guidelines.
66

7+
## Table of Contents
8+
9+
- [Installation](#installation)
10+
- [Dependencies](#dependencies)
11+
- [Setup](#setup)
12+
- [Form Config](#form-config)
13+
- [Static Assets and Styles](#static-assets-and-styles)
14+
- [Example](#example)
15+
- [Environment Variables](#environment-variables)
16+
- [Options](#options)
17+
- [Services](#services)
18+
- [Custom Controllers](#custom-controllers)
19+
- [Custom Filters](#custom-filters)
20+
- [Custom Cache](#custom-cache)
21+
- [Exemplar](#exemplar)
22+
- [Templates](#templates)
23+
- [Template Data](#template-data)
24+
- [Liquid Filters](#liquid-filters)
25+
- [Examples](#examples)
26+
- [Templates and Views: Extending the Default Layout](#templates-and-views-extending-the-default-layout)
27+
- [Publishing the Package](#publishing-the-package)
28+
- [Semantic Versioning Control](#semantic-versioning-control)
29+
- [Major-Version Release Branches](#major-version-release-branches)
30+
- [Manual Workflow Triggers](#manual-workflow-triggers)
31+
- [Workflow Triggers](#workflow-triggers)
32+
- [Safety and Consistency](#safety-and-consistency)
33+
734
## Installation
835

936
`npm install @defra/forms-engine-plugin --save`
@@ -209,7 +236,7 @@ The following elements support [LiquidJS templates](https://liquidjs.com/):
209236
- Html (guidance) component **content**
210237
- Summary component **row** key title (check answers and repeater summary)
211238

212-
### Template data
239+
### Template Data
213240

214241
The data the templates are evaluated against is the raw answers the user has provided up to the page they're currently on.
215242
For example, given a YesNoField component called `TKsWbP`, the template `{{ TKsWbP }}` would render "true" or "false" depending on how the user answered the question.
@@ -296,3 +323,43 @@ The `forms-engine-plugin` path to add can be imported from:
296323
Which can then be appended to the `node_modules` path `node_modules/@defra/forms-engine`.
297324

298325
The main template layout is `govuk-frontend`'s `template.njk` file, this also needs to be added to the `path`s that nunjucks can look in.
326+
327+
## Publishing the Package
328+
329+
Our GitHub Actions workflow (`publish.yml`) is set up to make publishing a breeze, using semantic versioning and a variety of release strategies. Here's how you can make the most of it:
330+
331+
### Semantic Versioning Control
332+
333+
- **Patch Versioning**: This happens automatically whenever you merge code changes into `main` or any release branch.
334+
- **Minor and Major Bumps**: You can control these by making empty commits with specific hashtags:
335+
- Use `#minor` for a minor version bump.
336+
- Use `#major` for a major version bump.
337+
338+
**Example Commands**:
339+
340+
```bash
341+
git commit --allow-empty -m "chore(release): #minor" # Minor bump
342+
git commit --allow-empty -m "chore(release): #major" # Major bump
343+
```
344+
345+
### Major-Version Release Branches
346+
347+
- **Branch Naming**: Stick to `release/vX` (like `release/v1`, `release/v2`).
348+
- **Independent Versioning**: Each branch has its own versioning and publishes to npm with a unique dist-tag (like `2x` for `release/v2`).
349+
350+
### Manual Workflow Triggers
351+
352+
- **Customizable Options**: You can choose the type of version bump, specify custom npm tags, and use dry run mode for testing. Dry-run is enabled by default.
353+
- **Special Releases**: Perfect for beta releases or when you need to publish outside the usual process.
354+
355+
### Workflow Triggers
356+
357+
1. **Standard Development Flow**: Merging PRs to `main` automatically triggers a patch bump and publishes with the `beta` tag.
358+
2. **Backporting**: Cherry-pick fixes to release branches for patch bumps with specific tags (like `2x`).
359+
3. **Version Bumps**: Use empty commits for minor or major bumps.
360+
4. **Manual Releases**: Trigger these manually for special cases like beta or release candidates.
361+
362+
### Safety and Consistency
363+
364+
- **Build Process**: Every publishing scenario includes a full build to ensure everything is in top shape, except for files like tests and lint rules.
365+
- **Tagging Safety**: We prevent overwriting the `beta` tag by enforcing custom tags for non-standard branches. The default is set to `beta`. For release branches, the tag will be picked up from the release branch itself.

0 commit comments

Comments
 (0)