Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/scripts/publish/determine-workflow-path.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash
set -e

# Arguments
EVENT_NAME="$1"
INPUT_VERSION_BUMP="$2"
INPUT_NPM_TAG="$3"

# Default values
WORKFLOW_PATH="unknown"
SHOULD_BUILD="false"
VERSION_BUMP="patch"
NPM_TAG=""

if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
echo "🔵 Manual workflow trigger detected"
WORKFLOW_PATH="manual"
SHOULD_BUILD="true"
VERSION_BUMP="$INPUT_VERSION_BUMP"
NPM_TAG="$INPUT_NPM_TAG"

elif [[ "$EVENT_NAME" == "push" ]]; then
COMMIT_MSG=$(git log -1 --pretty=%B)
if [[ "$COMMIT_MSG" == *"#minor"* || "$COMMIT_MSG" == *"#major"* ]]; then
echo "🟢 Version bump commit detected!"
WORKFLOW_PATH="version-bump"
SHOULD_BUILD="true"

if [[ "$COMMIT_MSG" == *"#minor"* ]]; then
echo "Minor version bump"
VERSION_BUMP="minor"
elif [[ "$COMMIT_MSG" == *"#major"* ]]; then
echo "Major version bump"
VERSION_BUMP="major"
fi

elif git diff --name-only HEAD^ HEAD | grep -v "\.test\." | grep -q -E "(^\.browserslistrc$|^babel\.config\.|^src/)"; then
echo "🟠 Relevant file changes detected"
WORKFLOW_PATH="file-changes"
SHOULD_BUILD="true"

else
echo "⚪ No publishing-relevant changes detected"
WORKFLOW_PATH="skip"
fi
fi

echo "workflow-path=$WORKFLOW_PATH" >> $GITHUB_OUTPUT
echo "should-build=$SHOULD_BUILD" >> $GITHUB_OUTPUT
echo "version-bump=$VERSION_BUMP" >> $GITHUB_OUTPUT
echo "npm-tag=$NPM_TAG" >> $GITHUB_OUTPUT

echo "==========================================="
echo "Workflow Path: $WORKFLOW_PATH"
echo "Should Build: $SHOULD_BUILD"
echo "Version Bump: $VERSION_BUMP"
echo "NPM Tag: ${NPM_TAG:-<auto-detect>}"
echo "==========================================="
49 changes: 49 additions & 0 deletions .github/scripts/publish/publish-to-npm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash
set -e

BRANCH_NAME="$1"
WORKFLOW_PATH="$2"
NPM_TAG="$3"
DRY_RUN="$4"

NEW_VERSION=$(npm pkg get version | tr -d \")
PUBLISH_ARGS="--access public"

USING_DEFAULT_TAG=false
if [[ "$WORKFLOW_PATH" == "manual" && "$NPM_TAG" == "beta" ]]; then
USING_DEFAULT_TAG=true
fi

if [[ -n "$NPM_TAG" && "$USING_DEFAULT_TAG" == "false" ]]; then
DIST_TAG="$NPM_TAG"
PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG"
echo "Publishing v$NEW_VERSION with custom tag '$DIST_TAG'"
elif [[ "$BRANCH_NAME" == "main" ]]; then
DIST_TAG="beta"
PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG"
echo "Publishing v$NEW_VERSION from main -> using 'beta' tag"
elif [[ "$BRANCH_NAME" =~ release/v([0-9]+) ]]; then
MAJOR_VERSION="${BASH_REMATCH[1]}"
DIST_TAG="${MAJOR_VERSION}x"
PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG"
echo "Publishing v$NEW_VERSION from $BRANCH_NAME -> using tag '$DIST_TAG'"
else
if [[ "$WORKFLOW_PATH" == "manual" && "$USING_DEFAULT_TAG" == "true" ]]; then
DIST_TAG="beta"
PUBLISH_ARGS="$PUBLISH_ARGS --tag $DIST_TAG"
echo "⚠️ WARNING: Publishing from non-standard branch '$BRANCH_NAME' with default 'beta' tag"
else
echo "Branch $BRANCH_NAME doesn't match expected patterns, using default publishing"
fi
fi

if [[ "$DRY_RUN" == "true" ]]; then
PUBLISH_ARGS="$PUBLISH_ARGS --dry-run"
echo "DRY RUN MODE - No actual publishing will occur"
fi

# Temporary guards for testing
# PUBLISH_ARGS="$PUBLISH_ARGS --dry-run"
# echo "⚠️ TEST MODE: Force using --dry-run flag. Remove before merging to main! ⚠️"

npm publish $PUBLISH_ARGS
29 changes: 29 additions & 0 deletions .github/scripts/publish/version-bump-details.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash
set -e

BRANCH_NAME="$1"
VERSION_TYPE="$2"

echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV

if [[ "$BRANCH_NAME" =~ release/v([0-9]+) ]]; then
MAJOR_VERSION="${BASH_REMATCH[1]}"

if [[ "$VERSION_TYPE" == "major" ]]; then
echo "::error::⛔ MAJOR VERSION BUMP NOT ALLOWED ON RELEASE BRANCH"
echo "::error::Branch release/v${MAJOR_VERSION} is locked to major version ${MAJOR_VERSION}."
echo "::error::To publish a new major version, create a new branch named release/v$((MAJOR_VERSION+1))."
exit 1
fi

CURRENT_VERSION=$(npm pkg get version | tr -d \")
CURRENT_MAJOR=$(echo $CURRENT_VERSION | cut -d. -f1)

if [[ "$CURRENT_MAJOR" != "$MAJOR_VERSION" ]]; then
echo "::error::🚫 Major version mismatch: package.json version $CURRENT_VERSION does not match release/v${MAJOR_VERSION} branch."
echo "::error::Please update the package.json manually to match major version ${MAJOR_VERSION}, or rename the branch if you intended a different major."
exit 1
fi
fi

echo "VERSION_TYPE=$VERSION_TYPE" >> $GITHUB_ENV
88 changes: 69 additions & 19 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,32 +1,70 @@
name: Publish Engine plugin
name: Publish Engine Plugin

on:
workflow_dispatch: {}
workflow_dispatch:
inputs:
version_bump:
description: 'Type of version bump to perform'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
npm_tag:
description: 'Custom npm tag (only needed for non-standard branches; release branches will use their major version tag automatically)'
required: false
default: 'beta'
type: string
dry_run:
description: 'Dry run (no actual publishing)'
required: false
default: true
type: boolean

push:
branches:
- main
paths:
- '.browserslistrc'
- 'babel.config.*'
- 'src/**'
- '!**/*.test.*'
- 'release/v[0-9]*'

concurrency:
group: publish-engine-plugin
group: publish-engine-plugin-${{ github.ref }}

permissions:
contents: write
packages: write

jobs:
determine-path:
name: Determine Workflow Path
runs-on: ubuntu-24.04
outputs:
workflow-path: ${{ steps.check-path.outputs.workflow-path }}
version-bump: ${{ steps.check-path.outputs.version-bump }}
npm-tag: ${{ steps.check-path.outputs.npm-tag }}

steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 2

- name: Determine workflow path
id: check-path
run: bash .github/scripts/publish/determine-workflow-path.sh "${{ github.event_name }}" "${{ github.event.inputs.version_bump }}" "${{ github.event.inputs.npm_tag }}"

build:
name: Build
needs: [determine-path]
if: needs.determine-path.outputs.workflow-path != 'skip'
runs-on: ubuntu-24.04

steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Cache dependencies
uses: actions/cache@v4
Expand Down Expand Up @@ -60,30 +98,35 @@ jobs:

publish:
name: Publish
needs: [determine-path, build]
if: needs.determine-path.outputs.workflow-path != 'skip'
runs-on: ubuntu-24.04
needs: [build]
environment: production

steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main
fetch-depth: 2
ref: ${{ github.ref }}

- name: Display workflow path
run: |
echo "⭐ Executing ${{ needs.determine-path.outputs.workflow-path }} workflow path"
echo "Version bump type: ${{ needs.determine-path.outputs.version-bump }}"
echo "NPM Tag: ${{ needs.determine-path.outputs.npm-tag || '<auto-detect>' }}"

- name: Restore dependencies
uses: actions/cache/restore@v4
with:
enableCrossOsArchive: true
fail-on-cache-miss: true
key: npm-install-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
path: node_modules

- name: Restore build
- name: Restore build artifacts
uses: actions/cache/restore@v4
with:
enableCrossOsArchive: true
fail-on-cache-miss: true
key: npm-build-${{ runner.os }}-${{ github.sha }}
path: |
.server
Expand All @@ -96,16 +139,23 @@ jobs:
registry-url: https://registry.npmjs.org
scope: '@defra'

- name: Update package versions
run: npm version patch --git-tag-version false --save
- name: Determine version bump details
id: version-details
run: bash .github/scripts/publish/version-bump-details.sh "${{ github.ref_name }}" "${{ needs.determine-path.outputs.version-bump }}"

- name: Update package version
run: |
echo "Bumping version: $VERSION_TYPE"
npm version $VERSION_TYPE --git-tag-version false --save

- name: Commit and push updates
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git commit -am "v$(npm pkg get version | tr -d \") [skip ci]" && git push
NEW_VERSION=$(npm pkg get version | tr -d \")
git commit -am "v$NEW_VERSION [skip ci]" && git push

- name: Publish to npm
run: npm publish --access public
- name: Publish to npm with appropriate dist-tag
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 }}"
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
69 changes: 68 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,33 @@ The `@defra/forms-engine-plugin` is a [plugin](https://hapi.dev/tutorials/plugin

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.

## Table of Contents

- [Installation](#installation)
- [Dependencies](#dependencies)
- [Setup](#setup)
- [Form Config](#form-config)
- [Static Assets and Styles](#static-assets-and-styles)
- [Example](#example)
- [Environment Variables](#environment-variables)
- [Options](#options)
- [Services](#services)
- [Custom Controllers](#custom-controllers)
- [Custom Filters](#custom-filters)
- [Custom Cache](#custom-cache)
- [Exemplar](#exemplar)
- [Templates](#templates)
- [Template Data](#template-data)
- [Liquid Filters](#liquid-filters)
- [Examples](#examples)
- [Templates and Views: Extending the Default Layout](#templates-and-views-extending-the-default-layout)
- [Publishing the Package](#publishing-the-package)
- [Semantic Versioning Control](#semantic-versioning-control)
- [Major-Version Release Branches](#major-version-release-branches)
- [Manual Workflow Triggers](#manual-workflow-triggers)
- [Workflow Triggers](#workflow-triggers)
- [Safety and Consistency](#safety-and-consistency)

## Installation

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

### Template data
### Template Data

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

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.

## Publishing the Package

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:

### Semantic Versioning Control

- **Patch Versioning**: This happens automatically whenever you merge code changes into `main` or any release branch.
- **Minor and Major Bumps**: You can control these by making empty commits with specific hashtags:
- Use `#minor` for a minor version bump.
- Use `#major` for a major version bump.

**Example Commands**:

```bash
git commit --allow-empty -m "chore(release): #minor" # Minor bump
git commit --allow-empty -m "chore(release): #major" # Major bump
```

### Major-Version Release Branches

- **Branch Naming**: Stick to `release/vX` (like `release/v1`, `release/v2`).
- **Independent Versioning**: Each branch has its own versioning and publishes to npm with a unique dist-tag (like `2x` for `release/v2`).

### Manual Workflow Triggers

- **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.
- **Special Releases**: Perfect for beta releases or when you need to publish outside the usual process.

### Workflow Triggers

1. **Standard Development Flow**: Merging PRs to `main` automatically triggers a patch bump and publishes with the `beta` tag.
2. **Backporting**: Cherry-pick fixes to release branches for patch bumps with specific tags (like `2x`).
3. **Version Bumps**: Use empty commits for minor or major bumps.
4. **Manual Releases**: Trigger these manually for special cases like beta or release candidates.

### Safety and Consistency

- **Build Process**: Every publishing scenario includes a full build to ensure everything is in top shape, except for files like tests and lint rules.
- **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.
Loading