Skip to content

Commit 66491bc

Browse files
chore: ensure all packages publish to pkg.pr.new (#4481)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 203c950 commit 66491bc

3 files changed

Lines changed: 308 additions & 205 deletions

File tree

.cursorignore

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,6 @@
44
**/coverage/
55
**/dist/
66

7-
# Explicitly ignore specific packages
8-
packages/typescript/
9-
packages/native-*
10-
packages/core/
11-
packages/assemble-release-plan/
12-
packages/native-federation-typescript/
13-
packages/esbuild/
14-
157
# Ignore specific directories
168
webpack/tooling/
179
webpack/setup/

.github/workflows/pkg-pr-new.yml

Lines changed: 1 addition & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -58,201 +58,5 @@ jobs:
5858
npx nx run-many --targets=build --projects=tag:type:pkg --skip-nx-cache || \
5959
(sleep 5 && npx nx run-many --targets=build --projects=tag:type:pkg --skip-nx-cache)
6060
61-
- name: Resolve synced Changesets package paths
62-
id: package_paths
63-
run: |
64-
paths_json=$(node -e '
65-
const fs = require("fs");
66-
const path = require("path");
67-
68-
const config = JSON.parse(fs.readFileSync(".changeset/config.json", "utf8"));
69-
const fixed = new Set((config.fixed || []).flat());
70-
const roots = ["packages", "apps"];
71-
const skipDirNames = new Set(["dist", "node_modules", ".git", ".nx"]);
72-
const packageMetaByName = new Map();
73-
74-
function walk(dir) {
75-
if (!fs.existsSync(dir)) return;
76-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
77-
const fullPath = path.join(dir, entry.name);
78-
if (entry.isDirectory()) {
79-
if (skipDirNames.has(entry.name)) continue;
80-
walk(fullPath);
81-
continue;
82-
}
83-
if (entry.isFile() && entry.name === "package.json") {
84-
const pkgDir = path.dirname(fullPath);
85-
const pkg = JSON.parse(fs.readFileSync(fullPath, "utf8"));
86-
if (fixed.has(pkg.name) && pkg.private !== true) {
87-
const existing = packageMetaByName.get(pkg.name);
88-
if (!existing || pkgDir.length < existing.path.length) {
89-
packageMetaByName.set(pkg.name, {
90-
name: pkg.name,
91-
path: pkgDir,
92-
pkg,
93-
});
94-
}
95-
}
96-
}
97-
}
98-
}
99-
100-
for (const root of roots) {
101-
walk(root);
102-
}
103-
104-
const dependencyFields = [
105-
"dependencies",
106-
"devDependencies",
107-
"optionalDependencies",
108-
"peerDependencies",
109-
];
110-
111-
for (const meta of packageMetaByName.values()) {
112-
const internalDeps = new Set();
113-
for (const field of dependencyFields) {
114-
const deps = meta.pkg[field];
115-
if (!deps || typeof deps !== "object") continue;
116-
for (const depName of Object.keys(deps)) {
117-
if (packageMetaByName.has(depName)) {
118-
internalDeps.add(depName);
119-
}
120-
}
121-
}
122-
meta.internalDeps = internalDeps;
123-
}
124-
125-
const indegreeByName = new Map();
126-
const dependentsByName = new Map();
127-
for (const [name, meta] of packageMetaByName) {
128-
indegreeByName.set(name, meta.internalDeps.size);
129-
dependentsByName.set(name, new Set());
130-
}
131-
132-
for (const [name, meta] of packageMetaByName) {
133-
for (const depName of meta.internalDeps) {
134-
dependentsByName.get(depName).add(name);
135-
}
136-
}
137-
138-
const queue = Array.from(indegreeByName.entries())
139-
.filter(([, degree]) => degree === 0)
140-
.map(([name]) => name)
141-
.sort();
142-
const orderedNames = [];
143-
144-
while (queue.length) {
145-
const current = queue.shift();
146-
orderedNames.push(current);
147-
const dependents = Array.from(dependentsByName.get(current) || []).sort();
148-
for (const dependent of dependents) {
149-
const nextDegree = (indegreeByName.get(dependent) || 0) - 1;
150-
indegreeByName.set(dependent, nextDegree);
151-
if (nextDegree === 0) {
152-
queue.push(dependent);
153-
}
154-
}
155-
queue.sort();
156-
}
157-
158-
if (orderedNames.length !== packageMetaByName.size) {
159-
const remaining = Array.from(packageMetaByName.keys())
160-
.filter((name) => !orderedNames.includes(name))
161-
.sort((a, b) => {
162-
const pathA = packageMetaByName.get(a).path;
163-
const pathB = packageMetaByName.get(b).path;
164-
return pathA.localeCompare(pathB);
165-
});
166-
orderedNames.push(...remaining);
167-
}
168-
169-
const orderedPaths = orderedNames.map((name) => packageMetaByName.get(name).path);
170-
process.stdout.write(JSON.stringify(orderedPaths));
171-
')
172-
173-
echo "paths_json=$paths_json" >> "$GITHUB_OUTPUT"
174-
17561
- name: Publish pkg.pr.new previews
176-
env:
177-
PKG_PATHS_JSON: ${{ steps.package_paths.outputs.paths_json }}
178-
run: |
179-
# shellcheck disable=SC2016
180-
node -e '
181-
const { spawnSync } = require("child_process");
182-
const paths = JSON.parse(process.env.PKG_PATHS_JSON || "[]");
183-
if (!paths.length) {
184-
console.log("No synced Changesets packages found to publish.");
185-
process.exit(0);
186-
}
187-
188-
function isRetriablePkgPrFailure(output) {
189-
return (
190-
/Publishing failed \((5\d\d|429)\)/.test(output) ||
191-
/Cloudflare|Internal Server Error|Bad Gateway|Gateway Timeout/.test(output)
192-
);
193-
}
194-
195-
const args = [
196-
"dlx",
197-
"pkg-pr-new",
198-
"publish",
199-
"--pnpm",
200-
"--packageManager=pnpm",
201-
"--comment=update",
202-
"--commentWithSha",
203-
...paths,
204-
];
205-
206-
const maxAttempts = 3;
207-
const baseDelaySeconds = 10;
208-
let lastStatus = 1;
209-
210-
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
211-
if (attempt > 1) {
212-
console.warn(`Retrying pkg.pr.new publish (attempt ${attempt}/${maxAttempts})...`);
213-
}
214-
215-
const result = spawnSync("pnpm", args, {
216-
encoding: "utf8",
217-
timeout: 12 * 60 * 1000,
218-
maxBuffer: 10 * 1024 * 1024,
219-
});
220-
if (result.stdout) process.stdout.write(result.stdout);
221-
if (result.stderr) process.stderr.write(result.stderr);
222-
223-
if (result.status === 0) {
224-
process.exit(0);
225-
}
226-
227-
const commandError = result.error;
228-
const combinedOutput = `${result.stdout || ""}\n${result.stderr || ""}\n${
229-
commandError?.message || ""
230-
}`;
231-
if (combinedOutput.includes("https://github.com/apps/pkg-pr-new is not installed")) {
232-
console.log("pkg.pr.new app is not installed on this repository; skipping preview publish.");
233-
process.exit(0);
234-
}
235-
236-
lastStatus = result.status || 1;
237-
const timedOut = commandError?.code === "ETIMEDOUT";
238-
const shouldRetry =
239-
attempt < maxAttempts &&
240-
(timedOut || isRetriablePkgPrFailure(combinedOutput));
241-
if (!shouldRetry) {
242-
if (commandError) {
243-
console.error(`pkg.pr.new publish failed to execute: ${commandError.message}`);
244-
}
245-
process.exit(lastStatus);
246-
}
247-
248-
const delaySeconds = baseDelaySeconds * 2 ** (attempt - 1);
249-
console.warn(
250-
`pkg.pr.new publish failed with ${
251-
timedOut ? "timeout" : "retriable error"
252-
}. Waiting ${delaySeconds}s before retry...`,
253-
);
254-
spawnSync("bash", ["-lc", `sleep ${delaySeconds}`], { stdio: "inherit" });
255-
}
256-
257-
process.exit(lastStatus);
258-
'
62+
run: node tools/scripts/publish-pkg-pr-new-previews.mjs

0 commit comments

Comments
 (0)