Skip to content

Commit 2fe1cd3

Browse files
authored
Get changed packages (#8)
* Get packages * Fix now.json * Change entry to js * Fix a thing * Include title in changeset template * Gracefully handle the getting changed packages failing * Fix a thing * Support pnpm * Things * Move error handling * Try a thing * Bad things * Table * Better table * Only include new/changed changesets * change some things * Fix a thing * Another fix * Update now.json
1 parent 1e2aa99 commit 2fe1cd3

File tree

7 files changed

+1003
-133
lines changed

7 files changed

+1003
-133
lines changed

get-changed-packages.ts

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import nodePath from "path";
2+
import micromatch from "micromatch";
3+
import { Octokit } from "probot";
4+
import fetch from "node-fetch";
5+
import { safeLoad } from "js-yaml";
6+
import { Packages, Tool } from "@manypkg/get-packages";
7+
import assembleReleasePlan from "@changesets/assemble-release-plan";
8+
import { PreState, Config, NewChangeset } from "@changesets/types";
9+
import parseChangeset from "@changesets/parse";
10+
11+
type Sha = string & { ___sha: string };
12+
13+
export let getChangedPackages = async ({
14+
owner,
15+
repo,
16+
ref,
17+
changedFiles: changedFilesPromise,
18+
octokit,
19+
installationToken
20+
}: {
21+
owner: string;
22+
repo: string;
23+
ref: string;
24+
changedFiles: string[] | Promise<string[]>;
25+
octokit: Octokit;
26+
installationToken: string;
27+
}) => {
28+
let hasErrored = false;
29+
let encodedCredentials = Buffer.from(
30+
`x-access-token:${installationToken}`
31+
).toString("base64");
32+
33+
function fetchFile(path: string) {
34+
return fetch(
35+
`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path}`,
36+
{
37+
headers: {
38+
Authorization: `Basic ${encodedCredentials}`
39+
}
40+
}
41+
);
42+
}
43+
44+
function fetchJsonFile(path: string) {
45+
return fetchFile(path)
46+
.then(x => x.json())
47+
.catch(err => {
48+
hasErrored = true;
49+
console.error(err);
50+
return {};
51+
});
52+
}
53+
54+
function fetchTextFile(path: string) {
55+
return fetchFile(path)
56+
.then(x => x.text())
57+
.catch(err => {
58+
hasErrored = true;
59+
console.error(err);
60+
return "";
61+
});
62+
}
63+
64+
async function getPackage(pkgPath: string) {
65+
let jsonContent = await fetchJsonFile(pkgPath + "/package.json");
66+
return {
67+
packageJson: jsonContent,
68+
dir: pkgPath
69+
};
70+
}
71+
72+
let rootPackageJsonContentsPromise = fetchJsonFile("package.json");
73+
let configPromise: Promise<Config> = fetchJsonFile(".changeset/config.json");
74+
75+
let tree = await octokit.git.getTree({
76+
owner,
77+
repo,
78+
recursive: "1",
79+
tree_sha: ref
80+
});
81+
82+
let preStatePromise: Promise<PreState> | undefined;
83+
let changesetPromises: Promise<NewChangeset>[] = [];
84+
let itemsByDirPath = new Map<string, { path: string; sha: Sha }>();
85+
let potentialWorkspaceDirectories: string[] = [];
86+
let isPnpm = false;
87+
let changedFiles = await changedFilesPromise;
88+
89+
for (let item of tree.data.tree) {
90+
if (item.path.endsWith("/package.json")) {
91+
let dirPath = nodePath.dirname(item.path);
92+
potentialWorkspaceDirectories.push(dirPath);
93+
itemsByDirPath.set(dirPath, item);
94+
} else if (item.path === "pnpm-workspace.yaml") {
95+
isPnpm = true;
96+
} else if (item.path === ".changeset/pre.json") {
97+
preStatePromise = fetchJsonFile(".changeset/pre.json");
98+
} else if (
99+
item.path !== ".changeset/README.md" &&
100+
item.path.startsWith(".changeset") &&
101+
item.path.endsWith(".md") &&
102+
changedFiles.includes(item.path)
103+
) {
104+
let res = /\.changeset\/([^\.]+)\.md/.exec(item.path);
105+
if (!res) {
106+
throw new Error("could not get name from changeset filename");
107+
}
108+
let id = res[1];
109+
changesetPromises.push(
110+
fetchTextFile(item.path).then(text => {
111+
return { ...parseChangeset(text), id };
112+
})
113+
);
114+
}
115+
}
116+
let tool:
117+
| {
118+
tool: Tool;
119+
globs: string[];
120+
}
121+
| undefined;
122+
123+
if (isPnpm) {
124+
tool = {
125+
tool: "pnpm",
126+
globs: safeLoad(await fetchTextFile("pnpm-workspace.yaml")).packages
127+
};
128+
} else {
129+
let rootPackageJsonContent = await rootPackageJsonContentsPromise;
130+
131+
if (rootPackageJsonContent.workspaces) {
132+
if (!Array.isArray(rootPackageJsonContent.workspaces)) {
133+
tool = {
134+
tool: "yarn",
135+
globs: rootPackageJsonContent.workspaces.packages
136+
};
137+
} else {
138+
tool = {
139+
tool: "yarn",
140+
globs: rootPackageJsonContent.workspaces
141+
};
142+
}
143+
} else if (
144+
rootPackageJsonContent.bolt &&
145+
rootPackageJsonContent.bolt.workspaces
146+
) {
147+
tool = {
148+
tool: "bolt",
149+
globs: rootPackageJsonContent.bolt.workspaces
150+
};
151+
}
152+
}
153+
154+
if (
155+
!tool ||
156+
!(Array.isArray(tool.globs) && tool.globs.every(x => typeof x === "string"))
157+
) {
158+
throw new Error("globs are not valid");
159+
}
160+
161+
let rootPackageJsonContent = await rootPackageJsonContentsPromise;
162+
163+
let packages: Packages = {
164+
root: {
165+
dir: "/",
166+
packageJson: rootPackageJsonContent
167+
},
168+
tool: tool.tool,
169+
packages: []
170+
};
171+
172+
if (tool) {
173+
let matches = micromatch(potentialWorkspaceDirectories, tool.globs);
174+
175+
packages.packages = await Promise.all(matches.map(dir => getPackage(dir)));
176+
} else {
177+
packages.packages.push(packages.root);
178+
}
179+
if (hasErrored) {
180+
throw new Error("an error occurred");
181+
}
182+
183+
const releasePlan = assembleReleasePlan(
184+
await Promise.all(changesetPromises),
185+
packages,
186+
await configPromise,
187+
await preStatePromise
188+
);
189+
190+
return {
191+
changedPackages: packages.packages
192+
.filter(pkg =>
193+
changedFiles.some(changedFile => changedFile.includes(pkg.dir))
194+
)
195+
.map(x => x.packageJson.name),
196+
releasePlan
197+
};
198+
};

index.js

Lines changed: 0 additions & 126 deletions
This file was deleted.

0 commit comments

Comments
 (0)