Skip to content

Commit cd0d2f5

Browse files
pngwnabidlabs
andauthored
Add changelog generation and automatic versioning and publishing. (#271)
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
1 parent ce2aaf1 commit cd0d2f5

15 files changed

Lines changed: 1632 additions & 35 deletions

.changeset/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Changesets
2+
3+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4+
with multi-package repos, or single-package repos to help you version and publish your code. You can
5+
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6+
7+
We have a quick list of common questions to get you started engaging with this project in
8+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

.changeset/changeset.cjs

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
const { getPackagesSync } = require("@manypkg/get-packages");
2+
const dependents_graph = require("@changesets/get-dependents-graph");
3+
4+
const gh = require("@changesets/get-github-info");
5+
const { existsSync, readFileSync, writeFileSync } = require("fs");
6+
const { join } = require("path");
7+
8+
const { getInfo, getInfoFromPullRequest } = gh;
9+
const pkg_data = getPackagesSync(process.cwd());
10+
const { packages, rootDir } = pkg_data;
11+
const dependents = dependents_graph.getDependentsGraph({
12+
packages,
13+
root: pkg_data.rootPackage
14+
});
15+
16+
/**
17+
* @typedef {{packageJson: {name: string, python?: boolean}, dir: string}} Package
18+
*/
19+
20+
/**
21+
* @typedef {{summary: string, id: string, commit: string, releases: {name: string}}} Changeset
22+
*/
23+
24+
/**
25+
*
26+
* @param {string} package_name The name of the package to find the directories for
27+
* @returns {string[]} The directories for the package
28+
*/
29+
function find_packages_dirs(package_name) {
30+
/** @type {string[]} */
31+
let package_dirs = [];
32+
33+
/** @type {Package | undefined} */
34+
const _package = packages.find((p) => p.packageJson.name === package_name);
35+
if (!_package) throw new Error(`Package ${package_name} not found`);
36+
37+
package_dirs.push(_package.dir);
38+
if (_package.packageJson.python) {
39+
package_dirs.push(join(_package.dir, ".."));
40+
}
41+
return package_dirs;
42+
}
43+
44+
let lines = {
45+
_handled: []
46+
};
47+
48+
const changelogFunctions = {
49+
/**
50+
*
51+
* @param {Changeset[]} changesets The changesets that have been created
52+
* @param {any} dependenciesUpdated The dependencies that have been updated
53+
* @param {any} options The options passed to the changelog generator
54+
* @returns {Promise<string>} The release line for the dependencies
55+
*/
56+
getDependencyReleaseLine: async (
57+
changesets,
58+
dependenciesUpdated,
59+
options
60+
) => {
61+
if (!options.repo) {
62+
throw new Error(
63+
'Please provide a repo to this changelog generator like this:\n"changelog": ["@changesets/changelog-github", { "repo": "org/repo" }]'
64+
);
65+
}
66+
if (dependenciesUpdated.length === 0) return "";
67+
68+
const changesetLink = `- Updated dependencies [${(
69+
await Promise.all(
70+
changesets.map(async (cs) => {
71+
if (cs.commit) {
72+
let { links } = await getInfo({
73+
repo: options.repo,
74+
commit: cs.commit
75+
});
76+
return links.commit;
77+
}
78+
})
79+
)
80+
)
81+
.filter((_) => _)
82+
.join(", ")}]:`;
83+
84+
const updatedDepenenciesList = dependenciesUpdated.map(
85+
/**
86+
*
87+
* @param {any} dependency The dependency that has been updated
88+
* @returns {string} The formatted dependency
89+
*/
90+
(dependency) => {
91+
const updates = dependents.get(dependency.name);
92+
93+
if (updates && updates.length > 0) {
94+
updates.forEach((update) => {
95+
if (!lines[update]) {
96+
lines[update] = {
97+
dirs: find_packages_dirs(update),
98+
current_changelog: "",
99+
feat: [],
100+
fix: [],
101+
highlight: [],
102+
previous_version: packages.find(
103+
(p) => p.packageJson.name === update
104+
).packageJson.version,
105+
dependencies: []
106+
};
107+
108+
const changelog_path = join(
109+
//@ts-ignore
110+
lines[update].dirs[1] || lines[update].dirs[0],
111+
"CHANGELOG.md"
112+
);
113+
114+
if (existsSync(changelog_path)) {
115+
//@ts-ignore
116+
lines[update].current_changelog = readFileSync(
117+
changelog_path,
118+
"utf-8"
119+
)
120+
.replace(`# ${update}`, "")
121+
.trim();
122+
}
123+
}
124+
lines[update].dependencies.push(
125+
` - ${dependency.name}@${dependency.newVersion}`
126+
);
127+
});
128+
}
129+
130+
return ` - ${dependency.name}@${dependency.newVersion}`;
131+
}
132+
);
133+
134+
writeFileSync(
135+
join(rootDir, ".changeset", "_changelog.json"),
136+
JSON.stringify(lines, null, 2)
137+
);
138+
139+
return [changesetLink, ...updatedDepenenciesList].join("\n");
140+
},
141+
/**
142+
*
143+
* @param {{summary: string, id: string, commit: string, releases: {name: string}[]}} changeset The changeset that has been created
144+
* @param {any} type The type of changeset
145+
* @param {any} options The options passed to the changelog generator
146+
* @returns {Promise<string>} The release line for the changeset
147+
*/
148+
getReleaseLine: async (changeset, type, options) => {
149+
if (!options || !options.repo) {
150+
throw new Error(
151+
'Please provide a repo to this changelog generator like this:\n"changelog": ["@changesets/changelog-github", { "repo": "org/repo" }]'
152+
);
153+
}
154+
155+
let prFromSummary;
156+
let commitFromSummary;
157+
/**
158+
* @type {string[]}
159+
*/
160+
let usersFromSummary = [];
161+
162+
const replacedChangelog = changeset.summary
163+
.replace(/^\s*(?:pr|pull|pull\s+request):\s*#?(\d+)/im, (_, pr) => {
164+
let num = Number(pr);
165+
if (!isNaN(num)) prFromSummary = num;
166+
return "";
167+
})
168+
.replace(/^\s*commit:\s*([^\s]+)/im, (_, commit) => {
169+
commitFromSummary = commit;
170+
return "";
171+
})
172+
.replace(/^\s*(?:author|user):\s*@?([^\s]+)/gim, (_, user) => {
173+
usersFromSummary.push(user);
174+
return "";
175+
})
176+
.trim();
177+
178+
const [firstLine, ...futureLines] = replacedChangelog
179+
.split("\n")
180+
.map((l) => l.trimRight());
181+
182+
const links = await (async () => {
183+
if (prFromSummary !== undefined) {
184+
let { links } = await getInfoFromPullRequest({
185+
repo: options.repo,
186+
pull: prFromSummary
187+
});
188+
if (commitFromSummary) {
189+
links = {
190+
...links,
191+
commit: `[\`${commitFromSummary}\`](https://github.com/${options.repo}/commit/${commitFromSummary})`
192+
};
193+
}
194+
return links;
195+
}
196+
const commitToFetchFrom = commitFromSummary || changeset.commit;
197+
if (commitToFetchFrom) {
198+
let { links } = await getInfo({
199+
repo: options.repo,
200+
commit: commitToFetchFrom
201+
});
202+
return links;
203+
}
204+
return {
205+
commit: null,
206+
pull: null,
207+
user: null
208+
};
209+
})();
210+
211+
const user_link = /\[(@[^]+)\]/.exec(links.user);
212+
const users =
213+
usersFromSummary && usersFromSummary.length
214+
? usersFromSummary
215+
.map((userFromSummary) => `@${userFromSummary}`)
216+
.join(", ")
217+
: user_link
218+
? user_link[1]
219+
: links.user;
220+
221+
const prefix = [
222+
links.pull === null ? "" : `${links.pull}`,
223+
links.commit === null ? "" : `${links.commit}`
224+
]
225+
.join(" ")
226+
.trim();
227+
228+
const suffix = users === null ? "" : ` Thanks ${users}!`;
229+
230+
/**
231+
* @typedef {{[key: string]: string[] | {dirs: string[], current_changelog: string, feat: {summary: string}[], fix: {summary: string}[], highlight: {summary: string}[]}}} ChangesetMeta
232+
*/
233+
234+
/**
235+
* @type { ChangesetMeta & { _handled: string[] } }}
236+
*/
237+
238+
if (lines._handled.includes(changeset.id)) {
239+
return "done";
240+
}
241+
lines._handled.push(changeset.id);
242+
243+
changeset.releases.forEach((release) => {
244+
if (!lines[release.name]) {
245+
lines[release.name] = {
246+
dirs: find_packages_dirs(release.name),
247+
current_changelog: "",
248+
feat: [],
249+
fix: [],
250+
highlight: [],
251+
previous_version: packages.find(
252+
(p) => p.packageJson.name === release.name
253+
).packageJson.version,
254+
dependencies: []
255+
};
256+
}
257+
258+
const changelog_path = join(
259+
//@ts-ignore
260+
lines[release.name].dirs[1] || lines[release.name].dirs[0],
261+
"CHANGELOG.md"
262+
);
263+
264+
if (existsSync(changelog_path)) {
265+
//@ts-ignore
266+
lines[release.name].current_changelog = readFileSync(
267+
changelog_path,
268+
"utf-8"
269+
)
270+
.replace(`# ${release.name}`, "")
271+
.trim();
272+
}
273+
274+
const [, _type, summary] = changeset.summary
275+
.trim()
276+
.match(/^(feat|fix|highlight)\s*:\s*([^]*)/im) || [
277+
,
278+
"feat",
279+
changeset.summary
280+
];
281+
282+
let formatted_summary = "";
283+
284+
if (_type === "highlight") {
285+
const [heading, ...rest] = summary.trim().split("\n");
286+
const _heading = `${heading} ${prefix ? `(${prefix})` : ""}`;
287+
const _rest = rest.concat(["", suffix]);
288+
289+
formatted_summary = `${_heading}\n${_rest.join("\n")}`;
290+
} else {
291+
formatted_summary = handle_line(summary, prefix, suffix);
292+
}
293+
294+
//@ts-ignore
295+
lines[release.name][_type].push({
296+
summary: formatted_summary
297+
});
298+
});
299+
300+
writeFileSync(
301+
join(rootDir, ".changeset", "_changelog.json"),
302+
JSON.stringify(lines, null, 2)
303+
);
304+
305+
return `\n\n-${prefix ? `${prefix} -` : ""} ${firstLine}\n${futureLines
306+
.map((l) => ` ${l}`)
307+
.join("\n")}`;
308+
}
309+
};
310+
311+
/**
312+
* @param {string} str The changelog entry
313+
* @param {string} prefix The prefix to add to the first line
314+
* @param {string} suffix The suffix to add to the last line
315+
* @returns {string} The formatted changelog entry
316+
*/
317+
function handle_line(str, prefix, suffix) {
318+
const [_s, ...lines] = str.split("\n").filter(Boolean);
319+
320+
const desc = `${prefix ? `${prefix} -` : ""} ${_s.replace(
321+
/[\s\.]$/,
322+
""
323+
)}. ${suffix}`;
324+
325+
if (_s.length === 1) {
326+
return desc;
327+
}
328+
329+
return [desc, ...lines.map((l) => ` ${l}`)].join("/n");
330+
}
331+
332+
module.exports = changelogFunctions;

.changeset/config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
3+
"changelog": ["./changeset.cjs", { "repo": "gradio-app/trackio" }],
4+
"commit": false,
5+
"fixed": [],
6+
"linked": [],
7+
"access": "public",
8+
"baseBranch": "main",
9+
"updateInternalDependencies": "patch"
10+
}

0 commit comments

Comments
 (0)