@nan0web/release 1.0.3 → 3.1.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/README.md +42 -17
- package/bin/nan0release.js +3 -39
- package/knip.json +21 -0
- package/nan0web.nan0 +16 -0
- package/package.json +14 -14
- package/src/README.md.js +19 -21
- package/src/Release/Person.js +1 -1
- package/src/agents/inspectors/git-reviewer.md +35 -0
- package/src/agents/inspectors/package-hygiene.md +63 -0
- package/src/agents/workflows/commit.md +28 -0
- package/src/agents/workflows/npm-release.md +40 -0
- package/src/agents/workflows/release.md +205 -0
- package/src/agents/workflows/zero-tolerance-git.md +21 -0
- package/src/domain/App.js +36 -0
- package/src/domain/BumpCommand.js +137 -0
- package/src/domain/CheckCommand.js +193 -0
- package/src/domain/CloseCommand.js +68 -0
- package/src/domain/DepsCommand.js +104 -0
- package/src/domain/InitCommand.js +57 -0
- package/src/domain/PublishCommand.js +89 -0
- package/src/domain/SpecCommand.js +43 -0
- package/src/domain/StatusCommand.js +25 -0
- package/src/index.js +16 -2
- package/types/Release/Person.d.ts +3 -3
- package/types/Team.d.ts +3 -3
- package/types/commands/CheckCommand.d.ts +24 -0
- package/types/commands/CloseCommand.d.ts +2 -1
- package/types/commands/Command.d.ts +6 -1
- package/types/commands/DepsCommand.d.ts +2 -1
- package/types/commands/InitCommand.d.ts +2 -1
- package/types/commands/PublishCommand.d.ts +2 -1
- package/types/commands/SpecCommand.d.ts +2 -1
- package/types/domain/App.d.ts +17 -0
- package/types/domain/BumpCommand.d.ts +32 -0
- package/types/domain/CheckCommand.d.ts +24 -0
- package/types/domain/CloseCommand.d.ts +17 -0
- package/types/domain/DepsCommand.d.ts +22 -0
- package/types/domain/InitCommand.d.ts +18 -0
- package/types/domain/PublishCommand.d.ts +27 -0
- package/types/domain/SpecCommand.d.ts +17 -0
- package/types/domain/StatusCommand.d.ts +7 -0
- package/types/index.d.ts +8 -0
- package/types/ui/cli/index.d.ts +1 -0
- package/types/utils/runSpawn.d.ts +1 -0
- package/src/Team.js +0 -35
- package/src/cli/index.js +0 -9
- package/src/co/AppCommandOptions.js +0 -25
- package/src/commands/CloseCommand.js +0 -67
- package/src/commands/Command.js +0 -48
- package/src/commands/DepsCommand.js +0 -143
- package/src/commands/InitCommand.js +0 -57
- package/src/commands/PublishCommand.js +0 -105
- package/src/commands/SpecCommand.js +0 -41
- package/src/commands/StatusCommand.js +0 -26
- package/src/db/ReleaseDB.js +0 -54
- package/src/ui/cli/ReleaseCLi.js +0 -52
- package/src/ui/cli/index.js +0 -0
- package/src/utils/runSpawn.js +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default class SpecCommand extends ModelAsApp {
|
|
2
|
+
static version: {
|
|
3
|
+
help: string;
|
|
4
|
+
default: string;
|
|
5
|
+
};
|
|
6
|
+
static UI: {
|
|
7
|
+
title: string;
|
|
8
|
+
help: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* @param {Partial<SpecCommand>} [data]
|
|
12
|
+
* @param {Partial<import('@nan0web/ui').ModelAsAppOptions>} [options]
|
|
13
|
+
*/
|
|
14
|
+
constructor(data?: Partial<SpecCommand>, options?: Partial<import("@nan0web/ui").ModelAsAppOptions>);
|
|
15
|
+
/** @type {string} Release version filter */ version: string;
|
|
16
|
+
}
|
|
17
|
+
import { ModelAsApp } from '@nan0web/ui';
|
package/types/index.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export { ReleaseApp } from "./domain/App.js";
|
|
2
|
+
export { default as InitCommand } from "./domain/InitCommand.js";
|
|
3
|
+
export { default as CheckCommand } from "./domain/CheckCommand.js";
|
|
4
|
+
export { default as CloseCommand } from "./domain/CloseCommand.js";
|
|
5
|
+
export { default as DepsCommand } from "./domain/DepsCommand.js";
|
|
6
|
+
export { default as PublishCommand } from "./domain/PublishCommand.js";
|
|
7
|
+
export { default as SpecCommand } from "./domain/SpecCommand.js";
|
|
8
|
+
export { default as StatusCommand } from "./domain/StatusCommand.js";
|
|
1
9
|
import Release from './Release.js';
|
|
2
10
|
import ReleaseDocument from './Release/Document.js';
|
|
3
11
|
import Person from './Release/Person.js';
|
package/types/ui/cli/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/src/Team.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { HumanName, HumanGender, HumanContact } from '@nan0web/verse'
|
|
2
|
-
|
|
3
|
-
class Person {
|
|
4
|
-
/** @type {HumanName} */
|
|
5
|
-
name
|
|
6
|
-
/** @type {HumanGender} */
|
|
7
|
-
gender
|
|
8
|
-
/** @type {HumanContact[]} */
|
|
9
|
-
contacts
|
|
10
|
-
|
|
11
|
-
constructor(name) {
|
|
12
|
-
this.name = new HumanName(name)
|
|
13
|
-
this.gender = new HumanGender(-1)
|
|
14
|
-
this.contacts = []
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
class CLevelTeam {
|
|
19
|
-
static name = 'C Level'
|
|
20
|
-
static ceo = new Person(['Yaroslav', 'Wise'])
|
|
21
|
-
static cfo = new Person(['Chingis'])
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
class UXTeam {
|
|
25
|
-
static name = 'UX'
|
|
26
|
-
static designer = new Person(['Taras', 'Shevchenko'])
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
class Company {
|
|
30
|
-
static name = 'NaN•Web'
|
|
31
|
-
static c = CLevelTeam
|
|
32
|
-
static ux = UXTeam
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default Company
|
package/src/cli/index.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export default class AppCommandOptions {
|
|
2
|
-
/** @type {boolean} */
|
|
3
|
-
webui = false
|
|
4
|
-
/** @type {boolean} */
|
|
5
|
-
json = false
|
|
6
|
-
/** @type {boolean} */
|
|
7
|
-
quiet = false
|
|
8
|
-
/** @type {string} */
|
|
9
|
-
releaseDir = './releases'
|
|
10
|
-
/** @type {boolean} */
|
|
11
|
-
write = false
|
|
12
|
-
/** @type {string} */
|
|
13
|
-
user = 'anonymous'
|
|
14
|
-
/** @type {boolean} */
|
|
15
|
-
ignoreFailTests = false
|
|
16
|
-
/** @type {boolean} */
|
|
17
|
-
full = false
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @param {Object} input
|
|
21
|
-
*/
|
|
22
|
-
constructor(input = {}) {
|
|
23
|
-
Object.assign(this, input)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
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 CloseBody {
|
|
7
|
-
static version = { help: 'Release version', defaultValue: '' }
|
|
8
|
-
/** @type {string} */
|
|
9
|
-
version = ''
|
|
10
|
-
constructor({ version = '' } = {}) {
|
|
11
|
-
this.version = String(version)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default class CloseCommand extends Command {
|
|
16
|
-
static name = 'close'
|
|
17
|
-
static help = 'Close a release and move tests to src/'
|
|
18
|
-
static Body = CloseBody
|
|
19
|
-
|
|
20
|
-
/** @param {Partial<Command> & { body?: Partial<CloseBody> }} [input={}] */
|
|
21
|
-
constructor(input = {}) {
|
|
22
|
-
super(input)
|
|
23
|
-
this.body = new CloseBody(/** @type {*} */ (input))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async *run() {
|
|
27
|
-
const v = this.body.version || ''
|
|
28
|
-
|
|
29
|
-
yield new OutputMessage(`🛜 nan0release close ${v || 'all'}`)
|
|
30
|
-
|
|
31
|
-
const scanner = new Scanner(process.cwd())
|
|
32
|
-
const specs = scanner.findSpecs(v)
|
|
33
|
-
|
|
34
|
-
if (specs.length === 0) {
|
|
35
|
-
yield new OutputMessage(`No pending specs found for version ${v || 'all'}`)
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
yield new OutputMessage(`Running ${specs.length} pending specs...`)
|
|
40
|
-
|
|
41
|
-
const files = specs.map((s) => s.path)
|
|
42
|
-
await this._run('node', ['--test', ...files], 'Cannot close: Specs failed')
|
|
43
|
-
|
|
44
|
-
yield new OutputMessage(`✅ Specs passed. Closing...`)
|
|
45
|
-
|
|
46
|
-
let movedCount = 0
|
|
47
|
-
const { renameSync, mkdirSync } = await import('node:fs')
|
|
48
|
-
const { join, dirname } = await import('node:path')
|
|
49
|
-
|
|
50
|
-
for (const spec of specs) {
|
|
51
|
-
const relativePath = spec.path.substring(process.cwd().length + 1)
|
|
52
|
-
const destRelative = relativePath
|
|
53
|
-
.replace(/^releases\//, 'src/releases/')
|
|
54
|
-
.replace('.spec.js', '.test.js')
|
|
55
|
-
const destPath = join(process.cwd(), destRelative)
|
|
56
|
-
|
|
57
|
-
mkdirSync(dirname(destPath), { recursive: true })
|
|
58
|
-
renameSync(spec.path, destPath)
|
|
59
|
-
movedCount++
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
yield new OutputMessage({
|
|
63
|
-
content: [`✅ Successfully closed ${movedCount} specs and moved to src/releases/`],
|
|
64
|
-
type: OutputMessage.TYPES.SUCCESS,
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
}
|
package/src/commands/Command.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import DBFS from '@nan0web/db-fs'
|
|
2
|
-
import { OutputMessage } from '@nan0web/co'
|
|
3
|
-
import Logger from '@nan0web/log'
|
|
4
|
-
import { runSpawn } from '@nan0web/test'
|
|
5
|
-
import { UiMessage } from '@nan0web/ui'
|
|
6
|
-
|
|
7
|
-
/** @typedef {import("@nan0web/test/types/exec/runSpawn").SpawnResult} SpawnResult */
|
|
8
|
-
|
|
9
|
-
export default class Command extends UiMessage {
|
|
10
|
-
/** @type {DBFS} */
|
|
11
|
-
fs
|
|
12
|
-
/** @type {Logger} */
|
|
13
|
-
logger
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {Partial<UiMessage> & { }} [input={}]
|
|
17
|
-
*/
|
|
18
|
-
constructor(input = {}) {
|
|
19
|
-
super(input)
|
|
20
|
-
const { fs, logger } = UiMessage.parseBody(input, this.Body)
|
|
21
|
-
this.fs = fs ?? DBFS.from({})
|
|
22
|
-
this.logger = Logger.from(logger ?? {})
|
|
23
|
-
}
|
|
24
|
-
get Body() {
|
|
25
|
-
return /** @type {typeof Command} */ (this.constructor).Body
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Run the command.
|
|
29
|
-
* @param {string} cmd
|
|
30
|
-
* @param {string[]} [args=[]]
|
|
31
|
-
* @param {string} [fail=""]
|
|
32
|
-
* @returns {Promise<SpawnResult>}
|
|
33
|
-
* @throws {Error}
|
|
34
|
-
*/
|
|
35
|
-
async _run(cmd, args = [], fail = '') {
|
|
36
|
-
const result = await runSpawn(cmd, args)
|
|
37
|
-
if (0 !== result.code) {
|
|
38
|
-
this.logger.error(result.error)
|
|
39
|
-
throw new Error(fail)
|
|
40
|
-
}
|
|
41
|
-
return result
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** @returns {AsyncGenerator<OutputMessage>} */
|
|
45
|
-
async *run() {
|
|
46
|
-
throw new Error('Must be extended')
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
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,57 +0,0 @@
|
|
|
1
|
-
import { OutputMessage } from '@nan0web/co'
|
|
2
|
-
import Command from './Command.js'
|
|
3
|
-
import process from 'node:process'
|
|
4
|
-
|
|
5
|
-
class InitBody {
|
|
6
|
-
static version = { help: 'Release version', defaultValue: '' }
|
|
7
|
-
/** @type {string} */
|
|
8
|
-
version = ''
|
|
9
|
-
constructor({ version = '' } = {}) {
|
|
10
|
-
this.version = String(version)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default class InitCommand extends Command {
|
|
15
|
-
static name = 'init'
|
|
16
|
-
static help = 'Initialize a new release structure'
|
|
17
|
-
static Body = InitBody
|
|
18
|
-
|
|
19
|
-
/** @param {Partial<Command> & { body?: Partial<InitBody> }} [input={}] */
|
|
20
|
-
constructor(input = {}) {
|
|
21
|
-
super(input)
|
|
22
|
-
this.body = new InitBody(/** @type {*} */ (input))
|
|
23
|
-
}
|
|
24
|
-
|
|
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)')
|
|
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'
|
|
35
|
-
|
|
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
|
-
})
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
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
|
|
10
|
-
*/
|
|
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)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default class PublishCommand extends Command {
|
|
36
|
-
static name = 'publish'
|
|
37
|
-
static help = 'Publish npm packages'
|
|
38
|
-
static Body = PublishBody
|
|
39
|
-
|
|
40
|
-
/** @type {PublishBody} */
|
|
41
|
-
body
|
|
42
|
-
|
|
43
|
-
/** @param {Partial<Command> & { body?: Partial<PublishBody> }} [input={}] */
|
|
44
|
-
constructor(input = {}) {
|
|
45
|
-
super(input)
|
|
46
|
-
const { body = new PublishBody() } = Message.parseBody(input, PublishBody)
|
|
47
|
-
this.body = new PublishBody(body)
|
|
48
|
-
}
|
|
49
|
-
|
|
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')
|
|
61
|
-
}
|
|
62
|
-
const pkg = await this.fs.loadDocument('package.json', {})
|
|
63
|
-
const tag = `v${pkg.version}`
|
|
64
|
-
|
|
65
|
-
yield new OutputMessage(`🛜 nan0release: publishing @${pkg.name}@${pkg.version}`)
|
|
66
|
-
|
|
67
|
-
await this._run('git', ['diff', '--quiet'], 'Uncommitted changes found')
|
|
68
|
-
|
|
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')
|
|
73
|
-
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
const tagsResult = await this._run('git', ['tag'], 'Failed to get tags')
|
|
77
|
-
const tags = tagsResult.text.split('\n').filter(Boolean)
|
|
78
|
-
|
|
79
|
-
if (!tags.includes(tag)) {
|
|
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
|
-
})
|
|
89
|
-
} else {
|
|
90
|
-
yield new OutputMessage({
|
|
91
|
-
content: [`Tag ${tag} already exists`],
|
|
92
|
-
type: OutputMessage.TYPES.WARNING,
|
|
93
|
-
})
|
|
94
|
-
}
|
|
95
|
-
|
|
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')
|
|
99
|
-
|
|
100
|
-
yield new OutputMessage({
|
|
101
|
-
content: [`${pkg.name}@${pkg.version} published.`],
|
|
102
|
-
type: OutputMessage.TYPES.SUCCESS,
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import DBFS from '@nan0web/db-fs'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ReleaseDB – thin wrapper around DBFS that adds a convenient `releases`
|
|
5
|
-
* getter returning all release directories discovered under the root.
|
|
6
|
-
*
|
|
7
|
-
* The underlying DBFS stores a `Map` called `data` where each key is a
|
|
8
|
-
* relative file path (e.g. "1/0/v1.0.0/release.js").
|
|
9
|
-
*/
|
|
10
|
-
class ReleaseDB extends DBFS {
|
|
11
|
-
/**
|
|
12
|
-
* @param {string} uri
|
|
13
|
-
* @returns {ReleaseDB}
|
|
14
|
-
*/
|
|
15
|
-
extract(uri) {
|
|
16
|
-
return new ReleaseDB(super.extract(uri))
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* @param {string} version
|
|
20
|
-
* @returns {ReleaseDB}
|
|
21
|
-
*/
|
|
22
|
-
extractVersion(version) {
|
|
23
|
-
const found = version.match(/^v(\d+)\.(\d+)\.(\d+)$/)
|
|
24
|
-
if (!found) {
|
|
25
|
-
throw new Error('Incorrect version')
|
|
26
|
-
}
|
|
27
|
-
const arr = version.slice(1).split('.')
|
|
28
|
-
const path = [arr[0], arr[1], version, ''].join('/')
|
|
29
|
-
return ReleaseDB.from(this.extract(path))
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* @returns {Array<string>} List of discovered release root paths
|
|
33
|
-
*/
|
|
34
|
-
get releases() {
|
|
35
|
-
const paths = new Set()
|
|
36
|
-
for (const key of this.data.keys()) {
|
|
37
|
-
// Expected structure: major/minor/vX.Y.Z/…
|
|
38
|
-
const parts = key.split('/')
|
|
39
|
-
if (parts.length > 2 && /^\d+$/.test(parts[0]) && /^\d+$/.test(parts[1])) {
|
|
40
|
-
paths.add(`${parts[0]}/${parts[1]}/${parts[2]}`)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return Array.from(paths)
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* @param {object} input
|
|
47
|
-
* @returns {ReleaseDB}
|
|
48
|
-
*/
|
|
49
|
-
static from(input) {
|
|
50
|
-
if (input instanceof ReleaseDB) return input
|
|
51
|
-
return new ReleaseDB(input)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
export default ReleaseDB
|