@voxgig/create-sdkgen 0.28.0 → 0.29.1
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voxgig/create-sdkgen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.1",
|
|
4
4
|
"main": "dist/create-sdkgen.js",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"types": "dist/create-sdkgen.d.ts",
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"dist",
|
|
48
48
|
"bin",
|
|
49
49
|
"project",
|
|
50
|
-
"LICENSE"
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"README.md"
|
|
51
52
|
],
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"@types/node": "26.1.2",
|
|
@@ -38,7 +38,9 @@ test, and the only symptom is a tree that disagrees with the model:
|
|
|
38
38
|
anything whose output might be committed.
|
|
39
39
|
|
|
40
40
|
The script deletes every target tree, regenerates, and reports what differs —
|
|
41
|
-
which is the only way to see output the generator no longer emits.
|
|
41
|
+
which is the only way to see output the generator no longer emits. A target
|
|
42
|
+
generated at the project root (`output: root: true`) owns every tracked file
|
|
43
|
+
outside `.sdk/`, so those are what it deletes for that target. It refuses
|
|
42
44
|
to run on a dirty tree, restores the target's own build artifacts and
|
|
43
45
|
lockfiles (which the generator never writes), and leaves the regenerated tree
|
|
44
46
|
in place so the diff can be read.
|
|
@@ -60,7 +62,29 @@ The setup script configures GitHub Actions as the publishing source. It does
|
|
|
60
62
|
not commit, push, merge, or deploy. Push the documentation workflow and project
|
|
61
63
|
changes to its configured deployment branch to publish the website.
|
|
62
64
|
|
|
65
|
+
When a target publishes to npm, sdkgen generates `setup-npm-trust.sh` here.
|
|
66
|
+
For a repository on `github.com`, it registers each npm publish workflow as
|
|
67
|
+
the trusted publisher of its package, from the same model the workflows are
|
|
68
|
+
generated from, and leaves a package that is already set up alone:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
.sdk/admin/setup-npm-trust.sh --dry-run
|
|
72
|
+
.sdk/admin/setup-npm-trust.sh
|
|
73
|
+
.sdk/admin/setup-npm-trust.sh --check
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`--check` changes nothing and exits non-zero on any difference from what the
|
|
77
|
+
workflows need, including a trusted publisher that no workflow names;
|
|
78
|
+
`--replace` revokes those. It needs an npm login with publish rights. For a
|
|
79
|
+
repository hosted anywhere else, npm cannot trust GitHub Actions, so the script
|
|
80
|
+
says so and exits non-zero.
|
|
81
|
+
|
|
82
|
+
npm offers trusted publishing only for a package that already has a version,
|
|
83
|
+
so a new package's first version is published by hand, as `.sdk/PUBLISHING.md`
|
|
84
|
+
describes. Run the script straight after; every later release goes through the
|
|
85
|
+
generated workflow.
|
|
86
|
+
|
|
63
87
|
The status launcher and the drift check come from create-sdkgen; the status
|
|
64
|
-
reporting code
|
|
65
|
-
script. Put project-specific administration scripts
|
|
66
|
-
different names.
|
|
88
|
+
reporting code and the npm trust script come from sdkgen. Docgen owns the
|
|
89
|
+
generated Pages setup script. Put project-specific administration scripts
|
|
90
|
+
beside them with different names.
|
|
@@ -52,17 +52,34 @@ if [[ -n "$(git status --porcelain)" ]]; then
|
|
|
52
52
|
exit 2
|
|
53
53
|
fi
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
# Each target generated in a folder of its own, and the one, if any, that
|
|
56
|
+
# declares `output: root: true`.
|
|
57
|
+
list_targets() {
|
|
58
|
+
node -e '
|
|
59
|
+
const m = require("./.sdk/model/sdk.json")
|
|
60
|
+
const t = (m.main && m.main.kit && m.main.kit.target) || {}
|
|
61
|
+
const root = "root" === process.argv[1]
|
|
62
|
+
console.log(Object.keys(t).sort().filter((n) =>
|
|
63
|
+
root === (true === (t[n] && t[n].output && t[n].output.root))).join("\n"))
|
|
64
|
+
' "$1"
|
|
65
|
+
}
|
|
66
|
+
targets="$(list_targets folder)"
|
|
67
|
+
atroot="$(list_targets root)"
|
|
60
68
|
|
|
61
69
|
echo 'check-drift: deleting and regenerating every target ...'
|
|
62
70
|
while IFS= read -r t; do
|
|
63
71
|
[[ -n "$t" && -d "$t" ]] && rm -rf -- "$t"
|
|
64
72
|
done <<< "$targets"
|
|
65
73
|
|
|
74
|
+
# A target generated at the root owns the repository outside .sdk/, as the
|
|
75
|
+
# others own their folders. Only tracked files go: what git ignores there,
|
|
76
|
+
# such as installed dependencies, is not output.
|
|
77
|
+
if [[ -n "$atroot" ]]; then
|
|
78
|
+
git ls-files -z -- . ':(exclude).sdk' | while IFS= read -r -d '' f; do
|
|
79
|
+
rm -f -- "$f"
|
|
80
|
+
done
|
|
81
|
+
fi
|
|
82
|
+
|
|
66
83
|
log="$(mktemp)"
|
|
67
84
|
trap 'rm -f "$log"' EXIT
|
|
68
85
|
if ! ( cd "$sdk_dir" && npm run generate ) >"$log" 2>&1; then
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
AgentGuide,
|
|
16
16
|
Test,
|
|
17
17
|
|
|
18
|
+
SdkGenError,
|
|
19
|
+
|
|
18
20
|
} from '@voxgig/sdkgen'
|
|
19
21
|
|
|
20
22
|
import {
|
|
@@ -29,6 +31,7 @@ import { PointUtil, Content } from 'jostraca'
|
|
|
29
31
|
|
|
30
32
|
import { Top } from './Top'
|
|
31
33
|
import { BuildSDK } from './BuildSDK'
|
|
34
|
+
import { rootPlan } from './RootPlan'
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
const {
|
|
@@ -69,58 +72,79 @@ const Root = cmp(function Root(props: any) {
|
|
|
69
72
|
ctx$.stdrep = {}
|
|
70
73
|
names(ctx$.stdrep, model.Name, 'Project' + 'Name')
|
|
71
74
|
|
|
75
|
+
const plan = rootPlan(model.main[KIT], SdkGenError)
|
|
76
|
+
|
|
72
77
|
Project({}, () => {
|
|
73
78
|
|
|
74
|
-
|
|
79
|
+
if (plan.top) {
|
|
80
|
+
Top({})
|
|
81
|
+
}
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
if (plan.build) {
|
|
84
|
+
BuildSDK({})
|
|
85
|
+
}
|
|
77
86
|
|
|
78
87
|
each(target, (target: any) => {
|
|
88
|
+
const place = plan.place[target.name]
|
|
89
|
+
|
|
90
|
+
if (null == place) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
79
94
|
names(target, target.name)
|
|
80
95
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
names(entity, entity.name)
|
|
90
|
-
Entity({ target, entity })
|
|
91
|
-
})
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (phaseActive('feature')) {
|
|
95
|
-
each(feature).filter((feature: any) => feature.active).map((feature: any) => {
|
|
96
|
-
names(feature, feature.name)
|
|
97
|
-
Feature({ target, feature })
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
Main({ target })
|
|
102
|
-
|
|
103
|
-
if (phaseActive('readme')) {
|
|
104
|
-
Readme({ target })
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Per-target agent guides: <lang>/AGENTS.md + CLAUDE.md, and (driven
|
|
108
|
-
// internally by AgentGuide) a guide per active feature under
|
|
109
|
-
// <lang>/src/feature/<name>/. Placement mirrors Readme.
|
|
110
|
-
if (phaseActive('agentguide')) {
|
|
111
|
-
AgentGuide({ target })
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (phaseActive('test')) {
|
|
115
|
-
Test({ target })
|
|
116
|
-
}
|
|
117
|
-
})
|
|
96
|
+
if ('root' === place) {
|
|
97
|
+
targetPhases(target, entity, feature)
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
Folder({ name: target.name }, () => {
|
|
101
|
+
targetPhases(target, entity, feature)
|
|
102
|
+
})
|
|
103
|
+
}
|
|
118
104
|
})
|
|
119
105
|
|
|
120
106
|
})
|
|
121
107
|
})
|
|
122
108
|
|
|
123
109
|
|
|
110
|
+
function targetPhases(target: any, entity: any, feature: any) {
|
|
111
|
+
const phase = target.phase || {}
|
|
112
|
+
const phaseActive = (name: string): boolean =>
|
|
113
|
+
false !== (phase[name] && phase[name].active)
|
|
114
|
+
|
|
115
|
+
if (phaseActive('entity')) {
|
|
116
|
+
each(entity).filter((entity: any) => entity.active).map((entity: any) => {
|
|
117
|
+
names(entity, entity.name)
|
|
118
|
+
Entity({ target, entity })
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (phaseActive('feature')) {
|
|
123
|
+
each(feature).filter((feature: any) => feature.active).map((feature: any) => {
|
|
124
|
+
names(feature, feature.name)
|
|
125
|
+
Feature({ target, feature })
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Main({ target })
|
|
130
|
+
|
|
131
|
+
if (phaseActive('readme')) {
|
|
132
|
+
Readme({ target })
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Per-target agent guides: <lang>/AGENTS.md + CLAUDE.md, and (driven
|
|
136
|
+
// internally by AgentGuide) a guide per active feature under
|
|
137
|
+
// <lang>/src/feature/<name>/. Placement mirrors Readme.
|
|
138
|
+
if (phaseActive('agentguide')) {
|
|
139
|
+
AgentGuide({ target })
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (phaseActive('test')) {
|
|
143
|
+
Test({ target })
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
124
148
|
function makeFlow(def: any, data: any, stepMakers: Record<string, any>) {
|
|
125
149
|
|
|
126
150
|
const steps: any = {}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
// Import-free, so the scaffold's own tests can load it without the toolchain.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
type Place = 'folder' | 'root'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type RootPlan = {
|
|
9
|
+
top: boolean
|
|
10
|
+
build: boolean
|
|
11
|
+
|
|
12
|
+
// Generated targets only: absent means not generated at all.
|
|
13
|
+
place: Record<string, Place>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
type Fail = new (message: string) => Error
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function rootPlan(kit: any, Fail: Fail = Error): RootPlan {
|
|
21
|
+
const phase = kit?.phase || {}
|
|
22
|
+
const phaseActive = (name: string): boolean =>
|
|
23
|
+
false !== (phase[name] && phase[name].active)
|
|
24
|
+
|
|
25
|
+
const top = phaseActive('top')
|
|
26
|
+
const build = phaseActive('build')
|
|
27
|
+
|
|
28
|
+
const target = kit?.target || {}
|
|
29
|
+
const place: Record<string, Place> = {}
|
|
30
|
+
|
|
31
|
+
for (const name of Object.keys(target).sort()) {
|
|
32
|
+
if (null != target[name] && false !== target[name].active) {
|
|
33
|
+
place[name] = true === target[name].output?.root ? 'root' : 'folder'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const atRoot = Object.keys(place).filter((name: string) => 'root' === place[name])
|
|
38
|
+
|
|
39
|
+
if (1 < atRoot.length) {
|
|
40
|
+
throw new Fail(
|
|
41
|
+
'Only one target can be generated at the project root, and ' +
|
|
42
|
+
atRoot.length + ' declare `output: root: true`: ' + atRoot.join(', ') + '.')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (1 === atRoot.length && top) {
|
|
46
|
+
throw new Fail(
|
|
47
|
+
'Target "' + atRoot[0] + '" is generated at the project root, where the ' +
|
|
48
|
+
'SDK repository files (README, LICENSE, Makefile, workflows) would ' +
|
|
49
|
+
'overwrite its own. Declare `main: kit: phase: top: active: false` in ' +
|
|
50
|
+
'model/project.aontu.')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { top, build, place }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
export type {
|
|
58
|
+
Place,
|
|
59
|
+
RootPlan,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
rootPlan,
|
|
64
|
+
}
|