@spec-wave/cli 0.5.1 → 0.5.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/package.json
CHANGED
|
@@ -75,6 +75,7 @@ export async function generatePlan({ issueNumber }) {
|
|
|
75
75
|
git(`git config user.name "spec-wave[bot]"`);
|
|
76
76
|
git(`git add "${filePath}"`);
|
|
77
77
|
git(`git commit -m "docs: generate plan.md for ${slug} [spec-wave]"`);
|
|
78
|
+
git('git pull --rebase');
|
|
78
79
|
git('git push');
|
|
79
80
|
|
|
80
81
|
// Remove trigger label
|
|
@@ -68,6 +68,7 @@ export async function generateSpec({ issueNumber }) {
|
|
|
68
68
|
git(`git config user.name "spec-wave[bot]"`);
|
|
69
69
|
git(`git add "${filePath}"`);
|
|
70
70
|
git(`git commit -m "docs: generate spec.md for ${slug} [spec-wave]"`);
|
|
71
|
+
git('git pull --rebase');
|
|
71
72
|
git('git push');
|
|
72
73
|
|
|
73
74
|
// Remove trigger label
|
|
@@ -2,54 +2,9 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { resolveToken } from '../api/auth.mjs';
|
|
4
4
|
import { getIssue, removeLabel, addLabel, commentOnIssue } from '../api/github-rest.mjs';
|
|
5
|
-
import { addProjectItem, setItemSingleSelect, getSingleSelectField } from '../api/github-graphql.mjs';
|
|
6
5
|
import { slugify } from '../lib/slugify.mjs';
|
|
7
6
|
import { CONFIG_FILE, REQUIRED_PLAN_SECTIONS, REQUIRED_SPEC_SECTIONS } from '../config.mjs';
|
|
8
7
|
|
|
9
|
-
// Opção nativa do campo Status do GitHub Projects (Todo / In Progress / Done).
|
|
10
|
-
const DONE_STAGE = 'Done';
|
|
11
|
-
|
|
12
|
-
// Resolve um campo SINGLE_SELECT pelo nome: usa o .spec-wave.json, cai para o
|
|
13
|
-
// formato legado (etapaFieldId/stageOptions) e, por fim, consulta o Project.
|
|
14
|
-
async function resolveField(token, project, name) {
|
|
15
|
-
if (project.fields && project.fields[name]) return project.fields[name];
|
|
16
|
-
if (name === 'Etapa' && project.etapaFieldId) {
|
|
17
|
-
return { id: project.etapaFieldId, options: project.stageOptions || {} };
|
|
18
|
-
}
|
|
19
|
-
return await getSingleSelectField(token, project.id, name);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Move o item da issue para a Etapa "🎉 Done" no board. Best-effort: loga e
|
|
23
|
-
// segue se o .spec-wave.json não tiver o Project ou o campo não for encontrado.
|
|
24
|
-
async function moveToDone(token, issue) {
|
|
25
|
-
const configPath = path.join(process.cwd(), CONFIG_FILE);
|
|
26
|
-
if (!existsSync(configPath)) {
|
|
27
|
-
console.warn(`${CONFIG_FILE} não encontrado — status do board não atualizado.`);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
let project;
|
|
31
|
-
try {
|
|
32
|
-
project = (JSON.parse(readFileSync(configPath, 'utf-8')).project) || {};
|
|
33
|
-
} catch (err) {
|
|
34
|
-
console.warn(`${CONFIG_FILE} corrompido (${err.message}) — status do board não atualizado.`);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (!project.id) {
|
|
38
|
-
console.warn(`Project não configurado no ${CONFIG_FILE} — status do board não atualizado.`);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
// addProjectItem é idempotente: retorna o item existente se a issue já está no board.
|
|
42
|
-
const itemId = await addProjectItem(token, project.id, issue.node_id);
|
|
43
|
-
const field = await resolveField(token, project, 'Status');
|
|
44
|
-
const optionId = field?.options?.[DONE_STAGE];
|
|
45
|
-
if (field?.id && optionId) {
|
|
46
|
-
await setItemSingleSelect(token, project.id, itemId, field.id, optionId);
|
|
47
|
-
console.log(`Status do board atualizado para "${DONE_STAGE}".`);
|
|
48
|
-
} else {
|
|
49
|
-
console.warn(`Etapa "${DONE_STAGE}" não encontrada no Project — status do board não atualizado.`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
8
|
export async function validate({ issueNumber }) {
|
|
54
9
|
const token = await resolveToken();
|
|
55
10
|
const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY || '').split('/');
|
|
@@ -114,21 +69,14 @@ export async function validate({ issueNumber }) {
|
|
|
114
69
|
process.exit(1);
|
|
115
70
|
}
|
|
116
71
|
|
|
117
|
-
// Validação passou:
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
await moveToDone(token, issue);
|
|
121
|
-
doneOk = !!DONE_STAGE;
|
|
122
|
-
} catch (err) {
|
|
123
|
-
console.warn(`Falha ao atualizar status do board: ${err.message}`);
|
|
124
|
-
}
|
|
72
|
+
// Validação passou: adiciona label plan-approved.
|
|
73
|
+
await addLabel(token, owner, repo, parseInt(issueNumber, 10), 'spec-wave:plan-approved');
|
|
125
74
|
|
|
126
75
|
await commentOnIssue(
|
|
127
76
|
token, owner, repo, parseInt(issueNumber, 10),
|
|
128
77
|
`✅ **Validação concluída com sucesso!**\n\n` +
|
|
129
78
|
`- [\`${specPath}\`](${specPath}) ✓\n` +
|
|
130
79
|
`- [\`${planPath}\`](${planPath}) ✓\n\n` +
|
|
131
|
-
(doneOk ? `Status movido para **${DONE_STAGE}**. ` : '') +
|
|
132
80
|
`A Feature está pronta para decomposição. Use:\n` +
|
|
133
81
|
`\`\`\`\ngh issue edit ${issueNumber} --add-label "spec-wave:decompose"\n\`\`\``
|
|
134
82
|
);
|
package/src/config.mjs
CHANGED
|
@@ -129,10 +129,11 @@ export const PRIORITY_LABELS = [
|
|
|
129
129
|
];
|
|
130
130
|
|
|
131
131
|
export const TRIGGER_LABELS = [
|
|
132
|
-
{ name: 'spec-wave:spec',
|
|
133
|
-
{ name: 'spec-wave:plan',
|
|
134
|
-
{ name: 'spec-wave:ready',
|
|
135
|
-
{ name: 'spec-wave:
|
|
132
|
+
{ name: 'spec-wave:spec', color: 'BFD4F2', description: 'Gerar spec.md via GitHub Action' },
|
|
133
|
+
{ name: 'spec-wave:plan', color: 'BFD4F2', description: 'Gerar plan.md via GitHub Action' },
|
|
134
|
+
{ name: 'spec-wave:ready', color: '0E8A16', description: 'Validar spec+plan e mover para Ready' },
|
|
135
|
+
{ name: 'spec-wave:plan-approved', color: '0E8A16', description: 'Spec+plan validados com sucesso' },
|
|
136
|
+
{ name: 'spec-wave:decompose', color: 'BFD4F2', description: 'Decompor em Stories e Tasks' },
|
|
136
137
|
];
|
|
137
138
|
|
|
138
139
|
export const ALL_LABELS = [...TYPE_LABELS, ...PRIORITY_LABELS, ...TRIGGER_LABELS];
|