From 3fbf83675b19efcd117a0cb710c34b9633863749 Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Wed, 27 May 2026 19:01:19 +0100 Subject: [PATCH 1/6] feat(release): enhance version handling in update-tags-module-version script --- .../release/update-tags-module-version.cjs | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/scripts/release/update-tags-module-version.cjs b/scripts/release/update-tags-module-version.cjs index 5d137bc..b5c443d 100644 --- a/scripts/release/update-tags-module-version.cjs +++ b/scripts/release/update-tags-module-version.cjs @@ -52,19 +52,50 @@ function listFilesRecursively(dirPath) { return files; } +/** + * Ensure a semantic version has a leading v (for example 1.2.3 -> v1.2.3). + */ +function withVPrefix(version) { + return version.startsWith("v") ? version : `v${version}`; +} + +/** + * Build equivalent from/to version pairs so replacements work for both: + * - plain versions (1.2.3) + * - v-prefixed tags (v1.2.3) + */ +function buildVersionPairs(fromVersion, toVersion) { + const pairs = [ + { from: fromVersion, to: toVersion }, + { from: withVPrefix(fromVersion), to: withVPrefix(toVersion) } + ]; + + // De-duplicate if the input was already v-prefixed. + return pairs.filter( + (pair, index, all) => + all.findIndex((item) => item.from === pair.from && item.to === pair.to) === index + ); +} + /** * Replace all occurrences of release-pinned tags module references. */ function updateContent(content, fromVersion, toVersion) { - return content - .replaceAll( - `//infrastructure/modules/tags?ref=${fromVersion}`, - `//infrastructure/modules/tags?ref=${toVersion}` - ) - .replaceAll( - `//infrastructure/modules/tags | ${fromVersion} |`, - `//infrastructure/modules/tags | ${toVersion} |` - ); + let updated = content; + + for (const pair of buildVersionPairs(fromVersion, toVersion)) { + updated = updated + .replaceAll( + `//infrastructure/modules/tags?ref=${pair.from}`, + `//infrastructure/modules/tags?ref=${pair.to}` + ) + .replaceAll( + `//infrastructure/modules/tags | ${pair.from} |`, + `//infrastructure/modules/tags | ${pair.to} |` + ); + } + + return updated; } if (!fs.existsSync(MODULES_ROOT)) { From b3018472e27e19075b676ab61974202715be5f9c Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Wed, 27 May 2026 19:03:04 +0100 Subject: [PATCH 2/6] feat(release): update local module source version references in update-tags-module-version script --- .../release/update-tags-module-version.cjs | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/scripts/release/update-tags-module-version.cjs b/scripts/release/update-tags-module-version.cjs index b5c443d..e728437 100644 --- a/scripts/release/update-tags-module-version.cjs +++ b/scripts/release/update-tags-module-version.cjs @@ -1,7 +1,7 @@ #!/usr/bin/env node /** - * Update references to the local tags module between semantic-release versions. + * Update local module source version references between semantic-release versions. * * Why this exists: * - Keeping release-time string replacements in a standalone script is easier @@ -20,7 +20,6 @@ const fs = require("fs"); const path = require("path"); const MODULES_ROOT = path.join("infrastructure", "modules"); -const TARGET_FILE_NAMES = new Set(["context.tf", "readme.md"]); const [lastVersion, nextVersion] = process.argv.slice(2); @@ -78,21 +77,47 @@ function buildVersionPairs(fromVersion, toVersion) { } /** - * Replace all occurrences of release-pinned tags module references. + * Return true for files that may contain version-pinned local module references: + * - Terraform source files (.tf) + * - Module README files (README.md / readme.md) + */ +function isTargetFile(filePath) { + const fileExt = path.extname(filePath).toLowerCase(); + const baseName = path.basename(filePath).toLowerCase(); + + return fileExt === ".tf" || baseName === "readme.md"; +} + +/** + * Escape user-provided values before embedding into a regular expression. + */ +function escapeRegex(value) { + return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/** + * Replace all release-pinned local module source references, for example: + * git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/?ref= + * and README table rows such as: + * | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/ | | */ function updateContent(content, fromVersion, toVersion) { let updated = content; for (const pair of buildVersionPairs(fromVersion, toVersion)) { + const sourcePattern = new RegExp( + `(git::https://github\\.com/NHSDigital/screening-terraform-modules-aws\\.git//infrastructure/modules/[^?\\s"']+\\?ref=)${escapeRegex(pair.from)}`, + "g" + ); + + const readmeTablePattern = new RegExp( + `(\\|\\s*git::https://github\\.com/NHSDigital/screening-terraform-modules-aws\\.git//infrastructure/modules/[^|\\s]+\\s*\\|\\s*)${escapeRegex(pair.from)}(\\s*\\|)`, + "g" + ); + updated = updated - .replaceAll( - `//infrastructure/modules/tags?ref=${pair.from}`, - `//infrastructure/modules/tags?ref=${pair.to}` - ) - .replaceAll( - `//infrastructure/modules/tags | ${pair.from} |`, - `//infrastructure/modules/tags | ${pair.to} |` - ); + .replace(sourcePattern, `$1${pair.to}`) + .replace(readmeTablePattern, `$1${pair.to}$2`); } return updated; @@ -107,10 +132,7 @@ const allFiles = listFilesRecursively(MODULES_ROOT); let updatedFilesCount = 0; for (const filePath of allFiles) { - const fileName = path.basename(filePath).toLowerCase(); - - // Only process files where these references are expected. - if (!TARGET_FILE_NAMES.has(fileName)) continue; + if (!isTargetFile(filePath)) continue; const original = fs.readFileSync(filePath, "utf8"); const updated = updateContent(original, lastVersion, nextVersion); @@ -123,5 +145,5 @@ for (const filePath of allFiles) { } console.log( - `Updated tags module references from ${lastVersion} to ${nextVersion} in ${updatedFilesCount} file(s).` + `Updated local module source references from ${lastVersion} to ${nextVersion} in ${updatedFilesCount} file(s).` ); From 4db3637dffb66c2c824d950d1f9894eb19cba479 Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Wed, 27 May 2026 19:07:45 +0100 Subject: [PATCH 3/6] feat(tests): add smoke test for update-tags-module-version script --- .../release/update-tags-module-version.cjs | 12 ++-- scripts/tests/release-updater.sh | 58 +++++++++++++++++++ scripts/tests/unit.sh | 2 +- 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100755 scripts/tests/release-updater.sh diff --git a/scripts/release/update-tags-module-version.cjs b/scripts/release/update-tags-module-version.cjs index e728437..1fb3e51 100644 --- a/scripts/release/update-tags-module-version.cjs +++ b/scripts/release/update-tags-module-version.cjs @@ -16,8 +16,8 @@ * node scripts/release/update-tags-module-version.cjs "${lastRelease.version}" "${nextRelease.version}" */ -const fs = require("fs"); -const path = require("path"); +const fs = require("node:fs"); +const path = require("node:path"); const MODULES_ROOT = path.join("infrastructure", "modules"); @@ -105,13 +105,17 @@ function updateContent(content, fromVersion, toVersion) { let updated = content; for (const pair of buildVersionPairs(fromVersion, toVersion)) { + const sourcePrefixPattern = String.raw`(git::https://github\.com/NHSDigital/screening-terraform-modules-aws\.git//infrastructure/modules/[^?\s"']+\?ref=)`; + const readmePrefixPattern = String.raw`(\|\s*git::https://github\.com/NHSDigital/screening-terraform-modules-aws\.git//infrastructure/modules/[^|\s]+\s*\|\s*)`; + const readmeSuffixPattern = String.raw`(\s*\|)`; + const sourcePattern = new RegExp( - `(git::https://github\\.com/NHSDigital/screening-terraform-modules-aws\\.git//infrastructure/modules/[^?\\s"']+\\?ref=)${escapeRegex(pair.from)}`, + sourcePrefixPattern + escapeRegex(pair.from), "g" ); const readmeTablePattern = new RegExp( - `(\\|\\s*git::https://github\\.com/NHSDigital/screening-terraform-modules-aws\\.git//infrastructure/modules/[^|\\s]+\\s*\\|\\s*)${escapeRegex(pair.from)}(\\s*\\|)`, + readmePrefixPattern + escapeRegex(pair.from) + readmeSuffixPattern, "g" ); diff --git a/scripts/tests/release-updater.sh b/scripts/tests/release-updater.sh new file mode 100755 index 0000000..11ca381 --- /dev/null +++ b/scripts/tests/release-updater.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +# Build an isolated fixture so this smoke test never mutates real repo files. +tmp_dir="$(mktemp -d)" +trap 'rm -rf "${tmp_dir}"' EXIT + +mkdir -p "${tmp_dir}/infrastructure/modules/example" + +cat > "${tmp_dir}/infrastructure/modules/example/main.tf" <<'EOF' +module "plain" { + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags?ref=2.0.0" +} + +module "prefixed" { + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/inspector?ref=v2.0.0" +} +EOF + +cat > "${tmp_dir}/infrastructure/modules/example/README.md" <<'EOF' +| Name | Source | Version | +| ---- | ------ | ------- | +| this | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags | 2.0.0 | +| this2 | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/inspector | v2.0.0 | +EOF + +# Show the key lines before update for quick local debugging. +echo "Before update:" +grep -nE "source =|\| git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/" \ + "${tmp_dir}/infrastructure/modules/example/main.tf" \ + "${tmp_dir}/infrastructure/modules/example/README.md" + +( + cd "${tmp_dir}" + node "${OLDPWD}/scripts/release/update-tags-module-version.cjs" 2.0.0 2.1.0 +) + +# Assertions: both plain and v-prefixed references must be updated. +grep -q "tags?ref=2.1.0" "${tmp_dir}/infrastructure/modules/example/main.tf" +grep -q "inspector?ref=v2.1.0" "${tmp_dir}/infrastructure/modules/example/main.tf" +grep -q "modules/tags | 2.1.0 |" "${tmp_dir}/infrastructure/modules/example/README.md" +grep -q "modules/inspector | v2.1.0 |" "${tmp_dir}/infrastructure/modules/example/README.md" + +# Guard against old values remaining in the fixture. +if grep -qE "\?ref=v?2\.0\.0|\|\s*v?2\.0\.0\s*\|" "${tmp_dir}/infrastructure/modules/example/main.tf" "${tmp_dir}/infrastructure/modules/example/README.md"; then + echo "Smoke test failed: old version references are still present." >&2 + exit 1 +fi + +echo "After update:" +grep -nE "source =|\| git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/" \ + "${tmp_dir}/infrastructure/modules/example/main.tf" \ + "${tmp_dir}/infrastructure/modules/example/README.md" + +echo "release-updater smoke test passed" diff --git a/scripts/tests/unit.sh b/scripts/tests/unit.sh index c589be5..2ac7073 100755 --- a/scripts/tests/unit.sh +++ b/scripts/tests/unit.sh @@ -17,4 +17,4 @@ cd "$(git rev-parse --show-toplevel)" # tests from here. If you want to run other test suites, see the predefined # tasks in scripts/test.mk. -echo "Unit tests are not yet implemented. See scripts/tests/unit.sh for more." +./scripts/tests/release-updater.sh From 7a940936385eb8bb67c929ab3c51b939288d9c11 Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Wed, 27 May 2026 19:12:50 +0100 Subject: [PATCH 4/6] feat(release): update module source version to v2.1.0 across inspector and kms modules --- infrastructure/modules/inspector/README.md | 2 +- infrastructure/modules/inspector/context.tf | 2 +- infrastructure/modules/kms/README.md | 2 +- infrastructure/modules/kms/context.tf | 2 +- infrastructure/modules/tags/exports/README.md | 2 +- infrastructure/modules/tags/exports/context.tf | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/infrastructure/modules/inspector/README.md b/infrastructure/modules/inspector/README.md index af666eb..e83c7a9 100644 --- a/infrastructure/modules/inspector/README.md +++ b/infrastructure/modules/inspector/README.md @@ -24,7 +24,7 @@ No providers. | Name | Source | Version | | ---- | ------ | ------- | | [inspector](#module\_inspector) | cloudposse/inspector/aws | 0.4.0 | -| [this](#module\_this) | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags | v2.0.0 | +| [this](#module\_this) | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags | v2.1.0 | ## Resources diff --git a/infrastructure/modules/inspector/context.tf b/infrastructure/modules/inspector/context.tf index e5d757e..713b9bc 100644 --- a/infrastructure/modules/inspector/context.tf +++ b/infrastructure/modules/inspector/context.tf @@ -21,7 +21,7 @@ # module "this" { - source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags?ref=v2.0.0" + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags?ref=v2.1.0" service = var.service project = var.project diff --git a/infrastructure/modules/kms/README.md b/infrastructure/modules/kms/README.md index 7d4ddf1..c2f7d64 100644 --- a/infrastructure/modules/kms/README.md +++ b/infrastructure/modules/kms/README.md @@ -43,7 +43,7 @@ No providers. | Name | Source | Version | | ---- | ------ | ------- | | [kms\_key](#module\_kms\_key) | terraform-aws-modules/kms/aws | 4.2.0 | -| [this](#module\_this) | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags | v2.0.0 | +| [this](#module\_this) | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags | v2.1.0 | ## Resources diff --git a/infrastructure/modules/kms/context.tf b/infrastructure/modules/kms/context.tf index e5d757e..713b9bc 100644 --- a/infrastructure/modules/kms/context.tf +++ b/infrastructure/modules/kms/context.tf @@ -21,7 +21,7 @@ # module "this" { - source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags?ref=v2.0.0" + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags?ref=v2.1.0" service = var.service project = var.project diff --git a/infrastructure/modules/tags/exports/README.md b/infrastructure/modules/tags/exports/README.md index ba05dee..8c8ef89 100644 --- a/infrastructure/modules/tags/exports/README.md +++ b/infrastructure/modules/tags/exports/README.md @@ -15,7 +15,7 @@ No providers. | Name | Source | Version | | ---- | ------ | ------- | -| [this](#module\_this) | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags | v2.0.0 | +| [this](#module\_this) | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags | v2.1.0 | ## Resources diff --git a/infrastructure/modules/tags/exports/context.tf b/infrastructure/modules/tags/exports/context.tf index 89b68c6..c2990c8 100644 --- a/infrastructure/modules/tags/exports/context.tf +++ b/infrastructure/modules/tags/exports/context.tf @@ -21,7 +21,7 @@ # module "this" { - source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags?ref=v2.0.0" + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags?ref=v2.1.0" service = var.service project = var.project From f1fd7e5131b0451c759116f4096d046d9e4b5541 Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Wed, 27 May 2026 19:17:10 +0100 Subject: [PATCH 5/6] feat(release): implement pinning controls for version updates in Terraform files --- .../release/update-tags-module-version.cjs | 90 ++++++++++++++----- scripts/tests/release-updater.sh | 26 +++++- 2 files changed, 92 insertions(+), 24 deletions(-) diff --git a/scripts/release/update-tags-module-version.cjs b/scripts/release/update-tags-module-version.cjs index 1fb3e51..120f12a 100644 --- a/scripts/release/update-tags-module-version.cjs +++ b/scripts/release/update-tags-module-version.cjs @@ -14,12 +14,23 @@ * * Called by semantic-release exec plugin as: * node scripts/release/update-tags-module-version.cjs "${lastRelease.version}" "${nextRelease.version}" + * + * Optional control markers: + * - semantic-release:pin => do not auto-update that .tf line + * - semantic-release:unpin => update now and then remove this marker on that .tf line + * + * Notes: + * - README.md rows are always updated regardless of markers, because README + * content is generated by terraform-docs and marker semantics are only + * intended for hand-edited Terraform source lines. */ const fs = require("node:fs"); const path = require("node:path"); const MODULES_ROOT = path.join("infrastructure", "modules"); +const PIN_TOKEN = "semantic-release:pin"; +const UNPIN_TOKEN = "semantic-release:unpin"; const [lastVersion, nextVersion] = process.argv.slice(2); @@ -100,31 +111,58 @@ function escapeRegex(value) { * git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/?ref= * and README table rows such as: * | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/ | | + * + * Set applyPinningControls=true only for .tf files. */ -function updateContent(content, fromVersion, toVersion) { - let updated = content; - - for (const pair of buildVersionPairs(fromVersion, toVersion)) { - const sourcePrefixPattern = String.raw`(git::https://github\.com/NHSDigital/screening-terraform-modules-aws\.git//infrastructure/modules/[^?\s"']+\?ref=)`; - const readmePrefixPattern = String.raw`(\|\s*git::https://github\.com/NHSDigital/screening-terraform-modules-aws\.git//infrastructure/modules/[^|\s]+\s*\|\s*)`; - const readmeSuffixPattern = String.raw`(\s*\|)`; - - const sourcePattern = new RegExp( - sourcePrefixPattern + escapeRegex(pair.from), - "g" - ); - - const readmeTablePattern = new RegExp( - readmePrefixPattern + escapeRegex(pair.from) + readmeSuffixPattern, - "g" - ); - - updated = updated - .replace(sourcePattern, `$1${pair.to}`) - .replace(readmeTablePattern, `$1${pair.to}$2`); +function updateContentWithMode(content, fromVersion, toVersion, applyPinningControls) { + const eol = content.includes("\r\n") ? "\r\n" : "\n"; + const lines = content.split(/\r?\n/); + const updatedLines = []; + + for (const line of lines) { + const hasPin = line.toLowerCase().includes(PIN_TOKEN); + const hasUnpin = line.toLowerCase().includes(UNPIN_TOKEN); + + // Explicitly pinned .tf lines are never auto-updated. + if (applyPinningControls && hasPin && !hasUnpin) { + updatedLines.push(line); + continue; + } + + // "Unpin" updates this .tf line once and removes the marker. + let updated = applyPinningControls && hasUnpin + ? line + .replace(/\s*#\s*semantic-release:unpin\b/gi, "") + .replace(/\s*\/\/\s*semantic-release:unpin\b/gi, "") + .replace(/\s*/gi, "") + .replace(/\s*semantic-release:unpin\b/gi, "") + .replace(/\s+$/, "") + : line; + + for (const pair of buildVersionPairs(fromVersion, toVersion)) { + const sourcePrefixPattern = String.raw`(git::https://github\.com/NHSDigital/screening-terraform-modules-aws\.git//infrastructure/modules/[^?\s"']+\?ref=)`; + const readmePrefixPattern = String.raw`(\|\s*git::https://github\.com/NHSDigital/screening-terraform-modules-aws\.git//infrastructure/modules/[^|\s]+\s*\|\s*)`; + const readmeSuffixPattern = String.raw`(\s*\|)`; + + const sourcePattern = new RegExp( + sourcePrefixPattern + escapeRegex(pair.from), + "g" + ); + + const readmeTablePattern = new RegExp( + readmePrefixPattern + escapeRegex(pair.from) + readmeSuffixPattern, + "g" + ); + + updated = updated + .replace(sourcePattern, `$1${pair.to}`) + .replace(readmeTablePattern, `$1${pair.to}$2`); + } + + updatedLines.push(updated); } - return updated; + return updatedLines.join(eol); } if (!fs.existsSync(MODULES_ROOT)) { @@ -139,7 +177,13 @@ for (const filePath of allFiles) { if (!isTargetFile(filePath)) continue; const original = fs.readFileSync(filePath, "utf8"); - const updated = updateContent(original, lastVersion, nextVersion); + const applyPinningControls = path.extname(filePath).toLowerCase() === ".tf"; + const updated = updateContentWithMode( + original, + lastVersion, + nextVersion, + applyPinningControls + ); // Avoid touching unchanged files to keep release commits clean. if (updated === original) continue; diff --git a/scripts/tests/release-updater.sh b/scripts/tests/release-updater.sh index 11ca381..4a366d5 100755 --- a/scripts/tests/release-updater.sh +++ b/scripts/tests/release-updater.sh @@ -18,6 +18,14 @@ module "plain" { module "prefixed" { source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/inspector?ref=v2.0.0" } + +module "pinned" { + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/kms?ref=v2.0.0" # semantic-release:pin +} + +module "unpinned_now" { + source = "git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/vpc?ref=v2.0.0" # semantic-release:unpin +} EOF cat > "${tmp_dir}/infrastructure/modules/example/README.md" <<'EOF' @@ -25,6 +33,8 @@ cat > "${tmp_dir}/infrastructure/modules/example/README.md" <<'EOF' | ---- | ------ | ------- | | this | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/tags | 2.0.0 | | this2 | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/inspector | v2.0.0 | +| this3 | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/kms | v2.0.0 | +| this4 | git::https://github.com/NHSDigital/screening-terraform-modules-aws.git//infrastructure/modules/vpc | v2.0.0 | EOF # Show the key lines before update for quick local debugging. @@ -44,8 +54,22 @@ grep -q "inspector?ref=v2.1.0" "${tmp_dir}/infrastructure/modules/example/main.t grep -q "modules/tags | 2.1.0 |" "${tmp_dir}/infrastructure/modules/example/README.md" grep -q "modules/inspector | v2.1.0 |" "${tmp_dir}/infrastructure/modules/example/README.md" +# Assertions: pinned .tf references remain unchanged. +grep -q "kms?ref=v2.0.0\" # semantic-release:pin" "${tmp_dir}/infrastructure/modules/example/main.tf" +grep -q "modules/kms | v2.1.0 | " "${tmp_dir}/infrastructure/modules/example/README.md" + +# Assertions: .tf unpin marker updates now and removes marker. +grep -q "vpc?ref=v2.1.0\"" "${tmp_dir}/infrastructure/modules/example/main.tf" +if grep -q "semantic-release:unpin" "${tmp_dir}/infrastructure/modules/example/main.tf"; then + echo "Smoke test failed: .tf unpin marker should be removed after update." >&2 + exit 1 +fi + +# Assertions: README markers do not control behavior; value updates and marker text remains. +grep -q "modules/vpc | v2.1.0 | " "${tmp_dir}/infrastructure/modules/example/README.md" + # Guard against old values remaining in the fixture. -if grep -qE "\?ref=v?2\.0\.0|\|\s*v?2\.0\.0\s*\|" "${tmp_dir}/infrastructure/modules/example/main.tf" "${tmp_dir}/infrastructure/modules/example/README.md"; then +if grep -nE "\?ref=(2\.0\.0|v2\.0\.0)|\|\s*(2\.0\.0|v2\.0\.0)\s*\|" "${tmp_dir}/infrastructure/modules/example/main.tf" "${tmp_dir}/infrastructure/modules/example/README.md" | grep -vq "semantic-release:pin"; then echo "Smoke test failed: old version references are still present." >&2 exit 1 fi From 95ec0f14e4acb1d44976d71b2d1d33ca05ca8e3e Mon Sep 17 00:00:00 2001 From: Oliver Slater Date: Wed, 27 May 2026 19:19:16 +0100 Subject: [PATCH 6/6] feat(release): refactor regex escaping in update-tags-module-version script --- scripts/release/update-tags-module-version.cjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/release/update-tags-module-version.cjs b/scripts/release/update-tags-module-version.cjs index 120f12a..af0c49f 100644 --- a/scripts/release/update-tags-module-version.cjs +++ b/scripts/release/update-tags-module-version.cjs @@ -31,6 +31,7 @@ const path = require("node:path"); const MODULES_ROOT = path.join("infrastructure", "modules"); const PIN_TOKEN = "semantic-release:pin"; const UNPIN_TOKEN = "semantic-release:unpin"; +const REGEX_SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g; const [lastVersion, nextVersion] = process.argv.slice(2); @@ -103,7 +104,7 @@ function isTargetFile(filePath) { * Escape user-provided values before embedding into a regular expression. */ function escapeRegex(value) { - return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return value.replace(REGEX_SPECIAL_CHARS, String.raw`\$&`); } /**