Skip to content

Commit 57851b0

Browse files
fix: changelong (#349)
1 parent 9b8ffd2 commit 57851b0

File tree

6 files changed

+166
-11
lines changed

6 files changed

+166
-11
lines changed

.github/workflows/create-release.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,26 @@ jobs:
1818
with:
1919
fetch-depth: 0
2020

21+
- name: 🟢 Setup Node.js
22+
uses: actions/setup-node@v3
23+
with:
24+
node-version: 24.9.0
25+
26+
- name: 📦 Setup pnpm
27+
uses: pnpm/action-setup@v4
28+
with:
29+
version: 9.14.4
30+
31+
- name: ✨ Install dependencies
32+
run: pnpm install --frozen-lockfile
33+
2134
- name: 📝 Extract version from tag
2235
id: version
2336
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
2437

38+
- name: 🔄 Regenerate changelog to include all commits
39+
run: pnpm changelog
40+
2541
- name: 📄 Extract changelog for this version
2642
id: changelog
2743
run: |

libs/tools/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"build": "pnpm run build.lib & pnpm run build.types && pnpm generate.icon.types",
3535
"build.lib": "rolldown -c rolldown.config.ts",
3636
"build.types": "tsc --emitDeclarationOnly --outDir ./lib-types",
37-
"generate.icon.types": "node src/generate-icon-types.ts"
37+
"generate.icon.types": "node src/generate/icon-types.ts"
3838
},
3939
"devDependencies": {
4040
"@iconify/types": "^2",
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { execSync } from "node:child_process";
2+
import { readFileSync } from "node:fs";
3+
import { describe, expect, it } from "vitest";
4+
5+
function getLatestTag(): string | null {
6+
try {
7+
const tags = execSync("git tag --sort=-version:refname", { encoding: "utf-8" })
8+
.trim()
9+
.split("\n")
10+
.filter(Boolean);
11+
return tags.find(Boolean) || null;
12+
} catch {
13+
return null;
14+
}
15+
}
16+
17+
function getCommitsBetweenTags(fromTag: string | null, toRef = "HEAD"): string[] {
18+
if (!fromTag) {
19+
return [];
20+
}
21+
try {
22+
const commits = execSync(`git log --oneline --no-merges ${fromTag}..${toRef}`, {
23+
encoding: "utf-8"
24+
})
25+
.trim()
26+
.split("\n")
27+
.filter(Boolean);
28+
return commits;
29+
} catch {
30+
return [];
31+
}
32+
}
33+
34+
function extractChangelogSection(changelogContent: string): string {
35+
const lines = changelogContent.split("\n");
36+
const firstSection: string[] = [];
37+
let inSection = false;
38+
let sectionCount = 0;
39+
40+
for (const line of lines) {
41+
if (line.startsWith("## ")) {
42+
sectionCount++;
43+
if (sectionCount === 1) {
44+
inSection = true;
45+
continue;
46+
} else if (sectionCount === 2) {
47+
break;
48+
}
49+
}
50+
if (inSection) {
51+
firstSection.push(line);
52+
}
53+
}
54+
55+
return firstSection.join("\n");
56+
}
57+
58+
function extractPRNumbers(changelogSection: string): string[] {
59+
const prRegex = /\[#(\d+)\]/g;
60+
const matches: string[] = [];
61+
let match: RegExpExecArray | null;
62+
while ((match = prRegex.exec(changelogSection)) !== null) {
63+
if (match[1]) {
64+
matches.push(match[1]);
65+
}
66+
}
67+
return matches;
68+
}
69+
70+
describe("changelog generation", () => {
71+
it("should include all commits with PRs in the changelog", () => {
72+
const latestTag = getLatestTag();
73+
74+
// Skip test if no tags exist
75+
if (!latestTag) {
76+
console.log("⚠️ No tags found. Skipping test.");
77+
return;
78+
}
79+
80+
const commits = getCommitsBetweenTags(latestTag);
81+
82+
// Skip test if no commits since last tag
83+
if (commits.length === 0) {
84+
console.log("✅ No commits since last tag. Test passed.");
85+
return;
86+
}
87+
88+
// Generate changelog
89+
try {
90+
execSync("pnpm changelog", { stdio: "inherit" });
91+
} catch (error) {
92+
throw new Error(`Failed to generate changelog: ${(error as Error).message}`, {
93+
cause: error
94+
});
95+
}
96+
97+
// Read generated changelog
98+
let changelogContent: string;
99+
try {
100+
changelogContent = readFileSync("CHANGELOG.md", "utf-8");
101+
} catch (error) {
102+
throw new Error(`Failed to read CHANGELOG.md: ${(error as Error).message}`, {
103+
cause: error
104+
});
105+
}
106+
107+
const firstSection = extractChangelogSection(changelogContent);
108+
const prNumbers = extractPRNumbers(firstSection);
109+
110+
// Check if commits with PR numbers are included
111+
const commitsWithPRs = commits.filter((commit) => /\(#\d+\)/.test(commit));
112+
113+
if (commitsWithPRs.length > 0) {
114+
const prsInCommits = commitsWithPRs
115+
.map((commit) => {
116+
const match = commit.match(/\(#(\d+)\)/);
117+
return match && match[1] ? match[1] : null;
118+
})
119+
.filter((pr): pr is string => pr !== null);
120+
121+
const missingPRs = prsInCommits.filter((pr) => !prNumbers.includes(pr));
122+
123+
if (missingPRs.length > 0) {
124+
throw new Error(
125+
`Missing PRs in changelog: ${missingPRs.join(", ")}. ` +
126+
`Expected PRs: ${prsInCommits.join(", ")}. ` +
127+
`Found PRs: ${prNumbers.join(", ")}`
128+
);
129+
}
130+
}
131+
132+
// Verify the changelog section exists and has content
133+
expect(firstSection.trim()).not.toBe("");
134+
});
135+
});
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { fileURLToPath } from "node:url";
88
export { sanitizeIconName, generateIconTypes, generateRuntimeProxies };
99

1010
// Import shared utilities
11-
import { discoverAllIconifyCollections, toPascalCase } from "../utils/icons/naming.ts";
11+
import { discoverAllIconifyCollections, toPascalCase } from "../../utils/icons/naming.ts";
1212

1313
// Export shared utilities for convenience
1414
export { discoverAllIconifyCollections, toPascalCase };
@@ -112,9 +112,12 @@ async function generateIconTypes(packs?: Record<string, { iconifyPrefix: string
112112

113113
const libTypesPath = join(
114114
scriptDir,
115-
"../../components/lib-types/virtual-qds-icons.d.ts"
115+
"../../../components/lib-types/virtual-qds-icons.d.ts"
116+
);
117+
const componentsRootPath = join(
118+
scriptDir,
119+
"../../../components/virtual-qds-icons.d.ts"
116120
);
117-
const componentsRootPath = join(scriptDir, "../../components/virtual-qds-icons.d.ts");
118121

119122
const libTypesDir = dirname(libTypesPath);
120123
if (!existsSync(libTypesDir)) {
@@ -151,7 +154,7 @@ async function generateRuntimeProxies(
151154

152155
const packsToUse = packs || discoverAllIconifyCollections();
153156
const scriptDir = dirname(fileURLToPath(import.meta.url));
154-
const defaultOutputPath = join(scriptDir, "../../components/src/icons-runtime.ts");
157+
const defaultOutputPath = join(scriptDir, "../../../components/src/icons-runtime.ts");
155158
const finalOutputPath = outputPath || defaultOutputPath;
156159
const declarations: string[] = [];
157160

libs/tools/src/generate-icon-types.unit.ts renamed to libs/tools/src/generate/icon-types.unit.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { describe, expect, it, vi } from "vitest";
2-
import { sanitizeIconName } from "./generate-icon-types";
2+
import { sanitizeIconName } from "./icon-types";
33

44
// Mock fs module
55
vi.mock("node:fs", () => ({
66
writeFileSync: vi.fn()
77
}));
88

9-
describe("generate-icon-types", () => {
9+
describe("icon-types", () => {
1010
describe("sanitizeIconName", () => {
1111
it("should convert kebab-case to PascalCase", () => {
1212
expect(sanitizeIconName("a-arrow-down")).toBe("AArrowDown");
@@ -35,22 +35,22 @@ describe("generate-icon-types", () => {
3535

3636
describe("Icon Runtime Proxies Integration", () => {
3737
it("should be able to import Lucide namespace from runtime", async () => {
38-
const { Lucide } = await import("../../components/src/icons-runtime");
38+
const { Lucide } = await import("../../../components/src/icons-runtime");
3939
expect(Lucide).toBeDefined();
4040
});
4141

4242
it("should be able to import Heroicons namespace from runtime", async () => {
43-
const { Heroicons } = await import("../../components/src/icons-runtime");
43+
const { Heroicons } = await import("../../../components/src/icons-runtime");
4444
expect(Heroicons).toBeDefined();
4545
});
4646

4747
it("should be able to import Tabler namespace from runtime", async () => {
48-
const { Tabler } = await import("../../components/src/icons-runtime");
48+
const { Tabler } = await import("../../../components/src/icons-runtime");
4949
expect(Tabler).toBeDefined();
5050
});
5151

5252
it("should have proxy objects for icon namespaces", async () => {
53-
const { Lucide } = await import("../../components/src/icons-runtime");
53+
const { Lucide } = await import("../../../components/src/icons-runtime");
5454
// The proxy object should be defined and be an object
5555
expect(typeof Lucide).toBe("object");
5656
expect(Lucide).not.toBeNull();

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"changelog": "changelogen --output CHANGELOG.md --no-commit --no-tag",
1919
"publish": "pnpm --filter '@qds.dev/*' publish --access public --no-git-checks",
2020
"release": "bumpp package.json libs/*/package.json --minor --execute \"pnpm changelog\" --all --commit \"chore: release v%s\" --tag \"v%s\" --push && pnpm run publish",
21+
"test:changelog": "vitest run libs/tools/src/generate/changelog.unit.ts",
2122
"create-linear-issue": "node .github/scripts/update-linear.ts"
2223
},
2324
"devDependencies": {

0 commit comments

Comments
 (0)