@pixpilot/scaffoldfy-configs 0.41.0 → 0.43.0
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/CHANGELOG.md +13 -0
- package/README.md +10 -0
- package/github-action-release-app-token/release.yml +50 -0
- package/github-action-release-app-token/scaffoldfy.json +31 -0
- package/package.json +4 -4
- package/test/create-packages.test.ts +101 -0
- package/workspace-initializer/scaffoldfy.json +23 -6
- package/workspace-initializer/scripts/create-packages.cjs +46 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @pixpilot/scaffoldfy-configs
|
|
2
2
|
|
|
3
|
+
## 0.43.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- add GitHub App token release workflow
|
|
8
|
+
|
|
9
|
+
## 0.42.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- add `--set <key=value>` option for preset prompt answers
|
|
14
|
+
- 0319f19: workspace-initializer: create multiple packages at once from a comma separated list of names
|
|
15
|
+
|
|
3
16
|
## 0.41.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -24,6 +24,16 @@ Usage:
|
|
|
24
24
|
npx @pixpilot/scaffoldfy@latest --config https://unpkg.com/@pixpilot/scaffoldfy-configs@latest/codeql-private-repo/scaffoldfy.json
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
### github-action-release-app-token
|
|
28
|
+
|
|
29
|
+
Replaces the release workflow of a github-action-template clone with the GitHub App token variant (RELEASER_ID / RELEASER_PRIVATE_KEY) for pixpilot and ccpu repositories
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx @pixpilot/scaffoldfy@latest --config https://unpkg.com/@pixpilot/scaffoldfy-configs@latest/github-action-release-app-token/scaffoldfy.json
|
|
35
|
+
```
|
|
36
|
+
|
|
27
37
|
### license-file
|
|
28
38
|
|
|
29
39
|
Generate a LICENSE file with common open-source licenses
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_run:
|
|
5
|
+
workflows: [CI]
|
|
6
|
+
types:
|
|
7
|
+
- completed
|
|
8
|
+
branches:
|
|
9
|
+
- main
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
release:
|
|
16
|
+
if: >-
|
|
17
|
+
${{
|
|
18
|
+
github.event.workflow_run.conclusion == 'success' &&
|
|
19
|
+
github.event.workflow_run.event == 'push' &&
|
|
20
|
+
github.event.workflow_run.head_branch == 'main' &&
|
|
21
|
+
!contains(github.event.workflow_run.head_commit.message || '', '[skip ci]') &&
|
|
22
|
+
!contains(github.event.workflow_run.head_commit.message || '', '[ci skip]')
|
|
23
|
+
}}
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- name: Generate token
|
|
27
|
+
id: generate-token
|
|
28
|
+
uses: actions/create-github-app-token@v3
|
|
29
|
+
with:
|
|
30
|
+
client-id: ${{ secrets.RELEASER_ID }}
|
|
31
|
+
private-key: ${{ secrets.RELEASER_PRIVATE_KEY }}
|
|
32
|
+
|
|
33
|
+
- name: Checkout
|
|
34
|
+
uses: actions/checkout@v4
|
|
35
|
+
with:
|
|
36
|
+
fetch-depth: 0
|
|
37
|
+
persist-credentials: false
|
|
38
|
+
ref: ${{ github.event.workflow_run.head_sha }}
|
|
39
|
+
token: ${{ steps.generate-token.outputs.token }}
|
|
40
|
+
|
|
41
|
+
- name: Setup Project
|
|
42
|
+
uses: pixpilot/github-actions/setup-pnpm-project@v1
|
|
43
|
+
|
|
44
|
+
- name: Build dist
|
|
45
|
+
run: npm run bundle
|
|
46
|
+
|
|
47
|
+
- name: Semantic Release
|
|
48
|
+
uses: cycjimmy/semantic-release-action@v4
|
|
49
|
+
env:
|
|
50
|
+
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../node_modules/@pixpilot/scaffoldfy/schema/scaffoldfy.schema.json",
|
|
3
|
+
"name": "github-action-release-app-token",
|
|
4
|
+
"description": "Replaces the release workflow of a github-action-template clone with the GitHub App token variant (RELEASER_ID / RELEASER_PRIVATE_KEY) for pixpilot and ccpu repositories",
|
|
5
|
+
"dependencies": ["pixpilot-info"],
|
|
6
|
+
"variables": [
|
|
7
|
+
{
|
|
8
|
+
"id": "isGithubActionTemplate",
|
|
9
|
+
"value": {
|
|
10
|
+
"type": "exec",
|
|
11
|
+
"value": "node -e \"try{process.stdout.write(String(require(require('path').resolve('package.json')).name==='github-action-template'))}catch{process.stdout.write('false')}\""
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"tasks": [
|
|
16
|
+
{
|
|
17
|
+
"id": "github-action-release-app-token-workflow",
|
|
18
|
+
"name": "Use GitHub App token in release workflow",
|
|
19
|
+
"description": "Overwrites the release workflow to authenticate semantic-release with a GitHub App token",
|
|
20
|
+
"enabled": {
|
|
21
|
+
"type": "condition",
|
|
22
|
+
"value": "isGithubActionTemplate === true && pixpilot_project === true"
|
|
23
|
+
},
|
|
24
|
+
"type": "write",
|
|
25
|
+
"config": {
|
|
26
|
+
"templateFile": "release.yml",
|
|
27
|
+
"file": ".github/workflows/release.yml"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixpilot/scaffoldfy-configs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.43.0",
|
|
5
5
|
"author": "PixPilot <m.doaie@hotmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"@types/node": "^22.18.11",
|
|
31
31
|
"eslint": "^9.38.0",
|
|
32
32
|
"@internal/eslint-config": "0.3.0",
|
|
33
|
-
"@internal/
|
|
33
|
+
"@internal/vitest-config": "0.1.0",
|
|
34
34
|
"@internal/tsdown-config": "0.1.0",
|
|
35
|
-
"@pixpilot/scaffoldfy": "0.
|
|
36
|
-
"@internal/
|
|
35
|
+
"@pixpilot/scaffoldfy": "0.56.0",
|
|
36
|
+
"@internal/prettier-config": "0.0.1"
|
|
37
37
|
},
|
|
38
38
|
"prettier": "@internal/prettier-config",
|
|
39
39
|
"publishConfig": {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
7
|
+
|
|
8
|
+
const SCRIPT_PATH = path.join(
|
|
9
|
+
__dirname,
|
|
10
|
+
'..',
|
|
11
|
+
'workspace-initializer',
|
|
12
|
+
'scripts',
|
|
13
|
+
'create-packages.cjs',
|
|
14
|
+
);
|
|
15
|
+
const temporaryDirectories: string[] = [];
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
for (const directory of temporaryDirectories.splice(0)) {
|
|
19
|
+
fs.rmSync(directory, { force: true, recursive: true });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Put a fake `pnpm` on PATH that appends its arguments to a log file
|
|
25
|
+
*/
|
|
26
|
+
function createFakePnpm(): { binDirectory: string; logPath: string } {
|
|
27
|
+
const testDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'create-packages-'));
|
|
28
|
+
temporaryDirectories.push(testDirectory);
|
|
29
|
+
|
|
30
|
+
const binDirectory = path.join(testDirectory, 'bin');
|
|
31
|
+
const logPath = path.join(testDirectory, 'calls.log');
|
|
32
|
+
const fakePnpmScript = path.join(binDirectory, 'fake-pnpm.cjs');
|
|
33
|
+
fs.mkdirSync(binDirectory, { recursive: true });
|
|
34
|
+
fs.writeFileSync(
|
|
35
|
+
fakePnpmScript,
|
|
36
|
+
`require('node:fs').appendFileSync(${JSON.stringify(logPath)}, process.argv.slice(2).join(' ') + '\\n');`,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (process.platform === 'win32') {
|
|
40
|
+
fs.writeFileSync(
|
|
41
|
+
path.join(binDirectory, 'pnpm.cmd'),
|
|
42
|
+
`@"${process.execPath}" "${fakePnpmScript}" %*\r\n`,
|
|
43
|
+
);
|
|
44
|
+
} else {
|
|
45
|
+
const pnpmPath = path.join(binDirectory, 'pnpm');
|
|
46
|
+
fs.writeFileSync(
|
|
47
|
+
pnpmPath,
|
|
48
|
+
`#!/bin/sh\n"${process.execPath}" "${fakePnpmScript}" "$@"\n`,
|
|
49
|
+
);
|
|
50
|
+
fs.chmodSync(pnpmPath, 0o755);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { binDirectory, logPath };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function runScript(packageNames: string, binDirectory: string) {
|
|
57
|
+
const pathKey =
|
|
58
|
+
Object.keys(process.env).find((key) => key.toUpperCase() === 'PATH') ?? 'PATH';
|
|
59
|
+
return spawnSync(process.execPath, [SCRIPT_PATH], {
|
|
60
|
+
encoding: 'utf8',
|
|
61
|
+
env: {
|
|
62
|
+
...process.env,
|
|
63
|
+
[pathKey]: `${binDirectory}${path.delimiter}${process.env[pathKey] ?? ''}`,
|
|
64
|
+
PACKAGE_NAMES: packageNames,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe('workspace-initializer create-packages script', () => {
|
|
70
|
+
it('runs the package generator once per comma separated name', () => {
|
|
71
|
+
const { binDirectory, logPath } = createFakePnpm();
|
|
72
|
+
|
|
73
|
+
const result = runScript(' pkg-a, pkg-b ,,@scope/pkg-c ', binDirectory);
|
|
74
|
+
|
|
75
|
+
expect(result.status).toBe(0);
|
|
76
|
+
expect(fs.readFileSync(logPath, 'utf8').trim().split(/\r?\n/u)).toEqual([
|
|
77
|
+
'run gen:package --set packageBaseName=pkg-a',
|
|
78
|
+
'run gen:package --set packageBaseName=pkg-b',
|
|
79
|
+
'run gen:package --set packageBaseName=@scope/pkg-c',
|
|
80
|
+
]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('skips when no names are given', () => {
|
|
84
|
+
const { binDirectory, logPath } = createFakePnpm();
|
|
85
|
+
|
|
86
|
+
const result = runScript(' , ', binDirectory);
|
|
87
|
+
|
|
88
|
+
expect(result.status).toBe(0);
|
|
89
|
+
expect(fs.existsSync(logPath)).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('rejects names with shell characters', () => {
|
|
93
|
+
const { binDirectory, logPath } = createFakePnpm();
|
|
94
|
+
|
|
95
|
+
const result = runScript('pkg-a, bad;rm -rf', binDirectory);
|
|
96
|
+
|
|
97
|
+
expect(result.status).toBe(1);
|
|
98
|
+
expect(result.stderr).toContain('Invalid package name(s): bad;rm -rf');
|
|
99
|
+
expect(fs.existsSync(logPath)).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -22,8 +22,18 @@
|
|
|
22
22
|
{
|
|
23
23
|
"id": "createFirstPackagePrompt",
|
|
24
24
|
"type": "confirm",
|
|
25
|
-
"message": "Create
|
|
25
|
+
"message": "Create packages now?",
|
|
26
26
|
"default": false
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "packageNames",
|
|
30
|
+
"type": "input",
|
|
31
|
+
"message": "Enter package names (comma separated, e.g. my-package, my-other-package)",
|
|
32
|
+
"required": true,
|
|
33
|
+
"enabled": {
|
|
34
|
+
"type": "condition",
|
|
35
|
+
"value": "createFirstPackagePrompt === true"
|
|
36
|
+
}
|
|
27
37
|
}
|
|
28
38
|
],
|
|
29
39
|
"tasks": [
|
|
@@ -88,12 +98,19 @@
|
|
|
88
98
|
},
|
|
89
99
|
{
|
|
90
100
|
"id": "create-first-package",
|
|
91
|
-
"name": "Create
|
|
92
|
-
"description": "Generate
|
|
93
|
-
"type": "exec",
|
|
101
|
+
"name": "Create packages",
|
|
102
|
+
"description": "Generate each requested package in the monorepo using the package generator",
|
|
103
|
+
"type": "exec-file",
|
|
104
|
+
"enabled": {
|
|
105
|
+
"type": "condition",
|
|
106
|
+
"value": "createFirstPackagePrompt === true"
|
|
107
|
+
},
|
|
94
108
|
"config": {
|
|
95
|
-
"
|
|
96
|
-
"
|
|
109
|
+
"file": "./scripts/create-packages.cjs",
|
|
110
|
+
"runtime": "node",
|
|
111
|
+
"parameters": {
|
|
112
|
+
"PACKAGE_NAMES": "{{packageNames}}"
|
|
113
|
+
}
|
|
97
114
|
}
|
|
98
115
|
},
|
|
99
116
|
{
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable node/prefer-global/process */
|
|
3
|
+
/**
|
|
4
|
+
* Runs the workspace package generator once per package name, pre-filling
|
|
5
|
+
* the package name prompt so only the remaining prompts are asked.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* PACKAGE_NAMES="pkg-a, pkg-b" node create-packages.cjs
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const { spawnSync } = require('node:child_process');
|
|
14
|
+
|
|
15
|
+
const SAFE_NAME = /^[\w.@/-]+$/u;
|
|
16
|
+
|
|
17
|
+
const names = (process.env.PACKAGE_NAMES || '')
|
|
18
|
+
.split(',')
|
|
19
|
+
.map((name) => name.trim())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
|
|
22
|
+
if (names.length === 0) {
|
|
23
|
+
console.log('No package names provided, skipping package creation.');
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const invalid = names.filter((name) => !SAFE_NAME.test(name));
|
|
28
|
+
if (invalid.length > 0) {
|
|
29
|
+
console.error(`Invalid package name(s): ${invalid.join(', ')}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const [index, name] of names.entries()) {
|
|
34
|
+
console.log(`\nCreating package ${index + 1}/${names.length}: ${name}\n`);
|
|
35
|
+
|
|
36
|
+
const result = spawnSync(
|
|
37
|
+
'pnpm',
|
|
38
|
+
['run', 'gen:package', '--set', `packageBaseName=${name}`],
|
|
39
|
+
{ stdio: 'inherit', shell: true },
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (result.status !== 0) {
|
|
43
|
+
console.error(`Failed to create package: ${name}`);
|
|
44
|
+
process.exit(result.status ?? 1);
|
|
45
|
+
}
|
|
46
|
+
}
|