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
5 changes: 5 additions & 0 deletions .github/release-footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
> 📍 Find it under **Plugins → Post-Processor**.
> 📄 New here? The [usage guide](https://github.com/jaysuk/duet-gcode-postprocessor/blob/main/docs/usage.md)
> covers every step type, the safety rules and the scripting tiers.
> ⚠️ Preview before you apply. Overwriting always takes a backup, but reading the diff is what
> catches a pattern that matched more than you meant.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
test:
uses: jaysuk/dwc-plugin-test-kit/.github/workflows/dwc-plugin-ci.yml@v0.2.8
uses: jaysuk/dwc-plugin-test-kit/.github/workflows/dwc-plugin-ci.yml@v0.2.9
with:
dwc-ref: v3.7-dev
kit-ref: v0.2.8
kit-ref: v0.2.9
141 changes: 13 additions & 128 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
name: Release

# Builds the installable plugin ZIP against DuetWebControl's source and creates a GitHub Release for
# it - no local build needed. Just cut a release:
# Cut a release:
#
# npm run release -- 0.2.0 --push # bumps plugin.json+package.json, commits, tags, pushes
# (or manually: bump the versions, commit, then `git tag v0.2.0 && git push origin v0.2.0`)
# npm run release -- 1.0.1 --push # drafts release notes, bumps plugin.json+package.json, commits, tags, pushes
#
# This runs automatically on the tag, builds GCodePostProcessor-<version>.zip against DWC, and
# publishes a Release with the ZIP attached. The notes are the version's section of CHANGELOG.md (or,
# failing that, a generated Conventional-Commit changelog) plus an install footer; the tag name is
# the release title.
# The tag push runs the shared release workflow (jaysuk/dwc-plugin-workflows), which builds
# GCodePostProcessor-<version>.zip against DWC and publishes a Release with a pun title and
# generated notes. Run it by hand (Actions -> Release -> Run workflow) for a dry run: it builds the
# ZIP as an artifact and shows the title and notes in the run summary, publishing nothing.
#
# You can also run it manually (Actions -> Release -> Run workflow) to build a ZIP as an artifact,
# optionally against a different DWC ref.
#
# Adapted from duet-eddy-align's release.yml - same shape, minus its punny release-title step.
# Plugin-specific bits: scripts/release-titles.txt (the puns) and .github/release-footer.md (extra notes in
# the footer).

on:
push:
Expand All @@ -27,121 +23,10 @@ on:
default: v3.7-dev

permissions:
contents: write # create the Release and upload the asset

env:
# The Conventional-Commit changelog generator is shared across the Duet plugin family and lives in
# the runtime repo (single source of truth). Pinned to a ref; bump this one line to adopt changelog
# format changes. Source: jaysuk/dwc-plugin-runtime → scripts/changelog.mjs.
RUNTIME_REF: d077f81a11bb5af9a35fe39f7b82d789eab19896
contents: write # the shared workflow creates the Release and uploads the asset

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
path: plugin
fetch-depth: 0 # full history + tags so the changelog generator can find the previous tag
fetch-tags: true

- name: Checkout DuetWebControl
uses: actions/checkout@v5
with:
repository: Duet3D/DuetWebControl
ref: ${{ github.event.inputs.dwc-ref || 'v3.7-dev' }}
path: DuetWebControl

# Record exactly which DWC the ZIP is built against, for the release notes.
- name: Capture DuetWebControl build info
id: dwc
run: |
echo "version=$(node -p "require('./DuetWebControl/package.json').version")" >> "$GITHUB_OUTPUT"
echo "sha=$(git -C DuetWebControl rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
echo "ref=${{ github.event.inputs.dwc-ref || 'v3.7-dev' }}" >> "$GITHUB_OUTPUT"

- uses: actions/setup-node@v5
with: { node-version: 20 }

# The tag must match the version stamped into the ZIP/manifest, so a release can't ship a
# mismatched build.
- name: Verify tag matches plugin version
if: startsWith(github.ref, 'refs/tags/')
working-directory: plugin
run: |
VER=$(node -p "require('./plugin.json').version")
TAG="${GITHUB_REF_NAME#v}"
if [ "$VER" != "$TAG" ]; then
echo "::error::Tag '$TAG' != plugin.json version '$VER'. Bump plugin.json + package.json and re-tag."
exit 1
fi

- name: Install DuetWebControl
working-directory: DuetWebControl
run: npm install

- name: Install plugin (and its bundled deps into DWC)
working-directory: plugin
run: |
npm install
# Install the plugin's runtime dependencies into DWC with their exact specs (github: specs
# used verbatim, registry specs as name@range) so the build/type-check can resolve them.
SPECS=$(node -p "Object.entries(require('./package.json').dependencies||{}).map(([k,v])=>/[:\\/]/.test(v)?v:k+'@'+v).join(' ')")
if [ -n "$SPECS" ]; then (cd ../DuetWebControl && npm install --no-save $SPECS); fi

- name: Type-check against DWC
working-directory: plugin
env:
DWC_DIR: ${{ github.workspace }}/DuetWebControl
run: npm run typecheck

# verify-build itself runs scripts/build-quickjs-asset.mjs first (see package.json) — the
# sandboxed script engine's plugin asset has to exist before dwc-plugin-verify-build packages
# the plugin's dwc/ folder into the ZIP.
- name: Build installable ZIP
working-directory: plugin
env:
DWC_DIR: ${{ github.workspace }}/DuetWebControl
run: npm run verify-build

# Fetch the shared changelog generator from the runtime repo (single source of truth, pinned ref).
- name: Fetch shared release-notes generator
if: startsWith(github.ref, 'refs/tags/')
run: curl -fsSL "https://raw.githubusercontent.com/jaysuk/dwc-plugin-runtime/${RUNTIME_REF}/scripts/changelog.mjs" -o "$RUNNER_TEMP/changelog.mjs"

# Release body = shared Conventional-Commit changelog + a footer with install instructions and
# the DWC version built against.
- name: Generate release notes
id: changelog
if: startsWith(github.ref, 'refs/tags/')
working-directory: plugin
env:
DWC_VERSION: ${{ steps.dwc.outputs.version }}
DWC_SHA: ${{ steps.dwc.outputs.sha }}
DWC_REF: ${{ steps.dwc.outputs.ref }}
run: |
{
echo 'body<<__CHANGELOG_EOF__'
# Prefer the hand-written CHANGELOG.md section for this version; fall back to the
# generated Conventional-Commit list when there is none.
node scripts/changelog-section.mjs "$GITHUB_REF_NAME" || node "$RUNNER_TEMP/changelog.mjs"
echo
node scripts/release-footer.mjs
echo
echo '__CHANGELOG_EOF__'
} >> "$GITHUB_OUTPUT"

- name: Publish Release with the ZIP (tag push)
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v3
with:
draft: false # auto-publish: the notes are generated, the tag is the title
body: ${{ steps.changelog.outputs.body }}
files: plugin/GCodePostProcessor-*.zip

- name: Upload ZIP as an artifact (manual runs)
if: github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v5
with:
name: GCodePostProcessor-plugin
path: plugin/GCodePostProcessor-*.zip
release:
uses: jaysuk/dwc-plugin-workflows/.github/workflows/release.yml@v1
with:
dwc-ref: ${{ inputs.dwc-ref }}
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changelog

Release notes for each version. The GitHub Release body for a version is taken from its section
here (see `scripts/changelog-section.mjs`), so write the section before tagging. Versions before
here, so write the section before tagging (`npm run release` drafts it for you). Versions before
1.3.0 are documented on the [Releases page](https://github.com/jaysuk/duet-gcode-postprocessor/releases).

## [1.3.0] - 2026-09-29
Expand Down
31 changes: 0 additions & 31 deletions scripts/changelog-section.mjs

This file was deleted.

55 changes: 0 additions & 55 deletions scripts/release-footer.mjs

This file was deleted.

25 changes: 25 additions & 0 deletions scripts/release-titles.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# GitHub Release titles for duet-gcode-postprocessor, one per line (blank lines and # comments ignored).
# Picked by version index (semver-sorted), wrapping if it runs out. G-code-editing puns - top up
# the list as it gets low (the release workflow warns when few unused titles remain).
Post Haste
Find and Replace
Regex Appeal
Line by Line
Good Post
Edit Distance
Diff Erence
Patch Notes
Dry Run
Macro Economics
Mind Your G-Code
Code Review
Postmark
Comma Chameleon
Preview Before You Apply
Capture Group Therapy
Search and Rescue
Step by Step
Backup Plan
Safe to Overwrite
Syntax Tax
Beyond the Pale Comments
82 changes: 34 additions & 48 deletions scripts/release.mjs
Original file line number Diff line number Diff line change
@@ -1,57 +1,43 @@
#!/usr/bin/env node
/**
* Cut a release: bump plugin.json + package.json, commit, and create the annotated version tag. The
* tag push is what triggers the CI release build (see .github/workflows/release.yml).
* Launcher for the shared release script (jaysuk/dwc-plugin-workflows). Identical in every plugin and never
* needs editing: it keeps a cached checkout of the shared repo at the floating `v1` tag, refreshes it on each
* run, and hands over to its scripts/release.mjs. So a change to the release flow reaches every plugin without
* touching the plugin repos.
*
* node scripts/release.mjs 0.2.0 # bump + commit + tag (you push when ready)
* node scripts/release.mjs 0.2.0 --push # …and push the commit + tag (triggers the release)
* npm run release -- 0.2.0 [--push]
* npm run release -- 1.3.0 [--push] [--no-notes] [--yes]
*
* Ported from duet-eddy-align's scripts/release.mjs, minus its release-title generator.
* Offline, the cached copy is used. DWC_PLUGIN_WORKFLOWS_DIR=<path> runs a local checkout instead (to try a
* change before tagging it).
*/
import { execFileSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
import { execFileSync, spawnSync } from "node:child_process";
import { existsSync, mkdirSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";

const args = process.argv.slice(2);
const push = args.includes("--push");
const version = args.find((a) => !a.startsWith("-"));
const REPO = "https://github.com/jaysuk/dwc-plugin-workflows.git";
const REF = "v1";

if (!version || !/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(version)) {
console.error("Usage: node scripts/release.mjs <version> [--push] e.g. 0.2.0");
process.exit(1);
let dir = process.env.DWC_PLUGIN_WORKFLOWS_DIR;
if (!dir) {
dir = join(homedir(), ".cache", "dwc-plugin-workflows");
const git = (args) => execFileSync("git", args, { stdio: "pipe", encoding: "utf8" });
try {
if (existsSync(join(dir, ".git"))) {
git(["-C", dir, "fetch", "--quiet", "--depth", "1", "--force", "origin", `+refs/tags/${REF}:refs/tags/${REF}`]);
git(["-C", dir, "checkout", "--quiet", "--force", REF]);
} else {
mkdirSync(dir, { recursive: true });
git(["clone", "--quiet", "--depth", "1", "--branch", REF, REPO, dir]);
}
} catch (e) {
if (!existsSync(join(dir, "scripts", "release.mjs"))) {
console.error(`Could not fetch ${REPO} (${REF}): ${e.message}`);
process.exit(1);
}
console.warn("Could not refresh the shared release scripts (offline?) - using the cached copy.");
}
}
const tag = `v${version}`;

function git(argv) {
execFileSync("git", argv, { stdio: "inherit" });
}

// Refuse to release a dirty tree (other than the two files about to be bumped)
const dirty = execFileSync("git", ["status", "--porcelain"], { encoding: "utf8" })
.split("\n")
.map((l) => l.slice(3))
.filter((f) => f && f !== "plugin.json" && f !== "package.json");
if (dirty.length) {
console.error(`Working tree has uncommitted changes; commit or stash them first:\n ${dirty.join("\n ")}`);
process.exit(1);
}

for (const file of ["plugin.json", "package.json"]) {
const json = JSON.parse(readFileSync(file, "utf8"));
json.version = version;
writeFileSync(file, JSON.stringify(json, null, "\t") + "\n");
}
console.log(`Bumped plugin.json + package.json -> ${version}`);

git(["add", "plugin.json", "package.json"]);
git(["commit", "-m", `chore(release): ${tag}`]);
git(["tag", "-a", tag, "-m", `G-code Post-Processor ${tag}`]);
console.log(`Committed and tagged ${tag}`);

if (push) {
git(["push"]);
git(["push", "origin", tag]);
console.log("Pushed. CI will build the ZIP and publish the GitHub Release.");
} else {
console.log(`\nReview, then push to trigger the release:\n git push && git push origin ${tag}`);
}
const r = spawnSync(process.execPath, [join(dir, "scripts", "release.mjs"), ...process.argv.slice(2)], { stdio: "inherit" });
process.exit(r.status ?? 1);
Loading