mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
chore: drop Project v2 from the process, the docs and the release script
The owner stopped using GitHub Projects. Most of this is wording, but one part was not: release-prerelease.mjs talked to the Project in code. finishIssues looked up the project id, listed its items and its Status=Done option, and threw when an issue was missing from the board — so the first release that closed an issue would have died on a step with nothing to do with publishing. Found by reading rather than by releasing, which was luck. Closing issues stays, and now strips the status label first. That order is not cosmetic: the invariant that a closed issue carries no status label has broken twice already, both times because a manual step did it the other way round. The close-merged job already does it in this order. The documents now say labels and only labels. The explicit "no longer used" lines are kept on purpose, in PROCESS.md and next to the code that used to sync: a decision that vanishes quietly gets reintroduced a month later by someone who never knew it was made. Issue: #139 User-Visible: no
This commit is contained in:
@@ -30,7 +30,7 @@ export function parsePrereleaseArgs(args) {
|
||||
const values = new Map();
|
||||
const switches = new Set();
|
||||
const positionals = [];
|
||||
const valueNames = new Set(['repo', 'branch', 'project', 'issues']);
|
||||
const valueNames = new Set(['repo', 'branch', 'issues']);
|
||||
const switchNames = new Set(['check', 'yes']);
|
||||
for (const arg of args) {
|
||||
if (!arg.startsWith('--')) { positionals.push(arg); continue; }
|
||||
@@ -53,7 +53,6 @@ export function parsePrereleaseArgs(args) {
|
||||
tag: positionals[0],
|
||||
repo: values.get('repo') || 'Matysh/houseplan-card',
|
||||
branch: values.get('branch') || 'dev',
|
||||
projectNumber: values.get('project') || '1',
|
||||
issueOption: values.get('issues') || '',
|
||||
checkOnly: switches.has('check'),
|
||||
confirmed: switches.has('yes'),
|
||||
@@ -184,7 +183,7 @@ const invokedDirectly = process.argv[1]
|
||||
if (invokedDirectly) {
|
||||
try {
|
||||
const {
|
||||
tag, repo, branch, projectNumber, issueOption, checkOnly, confirmed,
|
||||
tag, repo, branch, issueOption, checkOnly, confirmed,
|
||||
} = parsePrereleaseArgs(process.argv.slice(2));
|
||||
let issues = [];
|
||||
const root = process.cwd();
|
||||
@@ -400,39 +399,30 @@ if (invokedDirectly) {
|
||||
}
|
||||
};
|
||||
|
||||
// Project v2 больше не используется (решение владельца 2026-08-14). Раньше
|
||||
// здесь выяснялся id проекта, список его элементов и опция Status=Done, а
|
||||
// отсутствие задачи в проекте роняло публикацию. Статус живёт в метках
|
||||
// (PROCESS.md §9), и релизу нечего синхронизировать: он закрывает issue и
|
||||
// снимает статусную метку, как это делает job close-merged (#120).
|
||||
const finishIssues = (releaseUrl) => {
|
||||
if (!issues.length) return;
|
||||
const projectInfo = ghJson([
|
||||
'project', 'view', projectNumber, '--owner', owner, '--format', 'json',
|
||||
]);
|
||||
const project = ghJson([
|
||||
'project', 'item-list', projectNumber, '--owner', owner, '--limit', '1000', '--format', 'json',
|
||||
]);
|
||||
const fields = ghJson(['project', 'field-list', projectNumber, '--owner', owner, '--format', 'json']);
|
||||
const statusField = fields.fields.find((field) => field.name === 'Status');
|
||||
const done = statusField?.options?.find((entry) => entry.name === 'Done');
|
||||
if (!statusField || !done) throw new Error(`Project ${owner}/${projectNumber} has no Status=Done option`);
|
||||
const items = new Map(project.items
|
||||
.filter((item) => item.content?.repository === repo && issues.includes(item.content?.number))
|
||||
.map((item) => [item.content.number, item]));
|
||||
const missing = issues.filter((issue) => !items.has(issue));
|
||||
if (missing.length) throw new Error(`Issues are missing from Project ${projectNumber}: ${missing.join(', ')}`);
|
||||
|
||||
for (const issue of issues) {
|
||||
const row = ghJson(['issue', 'view', String(issue), '--repo', repo, '--json', 'state']);
|
||||
const row = ghJson(['issue', 'view', String(issue), '--repo', repo, '--json', 'state,labels']);
|
||||
if (row.state === 'OPEN') {
|
||||
const status = (row.labels || [])
|
||||
.map((label) => label.name)
|
||||
.filter((name) => /^S\d-/.test(name));
|
||||
// Метка снимается ДО закрытия: инвариант «закрытый issue не несёт
|
||||
// статусных меток» ломался уже дважды, и оба раза из-за обратного
|
||||
// порядка в ручном шаге.
|
||||
for (const name of status) {
|
||||
run('gh', ['issue', 'edit', String(issue), '--repo', repo, '--remove-label', name]);
|
||||
}
|
||||
run('gh', [
|
||||
'issue', 'close', String(issue), '--repo', repo, '--reason', 'completed',
|
||||
'--comment', `Реализовано и опубликовано в [${tag}](${releaseUrl}).`,
|
||||
], { inherit: true });
|
||||
}
|
||||
const item = items.get(issue);
|
||||
if (item.status !== 'Done') {
|
||||
run('gh', [
|
||||
'project', 'item-edit', '--id', item.id, '--project-id', projectInfo.id,
|
||||
'--field-id', statusField.id, '--single-select-option-id', done.id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user