Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ updates:
schedule:
interval: "weekly"
open-pull-requests-limit: 25
commit-message:
prefix: "chore"
include: "scope"
52 changes: 52 additions & 0 deletions .github/release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"separator": "/",
"include-component-in-tag": true,
"include-v-in-tag": true,
"release-type": "simple",
"skip-github-release": true,
"changelog-path": "CHANGELOG.md",
"packages": {
".github/workflows/build_container_image": {
"component": "build_container_image",
"include-paths": [".github/workflows/build_container_image.yml"]
},
".github/workflows/build_node_package": {
"component": "build_node_package",
"include-paths": [".github/workflows/build_node_package.yml"]
},
".github/workflows/build_node_package_with_pgsql": {
"component": "build_node_package_with_pgsql",
"include-paths": [".github/workflows/build_node_package_with_pgsql.yml"]
},
".github/workflows/deploy_container_image": {
"component": "deploy_container_image",
"include-paths": [".github/workflows/deploy_container_image.yml"]
},
".github/workflows/deploy_helm_chart": {
"component": "deploy_helm_chart",
"include-paths": [".github/workflows/deploy_helm_chart.yml"]
},
".github/workflows/merge_multiarch_images": {
"component": "merge_multiarch_images",
"include-paths": [".github/workflows/merge_multiarch_images.yml"]
},
".github/workflows/publish_node_package": {
"component": "publish_node_package",
"include-paths": [".github/workflows/publish_node_package.yml"]
},
".github/workflows/sast_scan": {
"component": "sast_scan",
"include-paths": [".github/workflows/sast_scan.yaml"]
},
"actions/npm_test": {
"component": "npm_test"
},
"actions/scan_container_image": {
"component": "scan_container_image"
},
"actions/update-nr-flows": {
"component": "update-nr-flows"
}
}
}
13 changes: 13 additions & 0 deletions .github/release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
".github/workflows/build_container_image": "1.0.0",
".github/workflows/build_node_package": "1.0.0",
".github/workflows/build_node_package_with_pgsql": "1.0.0",
".github/workflows/deploy_container_image": "1.0.0",
".github/workflows/deploy_helm_chart": "1.0.0",
".github/workflows/merge_multiarch_images": "1.0.0",
".github/workflows/publish_node_package": "1.0.0",
".github/workflows/sast_scan": "1.0.0",
"actions/npm_test": "1.0.0",
"actions/scan_container_image": "1.0.0",
"actions/update-nr-flows": "1.0.0"
}
28 changes: 0 additions & 28 deletions .github/workflows/autotag.yml

This file was deleted.

16 changes: 16 additions & 0 deletions .github/workflows/lint-pr-title.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Lint Pull Request Title

on:
pull_request:
types: [opened, edited, synchronize, reopened]

permissions:
pull-requests: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 changes: 52 additions & 0 deletions .github/workflows/release-please.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Prepare release

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: write
pull-requests: write
issues: write

jobs:
release-please:
runs-on: ubuntu-latest
steps:
- name: Run Release Please
id: rp
uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1
with:
config-file: .github/release-please-config.json
manifest-file: .github/release-please-manifest.json

- name: Checkout
if: steps.rp.outputs.releases_created == 'true'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0

- name: Fast-forward major/minor floats
if: steps.rp.outputs.releases_created == 'true'
env:
RP_OUTPUTS: ${{ toJSON(steps.rp.outputs) }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
echo "$RP_OUTPUTS" | jq -r '
(.paths_released | fromjson) as $paths
| $paths[] as $p
| "\(.[$p + "--tag_name"])\t\(.[$p + "--sha"])"
' | while IFS=$'\t' read -r TAG SHA; do
COMP="${TAG%/*}"
VER="${TAG##*/v}"
MAJ="${VER%%.*}"
REST="${VER#*.}"
MIN="${REST%%.*}"
echo "Fast-forwarding ${COMP}/v${MAJ} and ${COMP}/v${MAJ}.${MIN} -> ${SHA}"
git tag -f "${COMP}/v${MAJ}" "${SHA}"
git tag -f "${COMP}/v${MAJ}.${MIN}" "${SHA}"
git push origin -f "${COMP}/v${MAJ}" "${COMP}/v${MAJ}.${MIN}"
done
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Changelog
131 changes: 130 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,130 @@
General purpose reusable Github Action workflows
# github-actions-workflows

General purpose reusable GitHub Action workflows and composite actions.

## Versioning and Release Process

Each reusable workflow and composite action in this repository is versioned and released **independently** using [Release Please](https://github.com/googleapis/release-please). The next version of each component is determined automatically from the [Conventional Commits](https://www.conventionalcommits.org/) in pull request titles that touched the component's files.

### Components

1. The `Prepare release` GitHub Action workflow (`.github/workflows/release-please.yaml`):

* Runs `release-please-action` on every push to `main`. Release Please analyzes commit messages since the last release and groups pending version bumps into a single, rolling release pull request.
* The release PR lists one entry per component that has unreleased changes, each with its proposed new version and changelog excerpt.
* Merging the release PR creates the exact version tag for each released component (e.g. `build_container_image/v1.2.3`) and writes the aggregated entries into the repo-root `CHANGELOG.md`.
* After the exact tags are created, the same workflow fast-forwards the per-component **floating tags** - the major float `<component>/vX` and the minor float `<component>/vX.Y` - so they point at the same commit. Consumers pinned to a float automatically pick up the new release on their next run.

2. The `Lint Pull Request Title` GitHub Action workflow (`.github/workflows/lint-pr-title.yaml`):

* Runs on every pull request and uses the `amannn/action-semantic-pull-request` action to validate that the pull request title follows the Conventional Commits format.
* This repository squash-merges all pull requests, so the PR title becomes the squash commit's subject line. The lint gate therefore ensures that Release Please can parse every commit merged into `main`.

### Pull Request Title Format

The Conventional Commits preset expects pull request titles to be in the following format:

<type>(<scope>): <subject>

* Type: Describes the category of the commit. Examples include:
* `feat`: A new feature (triggers a minor version bump).
* `fix`: A bug fix (triggers a patch version bump).
* `perf`: A code change that improves performance (triggers a patch version bump).
* `refactor`: A code change that neither fixes a bug nor adds a feature (triggers a patch version bump unless it is a BREAKING CHANGE).
* `docs`: Documentation-only changes.
* `chore`: Routine maintenance (e.g. CI tweaks, dependency bumps).
* Scope: An optional part that provides additional context about what was changed (e.g. module, component).
* Subject: A brief description of the changes.

A commit whose files fall within a component's tracked paths always produces at least a patch bump for that component, regardless of type. Types like `chore` and `docs` are hidden from the visible `CHANGELOG.md` but still participate in version calculation.

### Handling Breaking Changes

To indicate a breaking change, the exclamation mark `!` should be used immediately after the type (or type and scope):

* `feat!:`
* `fix!:`
* `refactor!:`

A breaking commit bumps the affected components to a new major version. Consumers pinned to the previous major float (e.g. `@build_container_image/v1`) stay frozen on the old major line — picking up the new major requires re-pinning to the new float explicitly.

### Tag Scheme

For every release, three tags are produced per affected component:

| Tag | Mutability | Points at |
|------------------------|-------------------------------------|-----------------------------------|
| `<component>/vX.Y.Z` | immutable | The release commit |
| `<component>/vX.Y` | moves on patch releases | Latest patch of `vX.Y.x` |
| `<component>/vX` | moves on any non-breaking release | Latest non-breaking of `vX.x.y` |

Consumers reference a component by one of these tags:

```yaml
# Major float — auto-updates on every non-breaking release (recommended):
uses: FlowFuse/github-actions-workflows/.github/workflows/build_container_image.yml@build_container_image/v1

# Minor float — auto-updates only on patches:
uses: FlowFuse/github-actions-workflows/.github/workflows/build_container_image.yml@build_container_image/v1.2

# Exact pin — never moves:
uses: FlowFuse/github-actions-workflows/.github/workflows/build_container_image.yml@build_container_image/v1.2.3

# Composite actions use the same pattern:
uses: FlowFuse/github-actions-workflows/actions/npm_test@npm_test/v1
```

### Adding a New Reusable Workflow or Composite Action

When introducing a new component, it must be registered with Release Please so that it is versioned and tagged independently like the existing ones.

1. **Create the workflow or action file.**

* Reusable workflow: place the YAML file at `.github/workflows/<name>.yml`.
* Composite action: create the directory `actions/<name>/` and add its `action.yml` (plus any supporting files).
* `<name>` must be unique across **all** workflows and actions — the two share a flat tag namespace.

2. **Register the component in `.github/release-please-config.json`** by adding a new entry under `packages`.

* For a reusable workflow:

```json
".github/workflows/<name>": {
"component": "<name>",
"include-paths": [".github/workflows/<name>.yml"]
}
```

The package key is a virtual path (no directory is created on disk); `include-paths` scopes change detection to the single YAML file.

* For a composite action:

```json
"actions/<name>": {
"component": "<name>"
}
```

The package key is the action's real directory; `include-paths` is not needed because any file under that directory is attributed to the component.

3. **Seed the initial version in `.github/release-please-manifest.json`** using the same key chosen in the previous step:

```json
"<path-used-above>": "1.0.0"
```

4. **Open the pull request with a Conventional Commit title**, for example `feat: add <name> reusable workflow`.

5. **After merging the pull request, bootstrap the initial tags** for the new component. They must exist before consumers can reference it and before Release Please runs cleanly for the new entry:

```bash
git fetch origin main
SHA=$(git rev-parse origin/main)
NAME="<name>"
git tag "${NAME}/v1.0.0" "${SHA}"
git tag "${NAME}/v1.0" "${SHA}"
git tag "${NAME}/v1" "${SHA}"
git push origin "${NAME}/v1.0.0" "${NAME}/v1.0" "${NAME}/v1"
```

> If Release Please happens to run between the merge and this bootstrap (it triggers on every push to `main`), it may open a stray release pull request proposing an inflated first version for the new component, because it has no tag to use as a baseline. Simply close that pull request — the next Release Please run, once the bootstrap tags exist, will be a no-op for the new component.