From 322096c593cf9953ef51c346d4ff7efa796ccbf4 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Fri, 10 Jul 2026 20:41:00 +0200 Subject: [PATCH 1/5] chore: remove release-please configs to add git-cliff release --- .../.release-please-manifest.json | 3 - .../release-please/release-please-config.json | 8 -- .github/workflows/release-please.yml | 21 ---- .github/workflows/release-pr.yml | 114 ++++++++++++++++++ .github/workflows/release-publish.yml | 61 ++++++++++ CHANGELOG.MD | 0 6 files changed, 175 insertions(+), 32 deletions(-) delete mode 100644 .github/release-please/.release-please-manifest.json delete mode 100644 .github/release-please/release-please-config.json delete mode 100644 .github/workflows/release-please.yml create mode 100644 .github/workflows/release-pr.yml create mode 100644 .github/workflows/release-publish.yml create mode 100644 CHANGELOG.MD diff --git a/.github/release-please/.release-please-manifest.json b/.github/release-please/.release-please-manifest.json deleted file mode 100644 index e18ee0776..000000000 --- a/.github/release-please/.release-please-manifest.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - ".": "0.0.0" -} diff --git a/.github/release-please/release-please-config.json b/.github/release-please/release-please-config.json deleted file mode 100644 index 9ca39d5d9..000000000 --- a/.github/release-please/release-please-config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "packages": { - ".": { - "release-type": "simple", - "component": "Security Frameworks" - } - } -} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml deleted file mode 100644 index 71ea4a829..000000000 --- a/.github/workflows/release-please.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Release Please - -on: - push: - branches: [main] - -permissions: - contents: write - pull-requests: write - -jobs: - release-please: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - name: Create or update release PR - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 - with: - target-branch: main - config-file: .github/release-please/release-please-config.json - manifest-file: .github/release-please/.release-please-manifest.json diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml new file mode 100644 index 000000000..f9366dc66 --- /dev/null +++ b/.github/workflows/release-pr.yml @@ -0,0 +1,114 @@ +name: Release PR + +on: + push: + branches: [main] + workflow_dispatch: + inputs: + version: + description: "Override version (e.g. v0.0.3). Leave blank to auto-bump." + required: false + type: string + +permissions: {} + +concurrency: + group: release-pr + cancel-in-progress: false + +jobs: + release-pr: + if: ${{ github.event_name == 'workflow_dispatch' || !contains(github.event.head_commit.message, 'chore(release):') }} + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + with: + fetch-depth: 0 + fetch-tags: true + persist-credentials: false + + - name: Compute next version + id: version + env: + OVERRIDE: ${{ inputs.version }} + run: | + set -euo pipefail + if [ -n "${OVERRIDE:-}" ]; then + if ! printf '%s' "$OVERRIDE" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Invalid version override '$OVERRIDE' (expected vMAJOR.MINOR.PATCH)" + exit 1 + fi + next="$OVERRIDE" + echo "Using override: $next" + else + latest=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n1 || true) + if [ -z "$latest" ]; then + next="v0.0.1" + else + v="${latest#v}" + major="${v%%.*}" + rest="${v#*.}" + minor="${rest%%.*}" + patch="${rest#*.}" + next="v${major}.${minor}.$((patch + 1))" + fi + echo "Latest tag: ${latest:-none}" + fi + echo "next=$next" >> "$GITHUB_OUTPUT" + echo "Next version: $next" + + - name: Ensure changelog exists + run: touch CHANGELOG.md + + - name: Generate changelog + uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4.8.0 + id: git-cliff + with: + config: cliff.toml + github_token: ${{ secrets.GITHUB_TOKEN }} + args: --unreleased --tag ${{ steps.version.outputs.next }} --prepend CHANGELOG.md + env: + OUTPUT: RELEASE_NOTES.md + + - name: Check for releasable changes + id: check + run: | + set -euo pipefail + if grep -qE '^- ' RELEASE_NOTES.md; then + echo "has_changes=true" >> "$GITHUB_OUTPUT" + echo "Changes found:" + cat RELEASE_NOTES.md + else + echo "has_changes=false" >> "$GITHUB_OUTPUT" + echo "Nothing to release since the last tag. Skipping." + fi + + - name: Write VERSION + if: steps.check.outputs.has_changes == 'true' + env: + NEXT: ${{ steps.version.outputs.next }} + run: printf '%s\n' "$NEXT" > VERSION + + - name: Open release PR + if: steps.check.outputs.has_changes == 'true' + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11 + with: + token: ${{ secrets.GITHUB_TOKEN }} + sign-commits: true + base: main + branch: release/${{ steps.version.outputs.next }} + title: "chore(release): ${{ steps.version.outputs.next }}" + commit-message: "chore(release): ${{ steps.version.outputs.next }}" + labels: release + delete-branch: true + add-paths: | + CHANGELOG.md + VERSION + body: | + Automated release PR for **${{ steps.version.outputs.next }}**. + + Review the changelog, edit if needed, then merge. + Merging creates the tag and publishes the GitHub Release. \ No newline at end of file diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml new file mode 100644 index 000000000..340312933 --- /dev/null +++ b/.github/workflows/release-publish.yml @@ -0,0 +1,61 @@ +name: Publish Release + +on: + pull_request: + types: [closed] + +permissions: {} + +jobs: + publish: + if: > + github.event.pull_request.merged == true && + github.event.pull_request.base.ref == 'main' && + startsWith(github.event.pull_request.head.ref, 'release/') && + contains(github.event.pull_request.labels.*.name, 'release') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + with: + ref: main + fetch-depth: 0 + persist-credentials: false + + - name: Read version + id: version + run: | + set -euo pipefail + tag="$(head -n1 VERSION | tr -d '[:space:]')" + if ! printf '%s' "$tag" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Invalid VERSION content '$tag' (expected vMAJOR.MINOR.PATCH)" + exit 1 + fi + echo "tag=$tag" >> "$GITHUB_OUTPUT" + + - name: Extract latest changelog section + run: | + awk '/^## /{ if (found) exit; found=1; next } found' CHANGELOG.md > RELEASE_NOTES.md + cat RELEASE_NOTES.md + + - name: Create tag + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.version.outputs.tag }} + run: | + set -euo pipefail + sha="$(git rev-parse HEAD)" + gh api "repos/${GITHUB_REPOSITORY}/git/refs" \ + -f "ref=refs/tags/${TAG}" \ + -f "sha=${sha}" + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.version.outputs.tag }} + run: | + set -euo pipefail + gh release create "$TAG" \ + --title "$TAG" \ + --notes-file RELEASE_NOTES.md diff --git a/CHANGELOG.MD b/CHANGELOG.MD new file mode 100644 index 000000000..e69de29bb From 5f27386390e63d9688a1cf650e1dcbbb308b6726 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Fri, 10 Jul 2026 20:53:13 +0200 Subject: [PATCH 2/5] add cliff.toml for git-cliff configs --- cliff.toml | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 cliff.toml diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 000000000..3e6e86a5f --- /dev/null +++ b/cliff.toml @@ -0,0 +1,49 @@ +[changelog] +header = """ +# Changelog + +All notable changes to the Security Frameworks are documented here. +""" + +body = """ +{% macro remote_url() %}https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}{% endmacro %} +{% if version %} +## {{ version | trim_start_matches(pat="v") }} ({{ timestamp | date(format="%Y-%m-%d") }}) +{% else %} +## Unreleased +{% endif %} +{% for group, commits in commits | filter(attribute="merge_commit", value=false) | group_by(attribute="group") %} +### {{ group | striptags | trim | upper_first }} +{% for commit in commits %} +{%- if commit.remote.pr_title -%} +{%- set entry = commit.remote.pr_title -%} +{%- else -%} +{%- set entry = commit.message | split(pat="\n") | first -%} +{%- endif %} +- {{ entry | trim }}{% if commit.remote.pr_number %} ([#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }})){% endif %}{% if commit.remote.username %} by @{{ commit.remote.username }}{% endif %} +{%- endfor %} +{% endfor %} +""" + +trim = true + +[git] +conventional_commits = false +filter_unconventional = false +filter_commits = false +sort_commits = "oldest" + +commit_parsers = [ + { message = "^Merge pull request", skip = true }, + { message = "^Merge branch", skip = true }, + { message = "^chore\\(release\\)", skip = true }, + + { field = "github.pr_labels", pattern = "dependencies", skip = true }, + + { field = "github.pr_labels", pattern = "content:add", group = " New content" }, + { field = "github.pr_labels", pattern = "content:update", group = " Updated content" }, + { field = "github.pr_labels", pattern = "enhancement", group = " Enhancements" }, + { field = "github.pr_labels", pattern = "certifications", group = " Certifications" }, + + { message = ".*", group = " Other" }, +] \ No newline at end of file From e4f4f5e7a1d97cd510a8c68666d98f6473123b47 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Fri, 10 Jul 2026 21:09:42 +0200 Subject: [PATCH 3/5] Rename CHANGELOG.MD to CHANGELOG.md --- CHANGELOG.MD | 0 CHANGELOG.md | 1 + 2 files changed, 1 insertion(+) delete mode 100644 CHANGELOG.MD create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.MD b/CHANGELOG.MD deleted file mode 100644 index e69de29bb..000000000 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..d3f5a12fa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ + From a67281cfa4f97050d629fde9ac80e02f6de62827 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Fri, 31 Jul 2026 14:44:57 +0200 Subject: [PATCH 4/5] add changelog page + version in the header with changelog callout --- docs/pages/changelog.mdx | 5 +++++ package.json | 2 +- vocs.config.ts | 43 ++++++++++++++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 docs/pages/changelog.mdx diff --git a/docs/pages/changelog.mdx b/docs/pages/changelog.mdx new file mode 100644 index 000000000..c7774200a --- /dev/null +++ b/docs/pages/changelog.mdx @@ -0,0 +1,5 @@ +# Changelog + +Release notes for the Security Frameworks, generated from [GitHub Releases](https://github.com/security-alliance/frameworks/releases). + +::changelog diff --git a/package.json b/package.json index 53caff303..df7af2a3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "workspace", - "version": "1.0.0", + "version": "0.0.1", "description": "Official repository to the Security Frameworks by SEAL. This repository contains the entire structure and contents of the frameworks. Feel free to suggest from new categories to grammar corrections. Collaboration is open to everyone. **This is a work in progress.**", "main": "index.js", "type": "module", diff --git a/vocs.config.ts b/vocs.config.ts index 3f3842ff4..2938229bb 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -1,18 +1,21 @@ -import { createElement, Fragment } from 'react' -import { defineConfig } from 'vocs/config' +import { readFileSync } from 'node:fs' +import { Changelog, defineConfig } from 'vocs/config' -const MAIN_SITE_URL = 'https://frameworks.securityalliance.org' +function siteVersion(): string { + try { + const raw = readFileSync(new URL('./VERSION', import.meta.url), 'utf8').trim() + if (raw) return raw.startsWith('v') ? raw : `v${raw}` + } catch {} + return 'v0.0.0' +} const isMainBranch = process.env.CF_PAGES_BRANCH === 'main' const config = { srcDir: 'docs', renderStrategy: 'full-static' as const, - head({ path }: { path: string }) { - const cleanPath = path.replace(/\/index\.html$/, '').replace(/\.html$/, '').replace(/\/$/, '') - if (!isMainBranch && devOnlyLinks.has(cleanPath)) return createElement(Fragment) - const canonicalUrl = `${MAIN_SITE_URL}${cleanPath || '/'}` - return createElement('link', { rel: 'canonical', href: canonicalUrl }) + head(path: string) { const cleanPath = path.replace(/\/index\.html$/, '').replace(/\.html$/, '').replace(/\/$/, '') + return { canonical: `https://frameworks.securityalliance.org${cleanPath || '/'}` } }, banner: { content: 'This is a work in progress and not a release. We are looking for volunteers. See [Issues](https://github.com/security-alliance/frameworks/issues) and [Contribution](https://github.com/security-alliance/frameworks/blob/develop/docs/pages/contribute/contributing.mdx) to know how to collaborate.', @@ -28,6 +31,16 @@ const config = { iconUrl: 'https://frameworks-static.s3.us-east-2.amazonaws.com/images/logo/favicon.svg', ogImageUrl: 'https://frameworks-static.s3.us-east-2.amazonaws.com/images/logo/frameworks-full-cropped.png', checkDeadlinks: "warn" as const, + changelog: Changelog.github({ repo: 'security-alliance/frameworks' }), + topNav: [ + { + text: siteVersion(), + items: [ + { text: 'Changelog', link: '/changelog' }, + { text: 'Contributing', link: '/contribute/contributing' }, + ], + }, + ], sidebar: [ { text: 'Introduction', @@ -701,6 +714,20 @@ function collectDevLinks(items: any[], parentIsDev = false): Set { const devOnlyLinks = collectDevLinks(config.sidebar) +if (!isMainBranch && devOnlyLinks.size > 0) { + const devRoutesLiteral = JSON.stringify([...devOnlyLinks]) + // @ts-expect-error - head is rebuilt from source so the dev-route list is + config.head = new Function( + 'path', + ` + const devOnlyLinks = new Set(${devRoutesLiteral}); + const cleanPath = path.replace(/\\/index\\.html$/, '').replace(/\\.html$/, '').replace(/\\/$/, ''); + if (devOnlyLinks.has(cleanPath)) return { canonical: false }; + return { canonical: 'https://frameworks.securityalliance.org' + (cleanPath || '/') }; + `, + ) +} + if (isMainBranch) { config.sidebar = filterDevItems(config.sidebar) } From 10361a46872da230fffeefb63dd06ec6b84bee38 Mon Sep 17 00:00:00 2001 From: Sara Russo Date: Fri, 31 Jul 2026 14:54:27 +0200 Subject: [PATCH 5/5] fix version reading function in vocs.config file to fix index generator run --- utils/generate-folder-indexes.cjs | 2 +- vocs.config.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/utils/generate-folder-indexes.cjs b/utils/generate-folder-indexes.cjs index cebd83e34..50056e6b6 100644 --- a/utils/generate-folder-indexes.cjs +++ b/utils/generate-folder-indexes.cjs @@ -247,7 +247,7 @@ function loadSidebarConfig(branchName) { .replace(/^(?:import[^\n]*\n)+/, '') .replace(/export default defineConfig\(config\)\s*;?\s*$/, 'return defineConfig(config);') .replace(/\bas const\b/g, '') - .replace(/head\(\{[^}]*\}\s*:\s*\{[^}]*\}\)\s*\{[\s\S]*?\n \},/, '') + .replace(/head\([^)]*\)\s*\{[\s\S]*?\n \},/, '') .replace(/new Set<[^>]+>\(/g, 'new Set(') .replace(/function\s+(\w+)\(([^)]*)\)\s*:\s*[^\s{]+/g, (_, name, params) => { const cleaned = params.replace(/:\s*[^,)]+/g, ''); diff --git a/vocs.config.ts b/vocs.config.ts index 2938229bb..dd958e604 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -3,7 +3,7 @@ import { Changelog, defineConfig } from 'vocs/config' function siteVersion(): string { try { - const raw = readFileSync(new URL('./VERSION', import.meta.url), 'utf8').trim() + const raw = readFileSync('VERSION', 'utf8').trim() if (raw) return raw.startsWith('v') ? raw : `v${raw}` } catch {} return 'v0.0.0' @@ -31,7 +31,10 @@ const config = { iconUrl: 'https://frameworks-static.s3.us-east-2.amazonaws.com/images/logo/favicon.svg', ogImageUrl: 'https://frameworks-static.s3.us-east-2.amazonaws.com/images/logo/frameworks-full-cropped.png', checkDeadlinks: "warn" as const, - changelog: Changelog.github({ repo: 'security-alliance/frameworks' }), + changelog: + typeof Changelog !== 'undefined' + ? Changelog.github({ repo: 'security-alliance/frameworks' }) + : undefined, topNav: [ { text: siteVersion(),