spec-up-t 1.6.9 → 1.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/freeze-spec-data.js +83 -8
- package/src/install-from-boilerplate/boilerplate/.github/workflows/menu.yml +20 -14
- package/src/install-from-boilerplate/boilerplate/.github/workflows/render-and-deploy.yml +109 -0
- package/src/install-from-boilerplate/boilerplate/.github/workflows/set-gh-pages.yml +42 -40
- package/src/install-from-boilerplate/boilerplate/gitignore +56 -6
- package/src/install-from-boilerplate/config-gitignore-entries.js +3 -1
- package/src/install-from-boilerplate/config-system-files.js +1 -0
- package/src/pipeline/configuration/create-versions-index.js +8 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spec-up-t",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.11",
|
|
4
4
|
"description": "Technical specification drafting tool that generates rich specification documents from markdown. Forked from https://github.com/decentralized-identity/spec-up by Daniel Buchner (https://github.com/csuwildcat)",
|
|
5
5
|
"main": "./index",
|
|
6
6
|
"repository": {
|
package/src/freeze-spec-data.js
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
* @file freeze-spec-data.js
|
|
3
3
|
* @description Reads the output path from specs.json, finds the highest versioned directory
|
|
4
4
|
* in the destination path, and copies index.html to a new directory with an incremented version.
|
|
5
|
+
* The user is prompted for a human-readable label for the snapshot; the label is persisted in
|
|
6
|
+
* versions/labels.json so that create-versions-index.js can use it as link text.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
9
|
const fs = require('fs-extra');
|
|
8
10
|
const path = require('path');
|
|
11
|
+
const readline = require('node:readline');
|
|
9
12
|
const Logger = require('./utils/logger');
|
|
10
13
|
const { versions } = require('./utils/regex-patterns');
|
|
11
14
|
|
|
@@ -15,6 +18,16 @@ const outputPath = config.specs[0].output_path;
|
|
|
15
18
|
const sourceFile = path.join(outputPath, 'index.html');
|
|
16
19
|
const destDir = path.join(outputPath, 'versions');
|
|
17
20
|
|
|
21
|
+
// A snapshot can only be created when the specification has been built at least once.
|
|
22
|
+
// If index.html is missing, the user must run `npm run menu 1` (or `npm run menu 4`) first.
|
|
23
|
+
if (!fs.existsSync(sourceFile)) {
|
|
24
|
+
Logger.error(
|
|
25
|
+
`Cannot create a snapshot: "${sourceFile}" does not exist.\n` +
|
|
26
|
+
`Please build the specification first (e.g. npm run menu 1 or npm run menu 9) and try again.`
|
|
27
|
+
);
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
if (!fs.existsSync(destDir)) {
|
|
19
32
|
fs.mkdirSync(destDir, { recursive: true });
|
|
20
33
|
}
|
|
@@ -35,16 +48,78 @@ dirs.forEach(dir => {
|
|
|
35
48
|
|
|
36
49
|
const newVersion = highestVersion + 1;
|
|
37
50
|
const newVersionDir = path.join(destDir, `v${newVersion}`);
|
|
51
|
+
const defaultLabel = `v${newVersion}`;
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Prompts the user for a single line of input, showing a default value.
|
|
55
|
+
* Pressing Enter without typing accepts the default.
|
|
56
|
+
* @param {string} question - The question text shown to the user.
|
|
57
|
+
* @param {string} defaultValue - The value used when the user presses Enter without typing.
|
|
58
|
+
* @returns {Promise<string>}
|
|
59
|
+
*/
|
|
60
|
+
function prompt(question, defaultValue) {
|
|
61
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
62
|
+
return new Promise(resolve => {
|
|
63
|
+
rl.question(`${question} [${defaultValue}]: `, answer => {
|
|
64
|
+
rl.close();
|
|
65
|
+
resolve(answer.trim() || defaultValue);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
41
68
|
}
|
|
42
69
|
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Persists the label for the given directory name in versions/labels.json.
|
|
72
|
+
* Existing entries are preserved; only the new key is added or updated.
|
|
73
|
+
* @param {string} labelsFile - Absolute path to labels.json.
|
|
74
|
+
* @param {string} dirName - The version directory name (e.g. "v3").
|
|
75
|
+
* @param {string} label - The human-readable label to store.
|
|
76
|
+
*/
|
|
77
|
+
function saveLabel(labelsFile, dirName, label) {
|
|
78
|
+
const existing = fs.existsSync(labelsFile) ? fs.readJsonSync(labelsFile) : {};
|
|
79
|
+
existing[dirName] = label;
|
|
80
|
+
fs.writeJsonSync(labelsFile, existing, { spaces: 2 });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Main flow: resolve the label, create the version directory, copy the snapshot,
|
|
85
|
+
* save the label, and regenerate the versions index.
|
|
86
|
+
*
|
|
87
|
+
* Label resolution order:
|
|
88
|
+
* 1. FREEZE_LABEL environment variable — set by the menu.yml GitHub Actions workflow
|
|
89
|
+
* so that GitHubUi (or any non-interactive caller) can supply a custom label.
|
|
90
|
+
* 2. Interactive readline prompt — only used when stdin is a real TTY (local CLI).
|
|
91
|
+
* 3. Default label (e.g. "v3") — fallback for non-interactive environments without
|
|
92
|
+
* an explicit FREEZE_LABEL (e.g. running freeze directly inside a CI script).
|
|
93
|
+
*/
|
|
94
|
+
async function run() {
|
|
95
|
+
let label;
|
|
96
|
+
|
|
97
|
+
if (process.env.FREEZE_LABEL) {
|
|
98
|
+
// Non-interactive path: label supplied by caller via environment variable
|
|
99
|
+
label = process.env.FREEZE_LABEL;
|
|
100
|
+
} else if (process.stdin.isTTY) {
|
|
101
|
+
// Interactive path: prompt the user, defaulting to the auto-generated name
|
|
102
|
+
label = await prompt('Enter a label for this snapshot', defaultLabel);
|
|
103
|
+
} else {
|
|
104
|
+
// Non-interactive path without an explicit label: use the auto-generated default
|
|
105
|
+
label = defaultLabel;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!fs.existsSync(newVersionDir)) {
|
|
109
|
+
fs.mkdirSync(newVersionDir, { recursive: true });
|
|
110
|
+
}
|
|
45
111
|
|
|
46
|
-
|
|
112
|
+
const destFile = path.join(newVersionDir, 'index.html');
|
|
113
|
+
fs.copyFileSync(sourceFile, destFile);
|
|
114
|
+
|
|
115
|
+
const labelsFile = path.join(destDir, 'labels.json');
|
|
116
|
+
saveLabel(labelsFile, `v${newVersion}`, label);
|
|
117
|
+
|
|
118
|
+
Logger.success(`Created a freezed specification version in ${destFile}`);
|
|
119
|
+
|
|
120
|
+
// Update the versions index.html to include the newly created version
|
|
121
|
+
const createVersionsIndex = require('./pipeline/configuration/create-versions-index.js');
|
|
122
|
+
createVersionsIndex(outputPath);
|
|
123
|
+
}
|
|
47
124
|
|
|
48
|
-
|
|
49
|
-
const createVersionsIndex = require('./pipeline/configuration/create-versions-index.js');
|
|
50
|
-
createVersionsIndex(outputPath);
|
|
125
|
+
run();
|
|
@@ -24,12 +24,17 @@ on:
|
|
|
24
24
|
triggered_by:
|
|
25
25
|
description: 'Triggered by'
|
|
26
26
|
required: false
|
|
27
|
+
freeze_label:
|
|
28
|
+
description: 'Label for the snapshot (freeze action only). Leave empty to use the auto-generated version number.'
|
|
29
|
+
required: false
|
|
30
|
+
default: ''
|
|
27
31
|
|
|
28
32
|
jobs:
|
|
29
33
|
build-and-deploy-spec:
|
|
30
34
|
runs-on: ubuntu-latest
|
|
31
35
|
permissions:
|
|
32
|
-
contents: write
|
|
36
|
+
contents: write # Needed for pushing changes
|
|
37
|
+
actions: write # Needed to dispatch render-and-deploy.yml
|
|
33
38
|
steps:
|
|
34
39
|
- name: Checkout 🛎️
|
|
35
40
|
uses: actions/checkout@v4
|
|
@@ -64,7 +69,14 @@ jobs:
|
|
|
64
69
|
run: |
|
|
65
70
|
case "${{ github.event.inputs.action_type }}" in
|
|
66
71
|
render)
|
|
67
|
-
|
|
72
|
+
# Rendering is handled by render-and-deploy.yml.
|
|
73
|
+
# Dispatch that workflow to render and deploy to GitHub Pages.
|
|
74
|
+
curl -s -X POST \
|
|
75
|
+
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
|
76
|
+
-H "Accept: application/vnd.github+json" \
|
|
77
|
+
"https://api.github.com/repos/${{ github.repository }}/actions/workflows/render-and-deploy.yml/dispatches" \
|
|
78
|
+
-d "{\"ref\":\"${{ github.ref_name }}\"}"
|
|
79
|
+
echo "render-and-deploy.yml dispatched"
|
|
68
80
|
;;
|
|
69
81
|
topdf)
|
|
70
82
|
npm run topdf
|
|
@@ -73,7 +85,9 @@ jobs:
|
|
|
73
85
|
npm run todocx
|
|
74
86
|
;;
|
|
75
87
|
freeze)
|
|
76
|
-
|
|
88
|
+
# Pass the label to freeze-spec-data.js via environment variable.
|
|
89
|
+
# If freeze_label is empty the script falls back to the auto-generated default.
|
|
90
|
+
FREEZE_LABEL="${{ github.event.inputs.freeze_label }}" npm run freeze
|
|
77
91
|
;;
|
|
78
92
|
custom-update)
|
|
79
93
|
npm run custom-update
|
|
@@ -99,7 +113,8 @@ jobs:
|
|
|
99
113
|
# Commit with appropriate message
|
|
100
114
|
case "${{ github.event.inputs.action_type }}" in
|
|
101
115
|
render)
|
|
102
|
-
|
|
116
|
+
# render-and-deploy.yml already handles the commit/deploy; nothing to commit here.
|
|
117
|
+
echo "Render dispatched to render-and-deploy.yml — no direct commit needed"
|
|
103
118
|
;;
|
|
104
119
|
topdf)
|
|
105
120
|
git commit -m "Export to PDF" || echo "No changes to commit"
|
|
@@ -122,16 +137,7 @@ jobs:
|
|
|
122
137
|
esac
|
|
123
138
|
|
|
124
139
|
# Push changes
|
|
125
|
-
git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git HEAD
|
|
126
|
-
|
|
127
|
-
- name: Commit output files
|
|
128
|
-
if: success()
|
|
129
|
-
run: |
|
|
130
|
-
git config --global user.email "actions@github.com"
|
|
131
|
-
git config --global user.name "GitHub Actions"
|
|
132
|
-
git add "$OUTPUT_PATH"
|
|
133
|
-
git commit -m "Update output files in $OUTPUT_PATH" || echo "No changes to commit in output directory"
|
|
134
|
-
git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git HEAD:main
|
|
140
|
+
git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git HEAD:${{ github.ref_name }}
|
|
135
141
|
|
|
136
142
|
- name: Clean up
|
|
137
143
|
if: always()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: Render and Deploy to GitHub Pages
|
|
2
|
+
|
|
3
|
+
# Triggered automatically on every push to main or master, or manually via
|
|
4
|
+
# the Actions tab (or when dispatched by menu.yml for a render action).
|
|
5
|
+
# Renders the specification and pushes the output to the gh-pages branch.
|
|
6
|
+
# docs/ is NEVER committed to main/master.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
- master
|
|
13
|
+
# Do not trigger on workflow file changes (.github/). Each workflow file
|
|
14
|
+
# is uploaded as a separate commit during repo creation, which would cause
|
|
15
|
+
# multiple competing runs in the same concurrency group. Real spec content
|
|
16
|
+
# changes (spec/, specs.json, package.json, etc.) still trigger a render.
|
|
17
|
+
# The initial render after repo creation is handled by an explicit dispatch.
|
|
18
|
+
# The trigger matrix is now:
|
|
19
|
+
#
|
|
20
|
+
# Event --> Triggers render?
|
|
21
|
+
#
|
|
22
|
+
# Push spec content (spec, specs.json, etc.) --> ✅ yes
|
|
23
|
+
# Push `.github/workflows/*.yml` (repo creation) --> ❌ no
|
|
24
|
+
# `workflow_dispatch` (GitHubUi "render" button, or menu.yml) --> ✅ yes
|
|
25
|
+
# PR into main/master --> ❌ not triggered (no `pull_request:` event)
|
|
26
|
+
|
|
27
|
+
paths-ignore:
|
|
28
|
+
- '.github/**'
|
|
29
|
+
workflow_dispatch:
|
|
30
|
+
|
|
31
|
+
# Only allow one deployment at a time; do not cancel in-progress runs so that
|
|
32
|
+
# a completed deploy is never replaced by a stale one.
|
|
33
|
+
# NOTE: do NOT use 'pages' as the group name — that conflicts with GitHub's own
|
|
34
|
+
# internal Pages deployment concurrency group and causes spurious cancellations.
|
|
35
|
+
concurrency:
|
|
36
|
+
group: render-and-deploy-${{ github.ref }}
|
|
37
|
+
cancel-in-progress: false
|
|
38
|
+
|
|
39
|
+
jobs:
|
|
40
|
+
# ── Build & Deploy ────────────────────────────────────────────────────────────
|
|
41
|
+
build-and-deploy:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
# GitHub automatically generates a GITHUB_TOKEN for each workflow run.
|
|
44
|
+
# This ephemeral token is scoped only to the current repository and expires
|
|
45
|
+
# after the run completes. The 'permissions' block declares what this token
|
|
46
|
+
# is allowed to do — GitHub denies operations outside these scopes.
|
|
47
|
+
permissions:
|
|
48
|
+
contents: write # Required by peaceiris/actions-gh-pages to push to gh-pages
|
|
49
|
+
pages: write # Required to configure GitHub Pages source via API
|
|
50
|
+
steps:
|
|
51
|
+
- name: Checkout 🛎️
|
|
52
|
+
uses: actions/checkout@v4
|
|
53
|
+
|
|
54
|
+
- name: Set up Node.js
|
|
55
|
+
uses: actions/setup-node@v4
|
|
56
|
+
with:
|
|
57
|
+
node-version: '20'
|
|
58
|
+
|
|
59
|
+
- name: Install dependencies 🔧
|
|
60
|
+
run: npm install
|
|
61
|
+
|
|
62
|
+
# Collect external references — this also renders the specification and
|
|
63
|
+
# writes output to the path defined in specs.json (output_path, typically ./docs/).
|
|
64
|
+
- name: Collect external references and render
|
|
65
|
+
run: npm run collectExternalReferences
|
|
66
|
+
|
|
67
|
+
# Push the rendered ./docs output to the gh-pages branch.
|
|
68
|
+
# peaceiris/actions-gh-pages creates the branch on first run when it
|
|
69
|
+
# does not yet exist — no manual branch setup is required.
|
|
70
|
+
# Uses GITHUB_TOKEN (automatically injected by GitHub) with 'contents: write'
|
|
71
|
+
# permission to push to this repository.
|
|
72
|
+
- name: Deploy to GitHub Pages 🚀
|
|
73
|
+
uses: peaceiris/actions-gh-pages@v3
|
|
74
|
+
with:
|
|
75
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
76
|
+
publish_dir: ./docs
|
|
77
|
+
publish_branch: gh-pages
|
|
78
|
+
|
|
79
|
+
# Configure GitHub Pages to serve from the gh-pages branch (root /).
|
|
80
|
+
# This uses the GitHub REST API with GITHUB_TOKEN (permission: 'pages: write').
|
|
81
|
+
# It is idempotent — safe to run on every render (201=create, 409/422=exists, update).
|
|
82
|
+
# GITHUB_TOKEN is ephemeral (created per-run, expires at run end), scoped only to
|
|
83
|
+
# this repository, and never logged. GitHub accepts it because the token is issued
|
|
84
|
+
# by GitHub itself and this workflow declares 'pages: write' permission.
|
|
85
|
+
# continue-on-error: the API call may return a conflict on the very first run while
|
|
86
|
+
# GitHub is still processing the new gh-pages branch — this is non-fatal.
|
|
87
|
+
- name: Configure GitHub Pages source
|
|
88
|
+
continue-on-error: true
|
|
89
|
+
run: |
|
|
90
|
+
TOKEN="${{ secrets.GITHUB_TOKEN }}"
|
|
91
|
+
REPO="${{ github.repository }}"
|
|
92
|
+
|
|
93
|
+
HTTP_CODE=$(curl -s -o response.json -w "%{http_code}" \
|
|
94
|
+
-X POST \
|
|
95
|
+
-H "Authorization: token $TOKEN" \
|
|
96
|
+
-H "Accept: application/vnd.github+json" \
|
|
97
|
+
"https://api.github.com/repos/$REPO/pages" \
|
|
98
|
+
-d '{"source":{"branch":"gh-pages","path":"/"}}')
|
|
99
|
+
|
|
100
|
+
# 201 = created; 409/422 = already exists, update instead
|
|
101
|
+
if [ "$HTTP_CODE" -eq 409 ] || [ "$HTTP_CODE" -eq 422 ]; then
|
|
102
|
+
curl -s -o /dev/null \
|
|
103
|
+
-X PUT \
|
|
104
|
+
-H "Authorization: token $TOKEN" \
|
|
105
|
+
-H "Accept: application/vnd.github+json" \
|
|
106
|
+
"https://api.github.com/repos/$REPO/pages" \
|
|
107
|
+
-d '{"source":{"branch":"gh-pages","path":"/"}}'
|
|
108
|
+
fi
|
|
109
|
+
echo "GitHub Pages source set to gh-pages branch"
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
name: Set GitHub Pages and Homepage
|
|
2
2
|
|
|
3
|
+
# One-time setup workflow: run this AFTER the first successful
|
|
4
|
+
# render-and-deploy run, which creates the gh-pages branch.
|
|
5
|
+
# It configures GitHub Pages to serve from the gh-pages branch (root /)
|
|
6
|
+
# and sets the repository homepage URL.
|
|
7
|
+
|
|
3
8
|
on:
|
|
4
9
|
workflow_dispatch:
|
|
5
10
|
|
|
@@ -7,93 +12,90 @@ jobs:
|
|
|
7
12
|
configure-pages-and-homepage:
|
|
8
13
|
runs-on: ubuntu-latest
|
|
9
14
|
permissions:
|
|
10
|
-
contents: read
|
|
11
|
-
pages: write # To configure Pages settings
|
|
15
|
+
contents: read
|
|
12
16
|
steps:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-H "Authorization: token ${{ secrets.MY_PAT }}" \
|
|
18
|
-
-H "Accept: application/vnd.github+json" \
|
|
19
|
-
https://api.github.com/repos/${{ github.repository }}/branches/gh-pages)
|
|
20
|
-
if [ "$RESPONSE" -eq 200 ]; then
|
|
21
|
-
echo "gh-pages branch exists"
|
|
22
|
-
echo "BRANCH_EXISTS=true" >> $GITHUB_ENV
|
|
23
|
-
elif [ "$RESPONSE" -eq 404 ]; then
|
|
24
|
-
echo "Error: gh-pages branch does not exist in ${{ github.repository }}"
|
|
25
|
-
exit 1
|
|
26
|
-
else
|
|
27
|
-
echo "Unexpected response code: $RESPONSE"
|
|
28
|
-
exit 1
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
- name: Set GitHub Pages to gh-pages
|
|
32
|
-
if: env.BRANCH_EXISTS == 'true'
|
|
17
|
+
# Configure Pages to serve from the gh-pages branch (root /).
|
|
18
|
+
# POST creates the resource; a 409 means it already exists, so we PUT
|
|
19
|
+
# to update the source to the gh-pages branch.
|
|
20
|
+
- name: Enable GitHub Pages (gh-pages branch source)
|
|
33
21
|
env:
|
|
34
22
|
PAT: ${{ secrets.MY_PAT }}
|
|
35
23
|
run: |
|
|
36
|
-
|
|
24
|
+
HTTP_CODE=$(curl -s -L \
|
|
37
25
|
-X POST \
|
|
38
26
|
-H "Authorization: token $PAT" \
|
|
39
27
|
-H "Accept: application/vnd.github+json" \
|
|
40
28
|
https://api.github.com/repos/${{ github.repository }}/pages \
|
|
41
29
|
-d '{"source":{"branch":"gh-pages","path":"/"}}' \
|
|
42
|
-
-w "%{http_code}" -o response.json)
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
-w "%{http_code}" -o response.json | tail -n1)
|
|
31
|
+
|
|
32
|
+
if [ "$HTTP_CODE" -eq 201 ] || [ "$HTTP_CODE" -eq 200 ]; then
|
|
33
|
+
echo "GitHub Pages enabled with gh-pages branch as source"
|
|
34
|
+
elif [ "$HTTP_CODE" -eq 409 ]; then
|
|
35
|
+
echo "Pages already exists — updating source to gh-pages branch..."
|
|
36
|
+
HTTP_CODE2=$(curl -s -L \
|
|
37
|
+
-X PUT \
|
|
38
|
+
-H "Authorization: token $PAT" \
|
|
39
|
+
-H "Accept: application/vnd.github+json" \
|
|
40
|
+
https://api.github.com/repos/${{ github.repository }}/pages \
|
|
41
|
+
-d '{"source":{"branch":"gh-pages","path":"/"}}' \
|
|
42
|
+
-w "%{http_code}" -o response2.json | tail -n1)
|
|
43
|
+
if [ "$HTTP_CODE2" -eq 200 ] || [ "$HTTP_CODE2" -eq 204 ]; then
|
|
44
|
+
echo "GitHub Pages source updated to gh-pages branch"
|
|
45
|
+
else
|
|
46
|
+
echo "Failed to update Pages: $HTTP_CODE2"
|
|
47
|
+
cat response2.json
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
45
50
|
else
|
|
46
|
-
echo "Failed to
|
|
51
|
+
echo "Failed to configure Pages: $HTTP_CODE"
|
|
47
52
|
cat response.json
|
|
48
53
|
exit 1
|
|
49
54
|
fi
|
|
50
55
|
|
|
56
|
+
# Set the repository homepage field to the expected GitHub Pages URL so
|
|
57
|
+
# it appears prominently on the repository landing page.
|
|
51
58
|
- name: Set repository homepage to GitHub Pages URL
|
|
52
|
-
if: env.BRANCH_EXISTS == 'true'
|
|
53
59
|
env:
|
|
54
60
|
PAT: ${{ secrets.MY_PAT }}
|
|
55
61
|
run: |
|
|
56
|
-
# Construct the expected GitHub Pages URL
|
|
57
62
|
REPO_NAME=$(echo "${{ github.repository }}" | cut -d'/' -f2)
|
|
58
63
|
OWNER=$(echo "${{ github.repository }}" | cut -d'/' -f1)
|
|
59
64
|
PAGES_URL="https://${OWNER}.github.io/${REPO_NAME}"
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
RESPONSE=$(curl -s -L \
|
|
66
|
+
HTTP_CODE=$(curl -s -L \
|
|
63
67
|
-X PATCH \
|
|
64
68
|
-H "Authorization: token $PAT" \
|
|
65
69
|
-H "Accept: application/vnd.github+json" \
|
|
66
70
|
https://api.github.com/repos/${{ github.repository }} \
|
|
67
71
|
-d "{\"homepage\":\"${PAGES_URL}\"}" \
|
|
68
|
-
-w "%{http_code}" -o response.json)
|
|
72
|
+
-w "%{http_code}" -o response.json | tail -n1)
|
|
69
73
|
|
|
70
|
-
if [ "$
|
|
71
|
-
echo "Repository homepage set to $PAGES_URL
|
|
74
|
+
if [ "$HTTP_CODE" -eq 200 ]; then
|
|
75
|
+
echo "Repository homepage set to $PAGES_URL"
|
|
72
76
|
else
|
|
73
|
-
echo "Failed to set homepage: $
|
|
77
|
+
echo "Failed to set homepage: $HTTP_CODE"
|
|
74
78
|
cat response.json
|
|
75
79
|
exit 1
|
|
76
80
|
fi
|
|
77
81
|
|
|
82
|
+
# Verify the homepage was written correctly.
|
|
78
83
|
- name: Verify homepage matches GitHub Pages URL
|
|
79
|
-
if: env.BRANCH_EXISTS == 'true'
|
|
80
84
|
env:
|
|
81
85
|
PAT: ${{ secrets.MY_PAT }}
|
|
82
86
|
run: |
|
|
83
|
-
# Construct the expected GitHub Pages URL
|
|
84
87
|
REPO_NAME=$(echo "${{ github.repository }}" | cut -d'/' -f2)
|
|
85
88
|
OWNER=$(echo "${{ github.repository }}" | cut -d'/' -f1)
|
|
86
89
|
EXPECTED_URL="https://${OWNER}.github.io/${REPO_NAME}"
|
|
87
90
|
|
|
88
|
-
# Fetch the current homepage from the repo
|
|
89
91
|
CURRENT_HOMEPAGE=$(curl -s -L \
|
|
90
92
|
-H "Authorization: token $PAT" \
|
|
91
93
|
-H "Accept: application/vnd.github+json" \
|
|
92
94
|
https://api.github.com/repos/${{ github.repository }} | jq -r '.homepage')
|
|
93
95
|
|
|
94
96
|
if [ "$CURRENT_HOMEPAGE" = "$EXPECTED_URL" ]; then
|
|
95
|
-
echo "Verified:
|
|
97
|
+
echo "Verified: homepage is $EXPECTED_URL"
|
|
96
98
|
else
|
|
97
|
-
echo "Error:
|
|
99
|
+
echo "Error: homepage ($CURRENT_HOMEPAGE) does not match $EXPECTED_URL"
|
|
98
100
|
exit 1
|
|
99
101
|
fi
|
|
@@ -1,11 +1,61 @@
|
|
|
1
|
+
#######################
|
|
2
|
+
#
|
|
3
|
+
# BEGIN SPEC-UP-T rules
|
|
4
|
+
#
|
|
5
|
+
#######################
|
|
6
|
+
|
|
7
|
+
# This file represents the recommended .gitignore for Spec-Up-T projects.
|
|
8
|
+
# If you only need to ignore something locally and don’t want to
|
|
9
|
+
# commit the rule, you can add the pattern to your repository’s
|
|
10
|
+
# `.git/info/exclude` file instead of modifying the shared .gitignore.
|
|
11
|
+
# Entries in `.git/info/exclude` stay completely local.
|
|
12
|
+
|
|
13
|
+
# Generated by render-and-deploy.yml — do not commit build output
|
|
14
|
+
docs/
|
|
15
|
+
|
|
16
|
+
# Dependencies
|
|
1
17
|
node_modules/
|
|
18
|
+
|
|
19
|
+
# Logs
|
|
2
20
|
*.log
|
|
3
|
-
|
|
21
|
+
|
|
22
|
+
# Editor / OS temporaries & backups
|
|
4
23
|
*.bak
|
|
5
24
|
*.tmp
|
|
6
|
-
.
|
|
7
|
-
.
|
|
8
|
-
|
|
9
|
-
|
|
25
|
+
.idea
|
|
26
|
+
.vscode/
|
|
27
|
+
|
|
28
|
+
# Environment / secrets
|
|
29
|
+
.env*
|
|
30
|
+
|
|
31
|
+
# Test coverage
|
|
32
|
+
coverage/
|
|
33
|
+
|
|
34
|
+
# Various caches & history
|
|
10
35
|
.history/
|
|
11
|
-
|
|
36
|
+
.cache/
|
|
37
|
+
|
|
38
|
+
# macOS
|
|
39
|
+
.DS_Store
|
|
40
|
+
.AppleDouble
|
|
41
|
+
.LSOverride
|
|
42
|
+
Icon\r # (the \r is intentional — it's a hidden carriage-return file)
|
|
43
|
+
|
|
44
|
+
# Windows
|
|
45
|
+
Thumbs.db
|
|
46
|
+
ehthumbs.db
|
|
47
|
+
Desktop.ini
|
|
48
|
+
$RECYCLE.BIN/
|
|
49
|
+
*.lnk # Windows shortcuts (optional, but very common noise)
|
|
50
|
+
|
|
51
|
+
# Linux / Ubuntu / general Unix-like
|
|
52
|
+
*~ # Editor backup files (Vim, Nano, etc.)
|
|
53
|
+
.*.swp # Vim swap files
|
|
54
|
+
.*.swo
|
|
55
|
+
.fuse_hidden* # Sometimes appears with fuse mounts
|
|
56
|
+
|
|
57
|
+
#######################
|
|
58
|
+
#
|
|
59
|
+
# END SPEC-UP-T rules
|
|
60
|
+
#
|
|
61
|
+
#######################
|
|
@@ -29,6 +29,11 @@ function createVersionsIndex(outputPath) {
|
|
|
29
29
|
// Get all directories in the destination directory
|
|
30
30
|
const dirs = fs.readdirSync(versionsDir).filter(file => fs.statSync(path.join(versionsDir, file)).isDirectory());
|
|
31
31
|
|
|
32
|
+
// Load persisted labels written by freeze-spec-data.js.
|
|
33
|
+
// Falls back to an empty object when no labels file exists yet.
|
|
34
|
+
const labelsFile = path.join(versionsDir, 'labels.json');
|
|
35
|
+
const labels = fs.existsSync(labelsFile) ? fs.readJsonSync(labelsFile) : {};
|
|
36
|
+
|
|
32
37
|
// Generate HTML content
|
|
33
38
|
let htmlContent = `
|
|
34
39
|
<!DOCTYPE html>
|
|
@@ -58,7 +63,9 @@ function createVersionsIndex(outputPath) {
|
|
|
58
63
|
htmlContent += ` <li class="list-group-item">No versions available</li>\n`;
|
|
59
64
|
} else {
|
|
60
65
|
dirs.forEach(dir => {
|
|
61
|
-
|
|
66
|
+
// Use the stored label when available; fall back to the directory name
|
|
67
|
+
const linkText = labels[dir] || `Version ${dir}`;
|
|
68
|
+
htmlContent += ` <li class="list-group-item"><a href="${dir}/">${linkText}</a></li>\n`;
|
|
62
69
|
});
|
|
63
70
|
}
|
|
64
71
|
|