spec-up-t 1.1.14 → 1.1.16

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.
@@ -0,0 +1,67 @@
1
+ name: Create Spec-Up-T Project via Artifact
2
+
3
+ # Trigger the workflow manually with an input for the project name
4
+ on:
5
+ workflow_dispatch:
6
+ inputs:
7
+ project-name:
8
+ description: 'Name of the new project directory'
9
+ required: true
10
+ user-pat:
11
+ description: 'Your GitHub PAT (optional, to push to your own repo)'
12
+ required: false
13
+
14
+ jobs:
15
+ create-project:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ # Step 1: Check out the spec-up-t repo
19
+ - name: Checkout spec-up-t repository
20
+ uses: actions/checkout@v4
21
+
22
+ # Step 2: Set up Node.js environment
23
+ - name: Set up Node.js
24
+ uses: actions/setup-node@v4
25
+ with:
26
+ node-version: '18' # Adjust to your required Node version
27
+
28
+ # Step 3: Install npx and the spec-up-t-starter-pack package
29
+ - name: Install dependencies and npx package
30
+ run: |
31
+ npm install -g create-spec-up-t
32
+
33
+ # Step 4: Run the npx command with the provided project name
34
+ - name: Create new project from boilerplate
35
+ run: |
36
+ npx create-spec-up-t ${{ github.event.inputs.project-name }}
37
+
38
+ # Step 5: List the generated project files (for verification)
39
+ - name: Verify generated project
40
+ run: |
41
+ ls -la ${{ github.event.inputs.project-name }}
42
+
43
+ # Step 6: Upload the generated project as an artifact
44
+ - name: Upload project as artifact
45
+ uses: actions/upload-artifact@v4
46
+ with:
47
+ name: ${{ github.event.inputs.project-name }}
48
+ path: ${{ github.event.inputs.project-name }}
49
+
50
+
51
+ - name: Push to new repository (if PAT provided)
52
+ if: github.event.inputs.user-pat != ''
53
+ env:
54
+ USER_PAT: ${{ github.event.inputs.user-pat }}
55
+ run: |
56
+ cd ${{ github.event.inputs.project-name }}
57
+ git init
58
+ git add .
59
+ git commit -m "Initial project's Spec-Up-T installation"
60
+ git remote add origin https://x-access-token:${{ env.USER_PAT }}@github.com/<username-from-user-or-dynamic>/${{ github.event.inputs.project-name }}.git
61
+ git push -u origin main
62
+ - name: Upload artifact (if no PAT)
63
+ if: github.event.inputs.user-pat == ''
64
+ uses: actions/upload-artifact@v4
65
+ with:
66
+ name: ${{ github.event.inputs.project-name }}
67
+ path: ${{ github.event.inputs.project-name }}
@@ -0,0 +1,140 @@
1
+ name: Create Spec-Up-T Project
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ project-name:
7
+ description: 'Name of the new project repository'
8
+ required: true
9
+ default: 'myproject'
10
+ username:
11
+ description: 'Your GitHub username (required if PAT is provided)'
12
+ required: false
13
+ user-pat:
14
+ description: 'Your GitHub PAT (optional, to push to your own repo)'
15
+ required: false
16
+
17
+ jobs:
18
+ create-project:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - name: Checkout spec-up-t repository
22
+ uses: actions/checkout@v4
23
+
24
+ - name: Set up Node.js
25
+ uses: actions/setup-node@v4
26
+ with:
27
+ node-version: '18'
28
+
29
+ - name: Install create-spec-up-t
30
+ run: |
31
+ npm install -g create-spec-up-t
32
+
33
+ - name: Create new project from boilerplate
34
+ run: |
35
+ npx create-spec-up-t ${{ github.event.inputs.project-name }}
36
+
37
+ - name: Debug inputs and PAT
38
+ if: github.event.inputs.user-pat != ''
39
+ env:
40
+ USER_PAT: ${{ github.event.inputs.user-pat }}
41
+ run: |
42
+ echo "Username: ${{ github.event.inputs.username }}"
43
+ echo "Project Name: ${{ github.event.inputs.project-name }}"
44
+ curl -i -L -H "Authorization: token $USER_PAT" -H "Accept: application/vnd.github+json" https://api.github.com/user
45
+
46
+ - name: Create new repository
47
+ if: github.event.inputs.user-pat != ''
48
+ env:
49
+ USER_PAT: ${{ github.event.inputs.user-pat }}
50
+ run: |
51
+ curl -i -L \
52
+ -X POST \
53
+ -H "Authorization: token $USER_PAT" \
54
+ -H "Accept: application/vnd.github+json" \
55
+ https://api.github.com/user/repos \
56
+ -d '{"name":"${{ github.event.inputs.project-name }}","description":"Spec-Up-T project created via workflow","private":false}'
57
+
58
+ - name: Get repo public key
59
+ if: github.event.inputs.user-pat != ''
60
+ id: get-public-key
61
+ env:
62
+ USER_PAT: ${{ github.event.inputs.user-pat }}
63
+ run: |
64
+ RESPONSE=$(curl -L -H "Authorization: token $USER_PAT" -H "Accept: application/vnd.github+json" https://api.github.com/repos/${{ github.event.inputs.username }}/${{ github.event.inputs.project-name }}/actions/secrets/public-key)
65
+ echo "PUBLIC_KEY=$(echo $RESPONSE | jq -r .key)" >> $GITHUB_OUTPUT
66
+ echo "KEY_ID=$(echo $RESPONSE | jq -r .key_id)" >> $GITHUB_OUTPUT
67
+
68
+ # Install libsodium-wrappers locally
69
+ - name: Install libsodium-wrappers
70
+ if: github.event.inputs.user-pat != ''
71
+ run: |
72
+ npm install libsodium-wrappers
73
+
74
+ # Encrypt and set the PAT as a secret
75
+ - name: Set PAT as repo secret
76
+ if: github.event.inputs.user-pat != ''
77
+ env:
78
+ USER_PAT: ${{ github.event.inputs.user-pat }}
79
+ run: |
80
+ node -e "
81
+ const sodium = require('./node_modules/libsodium-wrappers');
82
+ (async () => {
83
+ await sodium.ready;
84
+ const publicKey = Buffer.from('${{ steps.get-public-key.outputs.PUBLIC_KEY }}', 'base64');
85
+ const secretValue = process.env.USER_PAT;
86
+ const messageBytes = Buffer.from(secretValue);
87
+ const encryptedBytes = sodium.crypto_box_seal(messageBytes, publicKey);
88
+ const encrypted = Buffer.from(encryptedBytes).toString('base64');
89
+ const keyId = '${{ steps.get-public-key.outputs.KEY_ID }}';
90
+
91
+ const response = await fetch('https://api.github.com/repos/${{ github.event.inputs.username }}/${{ github.event.inputs.project-name }}/actions/secrets/MY_PAT', {
92
+ method: 'PUT',
93
+ headers: {
94
+ 'Authorization': 'token ' + secretValue,
95
+ 'Accept': 'application/vnd.github+json'
96
+ },
97
+ body: JSON.stringify({
98
+ encrypted_value: encrypted,
99
+ key_id: keyId
100
+ })
101
+ });
102
+ if (response.ok) {
103
+ console.log('PAT stored as secret MY_PAT in the new repo');
104
+ } else {
105
+ console.error('Failed to set secret:', await response.text());
106
+ process.exit(1);
107
+ }
108
+ })();
109
+ "
110
+
111
+ - name: Configure Git identity
112
+ if: github.event.inputs.user-pat != ''
113
+ run: |
114
+ git config --global user.email "actions@github.com"
115
+ git config --global user.name "GitHub Actions"
116
+
117
+ - name: Push to new repository
118
+ if: github.event.inputs.user-pat != ''
119
+ env:
120
+ USER_PAT: ${{ github.event.inputs.user-pat }}
121
+ run: |
122
+ cd ${{ github.event.inputs.project-name }}
123
+ git init
124
+ git add .
125
+ git commit -m "Initial project's Spec-Up-T installation"
126
+ git branch -M main
127
+ git remote add origin https://x-access-token:${{ env.USER_PAT }}@github.com/${{ github.event.inputs.username }}/${{ github.event.inputs.project-name }}.git
128
+ git push -u origin main
129
+
130
+ - name: Output repository URL
131
+ if: github.event.inputs.user-pat != ''
132
+ run: |
133
+ echo "Your new Spec-Up-T project is at: https://github.com/${{ github.event.inputs.username }}/${{ github.event.inputs.project-name }}"
134
+
135
+ - name: Upload artifact (if no PAT)
136
+ if: github.event.inputs.user-pat == ''
137
+ uses: actions/upload-artifact@v4
138
+ with:
139
+ name: ${{ github.event.inputs.project-name }}
140
+ path: ${{ github.event.inputs.project-name }}
package/index.js CHANGED
@@ -252,77 +252,91 @@ module.exports = async function (options = {}) {
252
252
  return template.replace(/\${(.*?)}/g, (match, p1) => variables[p1.trim()]);
253
253
  }
254
254
 
255
- return new Promise(async (resolve, reject) => {
256
- Promise.all((spec.markdown_paths || ['spec.md']).map(_path => {
257
- return fs.readFile(spec.spec_directory + _path, 'utf8').catch(e => reject(e))
258
- })).then(async docs => {
259
- const features = (({ source, logo }) => ({ source, logo }))(spec);
260
- if (spec.external_specs && !externalReferences) {
261
- externalReferences = await fetchExternalSpecs(spec);
262
- }
255
+ const docs = await Promise.all(
256
+ (spec.markdown_paths || ['spec.md']).map(_path =>
257
+ fs.readFile(spec.spec_directory + _path, 'utf8')
258
+ )
259
+ );
260
+
261
+ const features = (({ source, logo }) => ({ source, logo }))(spec);
262
+ if (spec.external_specs && !externalReferences) {
263
+ externalReferences = await fetchExternalSpecs(spec);
264
+ }
263
265
 
264
266
  // Find the index of the terms-and-definitions-intro.md file
265
- const termsIndex = (spec.markdown_paths || ['spec.md']).indexOf('terms-and-definitions-intro.md');
266
- if (termsIndex !== -1) {
267
+ const termsIndex = (spec.markdown_paths || ['spec.md']).indexOf('terms-and-definitions-intro.md');
268
+ if (termsIndex !== -1) {
267
269
  // Append the HTML string to the content of terms-and-definitions-intro.md. This string is used to create a div that is used to insert an alphabet index, and a div that is used as the starting point of the terminology index. The newlines are essential for the correct rendering of the markdown.
268
- docs[termsIndex] += '\n\n<div id="terminology-section-utility-container"></div>\n\n<div id="terminology-section-start-h7vc6omi2hr2880"></div>\n\n<hr>\n\n';
269
- }
270
+ docs[termsIndex] += '\n\n<div id="terminology-section-utility-container"></div>\n\n<div id="terminology-section-start-h7vc6omi2hr2880"></div>\n\n<hr>\n\n';
271
+ }
270
272
 
271
- let doc = docs.join("\n");
273
+ let doc = docs.join("\n");
272
274
 
273
275
  // `doc` is markdown
274
- doc = applyReplacers(doc);
276
+ doc = applyReplacers(doc);
275
277
 
276
- md[spec.katex ? "enable" : "disable"](katexRules);
278
+ md[spec.katex ? "enable" : "disable"](katexRules);
277
279
 
278
280
  // `render` is the rendered HTML
279
- const render = md.render(doc);
280
-
281
- const templateInterpolated = interpolate(template, {
282
- title: spec.title,
283
- description: spec.description,
284
- author: spec.author,
285
- toc: toc,
286
- render: render,
287
- assetsHead: assets.head,
288
- assetsBody: assets.body,
289
- assetsSvg: assets.svg,
290
- features: Object.keys(features).join(' '),
291
- externalReferences: JSON.stringify(externalReferences),
292
- xtrefsData: xtrefsData,
293
- specLogo: spec.logo,
294
- specFavicon: spec.favicon,
295
- specLogoLink: spec.logo_link,
296
- spec: JSON.stringify(spec)
297
- });
298
-
299
- fs.writeFile(path.join(spec.destination, 'index.html'),
300
- templateInterpolated, 'utf8'
301
-
302
- , function (err, data) {
303
- if (err) {
304
- reject(err);
305
- }
306
- else {
307
- resolve();
308
- }
309
- });
310
- validateReferences(references, definitions, render);
311
- references = [];
312
- definitions = [];
313
- });
281
+ const render = md.render(doc);
282
+
283
+ const templateInterpolated = interpolate(template, {
284
+ title: spec.title,
285
+ description: spec.description,
286
+ author: spec.author,
287
+ toc: toc,
288
+ render: render,
289
+ assetsHead: assets.head,
290
+ assetsBody: assets.body,
291
+ assetsSvg: assets.svg,
292
+ features: Object.keys(features).join(' '),
293
+ externalReferences: JSON.stringify(externalReferences),
294
+ xtrefsData: xtrefsData,
295
+ specLogo: spec.logo,
296
+ specFavicon: spec.favicon,
297
+ specLogoLink: spec.logo_link,
298
+ spec: JSON.stringify(spec)
314
299
  });
315
- }
316
- catch (e) {
317
- console.error("❌ " + e);
300
+
301
+ const outputPath = path.join(spec.destination, 'index.html');
302
+ console.log('ℹ️ Attempting to write to:', outputPath);
303
+
304
+ // Use promisified version instead of callback
305
+ await fs.promises.writeFile(outputPath, templateInterpolated, 'utf8');
306
+ console.log(`✅ Successfully wrote ${outputPath}`);
307
+
308
+ validateReferences(references, definitions, render);
309
+ references = [];
310
+ definitions = [];
311
+ } catch (e) {
312
+ console.error("❌ Render error: " + e.message);
313
+ throw e;
318
314
  }
319
315
  }
320
316
 
321
317
  config.specs.forEach(spec => {
322
318
  spec.spec_directory = normalizePath(spec.spec_directory);
323
319
  spec.destination = normalizePath(spec.output_path || spec.spec_directory);
320
+
321
+ if (!fs.existsSync(spec.destination)) {
322
+ try {
323
+ fs.mkdirSync(spec.destination, { recursive: true });
324
+ console.log(`✅ Created directory: ${spec.destination}`);
325
+ } catch (error) {
326
+ console.error(`❌ Failed to create directory ${spec.destination}: ${error.message}`);
327
+ throw error;
328
+ }
329
+ } else {
330
+ console.log(`ℹ️ Directory already exists: ${spec.destination}`);
331
+ }
324
332
 
325
- fs.ensureDirSync(spec.destination);
333
+ try {
334
+ fs.ensureDirSync(spec.destination);
335
+ console.log(`✅ Ensured directory is ready: ${spec.destination}`);
336
+ } catch (error) {
337
+ console.error(`❌ Failed to ensure directory ${spec.destination}: ${error.message}`);
338
+ throw error;
339
+ }
326
340
 
327
341
  let assetTags = {
328
342
  svg: fs.readFileSync(modulePath + '/assets/icons.svg', 'utf8') || ''
@@ -371,17 +385,27 @@ module.exports = async function (options = {}) {
371
385
  fs.copySync(path.join(katexDist, 'fonts'), path.join(spec.destination, 'fonts'));
372
386
  }
373
387
 
388
+ // Run render and wait for it
389
+ render(spec, assetTags)
390
+ .then(() => {
391
+ console.log('ℹ️ Render completed for:', spec.destination);
392
+ if (options.nowatch) {
393
+ console.log('ℹ️ Exiting with nowatch');
394
+ process.exit(0);
395
+ }
396
+ })
397
+ .catch((e) => {
398
+ console.error('❌ Render failed:', e.message);
399
+ process.exit(1);
400
+ });
401
+
374
402
  if (!options.nowatch) {
375
403
  gulp.watch(
376
404
  [spec.spec_directory + '**/*', '!' + path.join(spec.destination, 'index.html')],
377
405
  render.bind(null, spec, assetTags)
378
- )
406
+ );
379
407
  }
380
408
 
381
- render(spec, assetTags).then(() => {
382
- if (options.nowatch) process.exit(0)
383
- }).catch(() => process.exit(1));
384
-
385
409
  });
386
410
  } catch (error) {
387
411
  console.error(`Error during initialization or module execution: ${error.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-up-t",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
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": {
@@ -30,7 +30,7 @@
30
30
  "diff": "^7.0.0",
31
31
  "dotenv": "^16.4.7",
32
32
  "find-pkg-dir": "^2.0.0",
33
- "fs-extra": "^11.2.0",
33
+ "fs-extra": "^11.3.0",
34
34
  "gulp": "4.0.2",
35
35
  "gulp-clean-css": "4.3.0",
36
36
  "gulp-concat": "2.6.1",
@@ -17,7 +17,7 @@ function collectExternalReferences(options = {}) {
17
17
  const readlineSync = require('readline-sync');
18
18
  const config = fs.readJsonSync('specs.json');
19
19
  const externalSpecsRepos = config.specs[0].external_specs;
20
- const GITHUB_API_TOKEN = process.env.GITHUB_API_TOKEN;
20
+ const GITHUB_API_TOKEN = options.pat || process.env.GITHUB_API_TOKEN;
21
21
 
22
22
  const explanationPAT =
23
23
  `❌ No GitHub Personal Access Token (PAT) was found.
@@ -119,7 +119,7 @@ function collectExternalReferences(options = {}) {
119
119
  // Function to extend xtref objects with additional information, such as repository URL and directory information.
120
120
  function extendXTrefs(config, xtrefs) {
121
121
  if (config.specs[0].external_specs_repos) {
122
- console.log("ℹ️ PLEASE NOTE: Your specs.json file is outdated (not your fault, we changed something). Use this one: https://github.com/trustoverip/spec-up-t-starter-pack/blob/main/spec-up-t-boilerplate/specs.json or e-mail kor@dwarshuis.com for help.");
122
+ console.log("ℹ️ PLEASE NOTE: Your specs.json file is outdated (not your fault, we changed something). Use this one: https://github.com/trustoverip/spec-up-t/blob/master/src/install-from-boilerplate/boilerplate/specs.json");
123
123
  return;
124
124
  }
125
125
 
package/src/init.js CHANGED
@@ -13,7 +13,6 @@ async function initialize() {
13
13
 
14
14
  // Place the init script here
15
15
 
16
- collectExternalReferences(process.env.GITHUB_API_TOKEN, false);
17
16
  // prepareTref(path.join(config.specs[0].spec_directory, config.specs[0].spec_terms_directory));
18
17
 
19
18
  // End of the init script
@@ -0,0 +1,110 @@
1
+ name: Menu
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ script:
7
+ description: 'Select the script to run'
8
+ type: choice
9
+ required: true
10
+ default: 'render'
11
+ options:
12
+ - edit
13
+ - render
14
+ - dev
15
+ - collectExternalReferencesCache
16
+ - collectExternalReferencesNoCache
17
+ - topdf
18
+ - freeze
19
+ - references
20
+ - help
21
+ - menu
22
+ - addremovexrefsource
23
+ - configure
24
+ - custom-update
25
+
26
+ jobs:
27
+ build-and-deploy-spec:
28
+ runs-on: ubuntu-latest
29
+ permissions:
30
+ contents: write
31
+ steps:
32
+ - name: Checkout 🛎️
33
+ uses: actions/checkout@v2
34
+ with:
35
+ persist-credentials: false
36
+
37
+ - name: Extract output_path from JSON
38
+ # TODO: .specs[0] is a hack to get the first spec. This should be fixed.
39
+ run: |
40
+ OUTPUT_PATH=$(jq -r '.specs[0].output_path' specs.json)
41
+ echo "OUTPUT_PATH=$OUTPUT_PATH" >> $GITHUB_ENV
42
+
43
+ - name: Install and Build 🔧
44
+ run: |
45
+ echo "start install"
46
+ npm install
47
+ echo "end install"
48
+ echo "Spec-Up-T version:"
49
+ npm list spec-up-t
50
+
51
+ - name: Run selected script
52
+ run: |
53
+ case ${{ github.event.inputs.script }} in
54
+ "edit")
55
+ node -e "require('spec-up-t')()"
56
+ ;;
57
+ "render")
58
+ node --no-warnings -e "require('spec-up-t/index.js')({ nowatch: true })"
59
+ ;;
60
+ "dev")
61
+ node -e "require('spec-up-t')({ dev: true })"
62
+ ;;
63
+ "collectExternalReferencesCache")
64
+ node --no-warnings -e "require('spec-up-t/src/collect-external-references.js').collectExternalReferences({cache: true})"
65
+ ;;
66
+ "collectExternalReferencesNoCache")
67
+ node --no-warnings -e "require('spec-up-t/src/collect-external-references.js').collectExternalReferences({cache: false})"
68
+ ;;
69
+ "topdf")
70
+ node -e "require('spec-up-t/src/create-pdf.js')"
71
+ ;;
72
+ "freeze")
73
+ node -e "require('spec-up-t/src/freeze.js')"
74
+ ;;
75
+ "references")
76
+ node -e "require('spec-up-t/src/references.js')"
77
+ ;;
78
+ "help")
79
+ cat ./node_modules/spec-up-t/src/install-from-boilerplate/help.txt
80
+ ;;
81
+ "menu")
82
+ bash ./node_modules/spec-up-t/src/install-from-boilerplate/menu.sh
83
+ ;;
84
+ "addremovexrefsource")
85
+ node --no-warnings -e "require('spec-up-t/src/add-remove-xref-source.js')"
86
+ ;;
87
+ "configure")
88
+ node --no-warnings -e "require('spec-up-t/src/configure.js')"
89
+ ;;
90
+ "custom-update")
91
+ npm update && node -e "require('spec-up-t/src/install-from-boilerplate/custom-update.js')"
92
+ ;;
93
+ *)
94
+ echo "Unknown script selected"
95
+ exit 1
96
+ ;;
97
+ esac
98
+
99
+ - name: Clean up
100
+ run: |
101
+ echo "end render"
102
+ rm -rf node_modules
103
+
104
+ - name: Deploy
105
+ uses: peaceiris/actions-gh-pages@v3.7.3
106
+ with:
107
+ github_token: ${{ secrets.GITHUB_TOKEN }}
108
+ publish_dir: ${{ env.OUTPUT_PATH }}
109
+ allow_empty_commit: true
110
+ force_orphan: true