quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 6s
quality / changes (push) Successful in 4s
quality / docker-check (push) Skipped
quality / web (push) Successful in 57s
quality / api (push) Successful in 41s
CD / quality (push) Successful in 1m46s
CD / publish (push) Successful in 11m48s
Без type/scope semantic-release не ставил тег и пропускал bake.
89 lines
2.5 KiB
JavaScript
89 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Warns about commits since the last tag that semantic-release cannot parse.
|
|
* Exit 0 always — semantic-release still decides release/no-op.
|
|
*/
|
|
import { execSync } from 'node:child_process';
|
|
import { CommitParser } from 'conventional-commits-parser';
|
|
|
|
const parser = new CommitParser();
|
|
|
|
const RELEASABLE = new Set(['feat', 'fix', 'perf', 'ci', 'refactor']);
|
|
|
|
function lastTag() {
|
|
try {
|
|
return execSync('git describe --tags --abbrev=0', { encoding: 'utf8' }).trim();
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function commitsSince(ref) {
|
|
const range = ref ? `${ref}..HEAD` : 'HEAD';
|
|
const out = execSync(`git log ${range} --format=%H%x09%s`, { encoding: 'utf8' }).trim();
|
|
if (!out) return [];
|
|
return out.split('\n').map((line) => {
|
|
const [hash, subject] = line.split('\t');
|
|
return { hash: hash.trim(), subject: subject.trim() };
|
|
});
|
|
}
|
|
|
|
const tag = lastTag();
|
|
const commits = commitsSince(tag);
|
|
const unparseable = [];
|
|
const releasable = [];
|
|
|
|
for (const { hash, subject } of commits) {
|
|
let parsed;
|
|
try {
|
|
parsed = parser.parse(subject);
|
|
} catch {
|
|
unparseable.push({ hash: hash.slice(0, 7), subject });
|
|
continue;
|
|
}
|
|
if (!parsed?.type) {
|
|
unparseable.push({ hash: hash.slice(0, 7), subject });
|
|
continue;
|
|
}
|
|
// semantic-release expects lowercase types (feat|fix|…); "Refactor: …" is not releasable.
|
|
if (parsed.type !== parsed.type.toLowerCase()) {
|
|
unparseable.push({ hash: hash.slice(0, 7), subject });
|
|
continue;
|
|
}
|
|
if (RELEASABLE.has(parsed.type)) {
|
|
releasable.push({ hash: hash.slice(0, 7), subject, type: parsed.type });
|
|
}
|
|
}
|
|
|
|
if (commits.length === 0) {
|
|
console.log(`No new commits since ${tag || 'initial'}.`);
|
|
process.exit(0);
|
|
}
|
|
|
|
if (unparseable.length > 0) {
|
|
console.warn('::warning:: Commits not parseable by semantic-release (no version bump):');
|
|
for (const b of unparseable) {
|
|
console.warn(` ${b.hash} ${b.subject}`);
|
|
}
|
|
console.warn('Fix: single scope without commas, e.g. refactor(web): summary');
|
|
}
|
|
|
|
if (releasable.length > 0) {
|
|
console.log(`Releasable since ${tag || 'initial'}: ${releasable.length} commit(s).`);
|
|
for (const r of releasable) {
|
|
console.log(` ${r.hash} ${r.type} ${r.subject}`);
|
|
}
|
|
} else {
|
|
console.warn('::warning:: No releasable commits since last tag — release job will no-op.');
|
|
console.warn(
|
|
'Need at least one: feat|fix|perf|ci|refactor with Conventional Commits, e.g. feat(fleet): …',
|
|
);
|
|
if (!tag) {
|
|
console.warn(
|
|
'::warning:: No git tags yet — first release (v1.0.0) also requires a releasable commit on HEAD history.',
|
|
);
|
|
}
|
|
}
|
|
|
|
process.exit(0);
|