@nan0web/release 1.0.1 → 1.0.3
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/README.md +24 -42
- package/bin/nan0release.js +27 -42
- package/package.json +48 -42
- package/project.yaml +79 -0
- package/src/README.md.js +90 -93
- package/src/Release/Document.js +13 -19
- package/src/Release/MarkdownToTest.js +35 -33
- package/src/Release/Person.js +3 -8
- package/src/Release/Scanner.js +179 -0
- package/src/Release.js +19 -12
- package/src/Team.js +9 -9
- package/src/architecture/ProjectManagementAsCode.js +8 -8
- package/src/cli/index.js +9 -0
- package/src/co/AppCommandOptions.js +4 -6
- package/src/commands/CloseCommand.js +67 -0
- package/src/commands/Command.js +48 -0
- package/src/commands/DepsCommand.js +143 -0
- package/src/commands/InitCommand.js +48 -56
- package/src/commands/PublishCommand.js +80 -119
- package/src/commands/SpecCommand.js +41 -0
- package/src/commands/StatusCommand.js +26 -0
- package/src/db/ReleaseDB.js +5 -5
- package/src/index.js +18 -0
- package/src/ui/cli/ReleaseCLi.js +52 -0
- package/src/utils/runSpawn.js +0 -0
- package/types/Release/Document.d.ts +2 -2
- package/types/Release/Person.d.ts +3 -3
- package/types/Release/Scanner.d.ts +90 -0
- package/types/Release.d.ts +37 -33
- package/types/ReleaseCLI.d.ts +29 -28
- package/types/Team.d.ts +3 -3
- package/types/architecture/ProjectManagementAsCode.d.ts +1 -1
- package/types/cli/index.d.ts +2 -0
- package/types/co/AppCommandMessage.d.ts +10 -11
- package/types/co/AppCommandOptions.d.ts +1 -2
- package/types/commands/BaseCommand.d.ts +23 -0
- package/types/commands/ChatCommand.d.ts +9 -9
- package/types/commands/ChatMessage.d.ts +36 -0
- package/types/commands/CloseCommand.d.ts +25 -0
- package/types/commands/Command.d.ts +28 -0
- package/types/commands/DepsCommand.d.ts +32 -0
- package/types/commands/DepsMessage.d.ts +28 -0
- package/types/commands/HostCommand.d.ts +7 -7
- package/types/commands/HostMessage.d.ts +44 -0
- package/types/commands/InitCommand.d.ts +24 -19
- package/types/commands/InitMessage.d.ts +24 -0
- package/types/commands/ListCommand.d.ts +8 -8
- package/types/commands/ListMessage.d.ts +30 -0
- package/types/commands/PublishCommand.d.ts +36 -47
- package/types/commands/PublishMessage.d.ts +39 -0
- package/types/commands/SealCommand.d.ts +7 -7
- package/types/commands/SealMessage.d.ts +28 -0
- package/types/commands/ServeCommand.d.ts +7 -7
- package/types/commands/ServeMessage.d.ts +30 -0
- package/types/commands/ShowCommand.d.ts +7 -7
- package/types/commands/SpecCommand.d.ts +25 -0
- package/types/commands/StatusCommand.d.ts +9 -0
- package/types/commands/SubCommand.d.ts +9 -7
- package/types/commands/ValidateCommand.d.ts +7 -7
- package/types/commands/ValidateMessage.d.ts +20 -0
- package/types/db/ReleaseDB.d.ts +2 -2
- package/types/index.d.ts +10 -0
- package/types/ui/cli/ReleaseCLi.d.ts +10 -0
- package/types/ui/cli/index.d.ts +0 -0
- package/types/utils/runSpawn.d.ts +0 -0
- package/.datasets/README.dataset.jsonl +0 -19
- package/.editorconfig +0 -20
- package/CHANGELOG.md +0 -6
- package/CONTRIBUTING.md +0 -42
- package/bin/commands/InitCommand.js +0 -10
- package/bin/lib/functions.js +0 -240
- package/bin/lib/functions.test.js +0 -138
- package/bin/release.js +0 -12
- package/bin/release.test.js +0 -73
- package/bin/temp/package.json +0 -1
- package/docs/system.uk.md +0 -413
- package/docs/uk/README.md +0 -284
- package/legacy/PrepareCommand.js +0 -154
- package/legacy/PrepareCommand.test.js +0 -189
- package/legacy/changelog.js +0 -446
- package/releases/1/0/v1.0.0/models/Person.js +0 -26
- package/releases/1/0/v1.0.0/models/Person.test.js +0 -44
- package/releases/1/0/v1.0.0/models/company.js +0 -11
- package/releases/1/0/v1.0.0/models/members.js +0 -14
- package/releases/1/0/v1.0.0/models/members.test.js +0 -21
- package/releases/1/0/v1.0.0/models/teams/DevTeam.js +0 -6
- package/releases/1/0/v1.0.0/models/teams/DevTeam.test.js +0 -15
- package/releases/1/0/v1.0.0/models/teams/index.js +0 -5
- package/releases/1/0/v1.0.0/release.js +0 -12
- package/releases/1/0/v1.0.0/release.md +0 -121
- package/releases/1/0/v1.0.0/release.test.js +0 -36
- package/releases/1/0/v1.0.0/test/YaRaSLove/git-native.test.js +0 -14
- package/releases/current.js +0 -3
- package/releases/index.js +0 -11
- package/src/Release/MarkdownToTest.test.js +0 -60
- package/src/ReleaseCLI.js +0 -122
- package/src/architecture/ProjectManagementAsCode.test.js +0 -128
- package/src/co/AppCommandMessage.js +0 -22
- package/src/commands/ChatCommand.js +0 -56
- package/src/commands/HostCommand.js +0 -63
- package/src/commands/ListCommand.js +0 -56
- package/src/commands/SealCommand.js +0 -51
- package/src/commands/ServeCommand.js +0 -53
- package/src/commands/ShowCommand.js +0 -58
- package/src/commands/SubCommand.js +0 -21
- package/src/commands/ValidateCommand.js +0 -66
- package/system.md +0 -571
- package/tsconfig.json +0 -23
- /package/{bin/lib/temp/pnpm-lock.yaml → src/ui/cli/index.js} +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DepsCommand – Message‑based version of the old `DepsCommand`.
|
|
3
|
+
*
|
|
4
|
+
* Checks and optionally fixes workspace / link dependencies.
|
|
5
|
+
*
|
|
6
|
+
* Body schema:
|
|
7
|
+
* fix – boolean – whether to apply fixes (default: false)
|
|
8
|
+
*
|
|
9
|
+
* @module release/commands/DepsCommand
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Message, OutputMessage } from '@nan0web/co'
|
|
13
|
+
|
|
14
|
+
import Command from './Command.js'
|
|
15
|
+
|
|
16
|
+
class DepsBody {
|
|
17
|
+
/** @type {boolean} */
|
|
18
|
+
fix = false
|
|
19
|
+
static fix = {
|
|
20
|
+
help: 'Apply fixes automatically',
|
|
21
|
+
defaultValue: false,
|
|
22
|
+
}
|
|
23
|
+
/** @type {boolean} */
|
|
24
|
+
latest = false
|
|
25
|
+
static latest = {
|
|
26
|
+
help: 'Update @nan0web packages to the latests available versions',
|
|
27
|
+
defaultValue: false,
|
|
28
|
+
}
|
|
29
|
+
constructor(input = {}) {
|
|
30
|
+
const { fix = this.fix, latest = this.latest } = Message.parseBody(input, DepsBody)
|
|
31
|
+
this.fix = Boolean(fix)
|
|
32
|
+
this.latest = Boolean(latest)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default class DepsCommand extends Command {
|
|
37
|
+
static name = 'deps'
|
|
38
|
+
static help = 'Dependencies manager'
|
|
39
|
+
static Body = DepsBody
|
|
40
|
+
/** @type {DepsBody} */
|
|
41
|
+
body
|
|
42
|
+
|
|
43
|
+
/** @param {Partial<DepsBody>} [input={}] */
|
|
44
|
+
constructor(input = {}) {
|
|
45
|
+
super({})
|
|
46
|
+
this.body = Command.parseBody(input, DepsBody)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {DepsCommand} [msg]
|
|
51
|
+
* @returns {AsyncGenerator<OutputMessage>}
|
|
52
|
+
*/
|
|
53
|
+
async *run(msg = new DepsCommand()) {
|
|
54
|
+
const pkg = await this.fs.loadDocument('package.json', {})
|
|
55
|
+
yield new OutputMessage(['Loaded package.json', JSON.stringify(pkg).length + ' bytes'])
|
|
56
|
+
const deps = {
|
|
57
|
+
devDependencies: pkg?.devDependencies ?? {},
|
|
58
|
+
dependencies: pkg?.dependencies ?? {},
|
|
59
|
+
peerDependencies: pkg?.peerDependencies ?? {},
|
|
60
|
+
}
|
|
61
|
+
const fixes = new Map()
|
|
62
|
+
const fails = new Set()
|
|
63
|
+
|
|
64
|
+
const isLocal = (ver) =>
|
|
65
|
+
['workspace:', 'link:'].some((p) => String(ver).toLowerCase().startsWith(p))
|
|
66
|
+
|
|
67
|
+
const changes = new Map()
|
|
68
|
+
|
|
69
|
+
for (const [group, map] of Object.entries(deps)) {
|
|
70
|
+
yield new OutputMessage(`${group} (${Object.keys(map).length} items)`)
|
|
71
|
+
for (const [dep, ver] of Object.entries(map ?? {})) {
|
|
72
|
+
if (isLocal(ver) || msg.body.latest) {
|
|
73
|
+
const result = await this._run(
|
|
74
|
+
'npm',
|
|
75
|
+
['info', dep, '--json'],
|
|
76
|
+
'Cannot retrieve npm package info',
|
|
77
|
+
)
|
|
78
|
+
try {
|
|
79
|
+
const data = JSON.parse(result.text)
|
|
80
|
+
if (data.version) {
|
|
81
|
+
fixes.set(dep, data.version)
|
|
82
|
+
yield new OutputMessage({
|
|
83
|
+
content: [` ${dep}: ${ver} → ${data.version}`],
|
|
84
|
+
type: OutputMessage.TYPES.WARNING,
|
|
85
|
+
})
|
|
86
|
+
} else {
|
|
87
|
+
fails.add(dep)
|
|
88
|
+
yield new OutputMessage({
|
|
89
|
+
content: [` ${dep}: 404 — not found`],
|
|
90
|
+
type: OutputMessage.TYPES.ERROR,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
} catch (/** @type {any} */ err) {
|
|
94
|
+
yield new OutputMessage(err)
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
yield new OutputMessage({
|
|
98
|
+
content: [` ${dep}: ${ver}`],
|
|
99
|
+
type: OutputMessage.TYPES.SUCCESS,
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (fails.size) {
|
|
106
|
+
yield new OutputMessage(
|
|
107
|
+
new Error(`Some dependencies are not yet published on npm: ${[...fails].join(', ')}`),
|
|
108
|
+
)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if ((msg.body.fix || msg.body.latest) && fixes.size) {
|
|
113
|
+
for (const [dep, ver] of fixes) {
|
|
114
|
+
// naive fix – replace in package.json
|
|
115
|
+
const args = ['install']
|
|
116
|
+
if (pkg.devDependencies?.[dep]) {
|
|
117
|
+
pkg.devDependencies[dep] = ver
|
|
118
|
+
args.push('--save-dev')
|
|
119
|
+
}
|
|
120
|
+
if (pkg.dependencies?.[dep]) pkg.dependencies[dep] = ver
|
|
121
|
+
args.push(dep + '@latest')
|
|
122
|
+
if (msg.body.latest) {
|
|
123
|
+
yield new OutputMessage([`Installing dependency`])
|
|
124
|
+
await this._run('npm', args)
|
|
125
|
+
}
|
|
126
|
+
changes.set(dep, `${dep}: ${ver}`)
|
|
127
|
+
yield new OutputMessage({
|
|
128
|
+
content: [`Fixed ${dep} → ${ver}`],
|
|
129
|
+
type: OutputMessage.TYPES.SUCCESS,
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
await this.fs.saveDocument('package.json', pkg)
|
|
133
|
+
yield new OutputMessage({
|
|
134
|
+
content: [`Fixes saved (${changes.size} changes)`],
|
|
135
|
+
type: OutputMessage.TYPES.SUCCESS,
|
|
136
|
+
})
|
|
137
|
+
yield new OutputMessage({ content: ['Dependencies fixed'] })
|
|
138
|
+
} else {
|
|
139
|
+
yield new OutputMessage(`Run with --fix to apply ${fixes.size} available fixes`)
|
|
140
|
+
yield new OutputMessage({ content: ['Dependencies check completed'] })
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -1,65 +1,57 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import { OutputMessage } from '@nan0web/co'
|
|
2
|
+
import Command from './Command.js'
|
|
3
|
+
import process from 'node:process'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* Usage:
|
|
12
|
-
* release init v1.0.0
|
|
13
|
-
*/
|
|
14
|
-
class InitCommand extends SubCommand {
|
|
15
|
-
constructor(options = {}) {
|
|
16
|
-
super(options)
|
|
17
|
-
this.addArgument("*", String, "", "Version for the new release")
|
|
5
|
+
class InitBody {
|
|
6
|
+
static version = { help: 'Release version', defaultValue: '' }
|
|
7
|
+
/** @type {string} */
|
|
8
|
+
version = ''
|
|
9
|
+
constructor({ version = '' } = {}) {
|
|
10
|
+
this.version = String(version)
|
|
18
11
|
}
|
|
12
|
+
}
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*/
|
|
25
|
-
async run(msg) {
|
|
26
|
-
if (!this.db) {
|
|
27
|
-
throw new CommandError("Database (working directory) not set")
|
|
28
|
-
}
|
|
29
|
-
const version = msg.args[0]
|
|
30
|
-
if (!version) {
|
|
31
|
-
throw new CommandError("Version not defined", msg)
|
|
32
|
-
}
|
|
33
|
-
const db = this.db.extractVersion(version)
|
|
14
|
+
export default class InitCommand extends Command {
|
|
15
|
+
static name = 'init'
|
|
16
|
+
static help = 'Initialize a new release structure'
|
|
17
|
+
static Body = InitBody
|
|
34
18
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
' createdAt: new Date(),',
|
|
41
|
-
' tasks: new Map()',
|
|
42
|
-
'})',
|
|
43
|
-
].join("\n")],
|
|
44
|
-
["release.md", [
|
|
45
|
-
`# ${version} - ${new Date().toISOString().split("T")[0]}`,
|
|
46
|
-
'## Overview',
|
|
47
|
-
'Initial release scaffold generated by `nan0release init`.',
|
|
48
|
-
].join("\n\n")]
|
|
49
|
-
]
|
|
19
|
+
/** @param {Partial<Command> & { body?: Partial<InitBody> }} [input={}] */
|
|
20
|
+
constructor(input = {}) {
|
|
21
|
+
super(input)
|
|
22
|
+
this.body = new InitBody(/** @type {*} */ (input))
|
|
23
|
+
}
|
|
50
24
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} else {
|
|
56
|
-
await db.saveDocument(file, content)
|
|
57
|
-
this.logger.success(file, "created")
|
|
58
|
-
}
|
|
25
|
+
async *run() {
|
|
26
|
+
let v = this.body.version
|
|
27
|
+
if (!v) {
|
|
28
|
+
throw new Error('Version is required (e.g. --version=1.0.0 or --version=v1.0.0)')
|
|
59
29
|
}
|
|
30
|
+
if (!v.startsWith('v')) v = 'v' + v
|
|
31
|
+
|
|
32
|
+
const parts = v.replace('v', '').split('.')
|
|
33
|
+
const major = parts[0] || '0'
|
|
34
|
+
const minor = parts[1] || '0'
|
|
60
35
|
|
|
61
|
-
|
|
36
|
+
const { mkdirSync, writeFileSync } = await import('node:fs')
|
|
37
|
+
const { join } = await import('node:path')
|
|
38
|
+
|
|
39
|
+
const dir = join(process.cwd(), 'releases', major, minor, v)
|
|
40
|
+
mkdirSync(dir, { recursive: true })
|
|
41
|
+
|
|
42
|
+
const taskPath = join(dir, 'task.md')
|
|
43
|
+
writeFileSync(taskPath, `# Release ${v}\n\n- [ ] Task 1`, 'utf8')
|
|
44
|
+
|
|
45
|
+
const specPath = join(dir, 'test.spec.js')
|
|
46
|
+
writeFileSync(
|
|
47
|
+
specPath,
|
|
48
|
+
`import test from 'node:test'\nimport assert from 'node:assert'\n\ntest('Sample spec', () => {\n\tassert.ok(true)\n})\n`,
|
|
49
|
+
'utf8',
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
yield new OutputMessage({
|
|
53
|
+
content: [`✅ Initialized release ${v} in ${dir}`],
|
|
54
|
+
type: OutputMessage.TYPES.SUCCESS,
|
|
55
|
+
})
|
|
62
56
|
}
|
|
63
57
|
}
|
|
64
|
-
|
|
65
|
-
export default InitCommand
|
|
@@ -1,144 +1,105 @@
|
|
|
1
|
-
import process from "node:process"
|
|
2
|
-
import { Command, CommandMessage } from "@nan0web/co"
|
|
3
|
-
import FS from "@nan0web/db-fs"
|
|
4
|
-
import { runSpawn } from '@nan0web/test'
|
|
5
|
-
|
|
6
|
-
export class PublishCommandOptions {
|
|
7
|
-
/** @type {string} */
|
|
8
|
-
tag
|
|
9
|
-
constructor(input = {}) {
|
|
10
|
-
const {
|
|
11
|
-
tag = ""
|
|
12
|
-
} = input
|
|
13
|
-
this.tag = String(tag)
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* @param {any} input
|
|
17
|
-
* @returns {PublishCommandOptions}
|
|
18
|
-
*/
|
|
19
|
-
static from(input) {
|
|
20
|
-
if (input instanceof PublishCommandOptions) return input
|
|
21
|
-
return new PublishCommandOptions(input)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
1
|
/**
|
|
26
|
-
*
|
|
2
|
+
* PublishCommand – Message‑based version of the old `PublishCommand`.
|
|
3
|
+
*
|
|
4
|
+
* Publishes the current package to NPM and creates a Git tag.
|
|
5
|
+
*
|
|
6
|
+
* Body schema:
|
|
7
|
+
* major | minor | patch – optional version bump flag.
|
|
8
|
+
*
|
|
9
|
+
* @module release/commands/PublishCommand
|
|
27
10
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
11
|
+
|
|
12
|
+
import { Message, OutputMessage } from '@nan0web/co'
|
|
13
|
+
|
|
14
|
+
import Command from './Command.js'
|
|
15
|
+
|
|
16
|
+
class PublishBody {
|
|
17
|
+
static major = { help: 'Bump major version', defaultValue: false }
|
|
18
|
+
static minor = { help: 'Bump minor version', defaultValue: false }
|
|
19
|
+
static patch = { help: 'Bump patch version', defaultValue: false }
|
|
20
|
+
|
|
21
|
+
/** @type {boolean} */
|
|
22
|
+
major = false
|
|
23
|
+
/** @type {boolean} */
|
|
24
|
+
minor = false
|
|
25
|
+
/** @type {boolean} */
|
|
26
|
+
patch = false
|
|
27
|
+
|
|
28
|
+
constructor({ major = false, minor = false, patch = false } = {}) {
|
|
29
|
+
this.major = Boolean(major)
|
|
30
|
+
this.minor = Boolean(minor)
|
|
31
|
+
this.patch = Boolean(patch)
|
|
39
32
|
}
|
|
40
33
|
}
|
|
41
34
|
|
|
42
|
-
/**
|
|
43
|
-
* @extends {Command}
|
|
44
|
-
*/
|
|
45
35
|
export default class PublishCommand extends Command {
|
|
46
|
-
static
|
|
36
|
+
static name = 'publish'
|
|
37
|
+
static help = 'Publish npm packages'
|
|
38
|
+
static Body = PublishBody
|
|
39
|
+
|
|
40
|
+
/** @type {PublishBody} */
|
|
41
|
+
body
|
|
47
42
|
|
|
43
|
+
/** @param {Partial<Command> & { body?: Partial<PublishBody> }} [input={}] */
|
|
48
44
|
constructor(input = {}) {
|
|
49
|
-
super(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
...input
|
|
53
|
-
})
|
|
54
|
-
this.addArgument("*", String, "", "Category of new version: major, minor, patch, or empty.")
|
|
45
|
+
super(input)
|
|
46
|
+
const { body = new PublishBody() } = Message.parseBody(input, PublishBody)
|
|
47
|
+
this.body = new PublishBody(body)
|
|
55
48
|
}
|
|
56
49
|
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
* - Publishes to NPM registry
|
|
69
|
-
* - Pushes changes and tags
|
|
70
|
-
*
|
|
71
|
-
* ```bash
|
|
72
|
-
* nan0release publish
|
|
73
|
-
* ```
|
|
74
|
-
* @param {PublishCommandMessage} msg
|
|
75
|
-
*/
|
|
76
|
-
async run(msg) {
|
|
77
|
-
const fs = new FS()
|
|
78
|
-
await fs.connect()
|
|
79
|
-
|
|
80
|
-
const category = ["major", "minor", "patch"].find(c => msg.args.includes(c))
|
|
81
|
-
if (category) {
|
|
82
|
-
await this.#run('npm', ["version", category], "Unable to change version", fs)
|
|
50
|
+
/** @returns {AsyncGenerator<OutputMessage>} */
|
|
51
|
+
async *run() {
|
|
52
|
+
const bump = this.body.major
|
|
53
|
+
? 'major'
|
|
54
|
+
: this.body.minor
|
|
55
|
+
? 'minor'
|
|
56
|
+
: this.body.patch
|
|
57
|
+
? 'patch'
|
|
58
|
+
: null
|
|
59
|
+
if (bump) {
|
|
60
|
+
await this._run('npm', ['version', bump], 'Failed to bump version')
|
|
83
61
|
}
|
|
84
|
-
|
|
85
|
-
const pkg = await fs.loadDocument('package.json', {})
|
|
62
|
+
const pkg = await this.fs.loadDocument('package.json', {})
|
|
86
63
|
const tag = `v${pkg.version}`
|
|
87
64
|
|
|
88
|
-
|
|
65
|
+
yield new OutputMessage(`🛜 nan0release: publishing @${pkg.name}@${pkg.version}`)
|
|
66
|
+
|
|
67
|
+
await this._run('git', ['diff', '--quiet'], 'Uncommitted changes found')
|
|
89
68
|
|
|
90
|
-
this.
|
|
69
|
+
await this._run('git', ['pull'], 'Failed to pull latest changes')
|
|
70
|
+
await this._run('npm', ['run', 'clean'], 'Clean failed')
|
|
71
|
+
await this._run('npm', ['run', 'build'], 'Build failed')
|
|
72
|
+
await this._run('npm', ['test'], 'Tests failed')
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
await this.#run('npm', ["run", "clean"], 'Clean failed', fs)
|
|
94
|
-
await this.#run('npm', ["run", "build"], 'Build failed', fs)
|
|
95
|
-
await this.#run('npm', ["test"], 'Tests failed', fs)
|
|
74
|
+
return
|
|
96
75
|
|
|
97
|
-
const tagsResult = await this
|
|
76
|
+
const tagsResult = await this._run('git', ['tag'], 'Failed to get tags')
|
|
98
77
|
const tags = tagsResult.text.split('\n').filter(Boolean)
|
|
99
78
|
|
|
100
79
|
if (!tags.includes(tag)) {
|
|
101
|
-
await this
|
|
80
|
+
await this._run(
|
|
81
|
+
'git',
|
|
82
|
+
['tag', '-a', tag, '-m', `Release ${pkg.version}`],
|
|
83
|
+
'Tag creation failed',
|
|
84
|
+
)
|
|
85
|
+
yield new OutputMessage({
|
|
86
|
+
content: [`Tag ${tag} created`],
|
|
87
|
+
type: OutputMessage.TYPES.SUCCESS,
|
|
88
|
+
})
|
|
102
89
|
} else {
|
|
103
|
-
|
|
90
|
+
yield new OutputMessage({
|
|
91
|
+
content: [`Tag ${tag} already exists`],
|
|
92
|
+
type: OutputMessage.TYPES.WARNING,
|
|
93
|
+
})
|
|
104
94
|
}
|
|
105
95
|
|
|
106
|
-
await this
|
|
96
|
+
await this._run('npm', ['publish', '--access', 'public'], 'Publish to npm failed')
|
|
97
|
+
await this._run('git', ['push', 'origin', 'main', '--no-verify'], 'Git push failed')
|
|
98
|
+
await this._run('git', ['push', 'origin', '--tags', '--no-verify'], 'Tag push failed')
|
|
107
99
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @param {string} cmd
|
|
116
|
-
* @param {string[]} args
|
|
117
|
-
* @param {string} errorMsg
|
|
118
|
-
* @param {FS} fs
|
|
119
|
-
* @param {number[]} [okCodes=[0]]
|
|
120
|
-
*/
|
|
121
|
-
async #run(cmd, args, errorMsg, fs, okCodes = [0]) {
|
|
122
|
-
let content = ""
|
|
123
|
-
const onData = (chunk) => {
|
|
124
|
-
content += String(chunk)
|
|
125
|
-
const rows = content.split("\n").filter(Boolean)
|
|
126
|
-
const recent = rows.slice(-1)
|
|
127
|
-
if (recent.length) {
|
|
128
|
-
this.logger.cursorUp(1, true)
|
|
129
|
-
this.logger.info(recent[0])
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
let cwd = fs.absolute()
|
|
134
|
-
if ("/" === cwd && "." === fs.cwd) cwd = fs.cwd
|
|
135
|
-
const response = await runSpawn(cmd, args, { onData, cwd })
|
|
136
|
-
|
|
137
|
-
if (!okCodes.includes(response.code)) {
|
|
138
|
-
this.logger.error(errorMsg)
|
|
139
|
-
process.exit(1)
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return response
|
|
100
|
+
yield new OutputMessage({
|
|
101
|
+
content: [`${pkg.name}@${pkg.version} published.`],
|
|
102
|
+
type: OutputMessage.TYPES.SUCCESS,
|
|
103
|
+
})
|
|
143
104
|
}
|
|
144
105
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { OutputMessage } from '@nan0web/co'
|
|
2
|
+
import Command from './Command.js'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import Scanner from '../Release/Scanner.js'
|
|
5
|
+
|
|
6
|
+
class SpecBody {
|
|
7
|
+
static version = { help: 'Release version filter', defaultValue: '' }
|
|
8
|
+
/** @type {string} */
|
|
9
|
+
version = ''
|
|
10
|
+
constructor({ version = '' } = {}) {
|
|
11
|
+
this.version = String(version)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default class SpecCommand extends Command {
|
|
16
|
+
static name = 'spec'
|
|
17
|
+
static help = 'Run spec testing for the active release'
|
|
18
|
+
static Body = SpecBody
|
|
19
|
+
|
|
20
|
+
/** @param {Partial<Command> & { body?: Partial<SpecBody> }} [input={}] */
|
|
21
|
+
constructor(input = {}) {
|
|
22
|
+
super(input)
|
|
23
|
+
this.body = new SpecBody(/** @type {*} */ (input))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async *run() {
|
|
27
|
+
yield new OutputMessage(`🛜 nan0release spec ${this.body.version || 'all'}`)
|
|
28
|
+
const scanner = new Scanner(process.cwd())
|
|
29
|
+
const specs = scanner.findSpecs(this.body.version)
|
|
30
|
+
if (specs.length === 0) {
|
|
31
|
+
yield new OutputMessage(`No specs found.`)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
const files = specs.map((s) => s.path)
|
|
35
|
+
await this._run('node', ['--test', ...files], 'Specs failed')
|
|
36
|
+
yield new OutputMessage({
|
|
37
|
+
content: [`✅ Specs passed`],
|
|
38
|
+
type: OutputMessage.TYPES.SUCCESS,
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { OutputMessage } from '@nan0web/co'
|
|
2
|
+
import Command from './Command.js'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import Scanner from '../Release/Scanner.js'
|
|
5
|
+
|
|
6
|
+
export default class StatusCommand extends Command {
|
|
7
|
+
static name = 'status'
|
|
8
|
+
static help = 'Show release status'
|
|
9
|
+
|
|
10
|
+
constructor(input = {}) {
|
|
11
|
+
super(input)
|
|
12
|
+
this.body = {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async *run() {
|
|
16
|
+
const scanner = new Scanner(process.cwd())
|
|
17
|
+
const statuses = scanner.status()
|
|
18
|
+
|
|
19
|
+
yield new OutputMessage(`🛜 nan0release status`)
|
|
20
|
+
for (const st of statuses) {
|
|
21
|
+
yield new OutputMessage(
|
|
22
|
+
`${st.version.padEnd(10)} | ${st.state.padEnd(6)} | Specs: ${st.specs.length}, Tests: ${st.tests.length}`,
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/db/ReleaseDB.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import DBFS from
|
|
1
|
+
import DBFS from '@nan0web/db-fs'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ReleaseDB – thin wrapper around DBFS that adds a convenient `releases`
|
|
@@ -22,10 +22,10 @@ class ReleaseDB extends DBFS {
|
|
|
22
22
|
extractVersion(version) {
|
|
23
23
|
const found = version.match(/^v(\d+)\.(\d+)\.(\d+)$/)
|
|
24
24
|
if (!found) {
|
|
25
|
-
throw new Error(
|
|
25
|
+
throw new Error('Incorrect version')
|
|
26
26
|
}
|
|
27
|
-
const arr = version.slice(1).split(
|
|
28
|
-
const path = [arr[0], arr[1], version,
|
|
27
|
+
const arr = version.slice(1).split('.')
|
|
28
|
+
const path = [arr[0], arr[1], version, ''].join('/')
|
|
29
29
|
return ReleaseDB.from(this.extract(path))
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
@@ -35,7 +35,7 @@ class ReleaseDB extends DBFS {
|
|
|
35
35
|
const paths = new Set()
|
|
36
36
|
for (const key of this.data.keys()) {
|
|
37
37
|
// Expected structure: major/minor/vX.Y.Z/…
|
|
38
|
-
const parts = key.split(
|
|
38
|
+
const parts = key.split('/')
|
|
39
39
|
if (parts.length > 2 && /^\d+$/.test(parts[0]) && /^\d+$/.test(parts[1])) {
|
|
40
40
|
paths.add(`${parts[0]}/${parts[1]}/${parts[2]}`)
|
|
41
41
|
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import Release from './Release.js'
|
|
2
|
+
import ReleaseDocument from './Release/Document.js'
|
|
3
|
+
import Person from './Release/Person.js'
|
|
4
|
+
import Scanner from './Release/Scanner.js'
|
|
5
|
+
import MarkdownToTest from './Release/MarkdownToTest.js'
|
|
6
|
+
import { ProjectManagement, ReleaseManager, TaskTestSuite, ChangelogTaskManager } from './architecture/ProjectManagementAsCode.js'
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
Release,
|
|
10
|
+
ReleaseDocument,
|
|
11
|
+
Person,
|
|
12
|
+
Scanner,
|
|
13
|
+
MarkdownToTest,
|
|
14
|
+
ProjectManagement,
|
|
15
|
+
ReleaseManager,
|
|
16
|
+
TaskTestSuite,
|
|
17
|
+
ChangelogTaskManager
|
|
18
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReleaseCLI – UI‑CLI driven entry point that registers the
|
|
3
|
+
* Message‑based release commands.
|
|
4
|
+
*
|
|
5
|
+
* Usage (from terminal):
|
|
6
|
+
*
|
|
7
|
+
* npx nan0release <subcommand> [options] [args]
|
|
8
|
+
*
|
|
9
|
+
* Subcommands:
|
|
10
|
+
* init, deps, publish, seal, host, serve, list, validate, chat
|
|
11
|
+
*
|
|
12
|
+
* Each subcommand is a Message class exporting a `run` async generator
|
|
13
|
+
* that yields {@link OutputMessage}s.
|
|
14
|
+
*
|
|
15
|
+
* @module release/cli/ReleaseCLI
|
|
16
|
+
*/
|
|
17
|
+
import process from 'node:process'
|
|
18
|
+
|
|
19
|
+
import { CLI } from '@nan0web/ui-cli'
|
|
20
|
+
import Logger from '@nan0web/log'
|
|
21
|
+
import DBFS from '@nan0web/db-fs'
|
|
22
|
+
import DepsCommand from '../../commands/DepsCommand.js'
|
|
23
|
+
import PublishCommand from '../../commands/PublishCommand.js'
|
|
24
|
+
import InitCommand from '../../commands/InitCommand.js'
|
|
25
|
+
import SpecCommand from '../../commands/SpecCommand.js'
|
|
26
|
+
import CloseCommand from '../../commands/CloseCommand.js'
|
|
27
|
+
import StatusCommand from '../../commands/StatusCommand.js'
|
|
28
|
+
|
|
29
|
+
class ReleaseCLi extends CLI {
|
|
30
|
+
/** @type {DBFS} */
|
|
31
|
+
fs
|
|
32
|
+
constructor(input) {
|
|
33
|
+
super(input)
|
|
34
|
+
const { fs } = input
|
|
35
|
+
this.fs = fs ?? DBFS.from({})
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default function createReleaseCLI(
|
|
40
|
+
argv = process.argv.slice(2),
|
|
41
|
+
logger = new Logger(),
|
|
42
|
+
fs = {},
|
|
43
|
+
) {
|
|
44
|
+
const cli = new ReleaseCLi({
|
|
45
|
+
argv,
|
|
46
|
+
commands: {}, // no map‑based commands
|
|
47
|
+
Messages: [InitCommand, DepsCommand, PublishCommand, SpecCommand, CloseCommand, StatusCommand],
|
|
48
|
+
logger,
|
|
49
|
+
fs: DBFS.from(fs),
|
|
50
|
+
})
|
|
51
|
+
return cli
|
|
52
|
+
}
|
|
File without changes
|
|
@@ -27,5 +27,5 @@ declare class ReleaseDocument extends Markdown {
|
|
|
27
27
|
/** @type {Date | undefined} */
|
|
28
28
|
date: Date | undefined;
|
|
29
29
|
}
|
|
30
|
-
import Markdown from
|
|
31
|
-
import Person from
|
|
30
|
+
import Markdown from '@nan0web/markdown';
|
|
31
|
+
import Person from './Person.js';
|
|
@@ -12,6 +12,6 @@ export default class Person {
|
|
|
12
12
|
/** @type {HumanContact[]} */
|
|
13
13
|
contacts: HumanContact[];
|
|
14
14
|
}
|
|
15
|
-
import { HumanName } from
|
|
16
|
-
import { HumanGender } from
|
|
17
|
-
import { HumanContact } from
|
|
15
|
+
import { HumanName } from '@nan0web/verse';
|
|
16
|
+
import { HumanGender } from '@nan0web/verse';
|
|
17
|
+
import { HumanContact } from '@nan0web/verse';
|