Skip to content

Commit 6ec24e8

Browse files
authoredApr 25, 2024
Merge pull request #150 from backstage/blam/renovate-changesets
feat: update rennovate changesets
2 parents 4e62efd + a0d0f77 commit 6ec24e8

17 files changed

+332
-16
lines changed
 

‎.pnp.cjs

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Binary file not shown.

‎.yarn/install-state.gz

1.27 KB
Binary file not shown.

‎.yarn/sdks/typescript/bin/tsc

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env node
22

33
const {existsSync} = require(`fs`);
4-
const {createRequire, createRequireFromPath} = require(`module`);
4+
const {createRequire} = require(`module`);
55
const {resolve} = require(`path`);
66

77
const relPnpApiPath = "../../../../.pnp.cjs";
88

99
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
10-
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
10+
const absRequire = createRequire(absPnpApiPath);
1111

1212
if (existsSync(absPnpApiPath)) {
1313
if (!process.versions.pnp) {

‎.yarn/sdks/typescript/bin/tsserver

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env node
22

33
const {existsSync} = require(`fs`);
4-
const {createRequire, createRequireFromPath} = require(`module`);
4+
const {createRequire} = require(`module`);
55
const {resolve} = require(`path`);
66

77
const relPnpApiPath = "../../../../.pnp.cjs";
88

99
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
10-
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
10+
const absRequire = createRequire(absPnpApiPath);
1111

1212
if (existsSync(absPnpApiPath)) {
1313
if (!process.versions.pnp) {

‎.yarn/sdks/typescript/lib/tsc.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env node
22

33
const {existsSync} = require(`fs`);
4-
const {createRequire, createRequireFromPath} = require(`module`);
4+
const {createRequire} = require(`module`);
55
const {resolve} = require(`path`);
66

77
const relPnpApiPath = "../../../../.pnp.cjs";
88

99
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
10-
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
10+
const absRequire = createRequire(absPnpApiPath);
1111

1212
if (existsSync(absPnpApiPath)) {
1313
if (!process.versions.pnp) {

‎.yarn/sdks/typescript/lib/tsserver.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env node
22

33
const {existsSync} = require(`fs`);
4-
const {createRequire, createRequireFromPath} = require(`module`);
4+
const {createRequire} = require(`module`);
55
const {resolve} = require(`path`);
66

77
const relPnpApiPath = "../../../../.pnp.cjs";
88

99
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
10-
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
10+
const absRequire = createRequire(absPnpApiPath);
1111

1212
const moduleWrapper = tsserver => {
1313
if (!process.versions.pnp) {
@@ -109,6 +109,8 @@ const moduleWrapper = tsserver => {
109109
str = `zip:${str}`;
110110
} break;
111111
}
112+
} else {
113+
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
112114
}
113115
}
114116

‎.yarn/sdks/typescript/lib/tsserverlibrary.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env node
22

33
const {existsSync} = require(`fs`);
4-
const {createRequire, createRequireFromPath} = require(`module`);
4+
const {createRequire} = require(`module`);
55
const {resolve} = require(`path`);
66

77
const relPnpApiPath = "../../../../.pnp.cjs";
88

99
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
10-
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
10+
const absRequire = createRequire(absPnpApiPath);
1111

1212
const moduleWrapper = tsserver => {
1313
if (!process.versions.pnp) {
@@ -109,6 +109,8 @@ const moduleWrapper = tsserver => {
109109
str = `zip:${str}`;
110110
} break;
111111
}
112+
} else {
113+
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
112114
}
113115
}
114116

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
#!/usr/bin/env node
22

33
const {existsSync} = require(`fs`);
4-
const {createRequire, createRequireFromPath} = require(`module`);
4+
const {createRequire} = require(`module`);
55
const {resolve} = require(`path`);
66

77
const relPnpApiPath = "../../../../.pnp.cjs";
88

99
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
10-
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
10+
const absRequire = createRequire(absPnpApiPath);
1111

1212
if (existsSync(absPnpApiPath)) {
1313
if (!process.versions.pnp) {
14-
// Setup the environment to be able to require typescript/lib/typescript.js
14+
// Setup the environment to be able to require typescript
1515
require(absPnpApiPath).setup();
1616
}
1717
}
1818

19-
// Defer to the real typescript/lib/typescript.js your application uses
20-
module.exports = absRequire(`typescript/lib/typescript.js`);
19+
// Defer to the real typescript your application uses
20+
module.exports = absRequire(`typescript`);

‎.yarn/sdks/typescript/package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,9 @@
22
"name": "typescript",
33
"version": "4.7.4-sdk",
44
"main": "./lib/typescript.js",
5-
"type": "commonjs"
5+
"type": "commonjs",
6+
"bin": {
7+
"tsc": "./bin/tsc",
8+
"tsserver": "./bin/tsserver"
9+
}
610
}

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
},
1313
"dependencies": {
1414
"@actions/core": "^1.9.0",
15+
"@actions/exec": "^1.1.1",
1516
"@actions/github": "^5.0.3",
1617
"@manypkg/get-packages": "^2.2.1",
1718
"@octokit/auth-app": "^3.6.1",

‎renovate-changesets/action.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: Backstage Renovate Changeset Creator
2+
description: Create changesets on the renovate bot PR's if needed
3+
inputs:
4+
multiple-workspaces:
5+
description: If it's this repository is a collection of workspaces
6+
required: false
7+
8+
outputs: {}
9+
runs:
10+
using: node16
11+
main: ./entry.js

‎renovate-changesets/entry.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
require('../.pnp.cjs').setup();
2+
3+
require('ts-node').register({
4+
transpileOnly: true,
5+
project: require('path').resolve(__dirname, '../tsconfig.json'),
6+
});
7+
8+
require('./index');

‎renovate-changesets/index.ts

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import * as core from '@actions/core';
2+
import {
3+
commitAndPush,
4+
createChangeset,
5+
getBranchName,
6+
getBumps,
7+
getChangedFiles,
8+
getChangesetFilename,
9+
listPackages,
10+
} from './renovateChangesets';
11+
import { relative as relativePath, resolve as resolvePath } from 'path';
12+
13+
async function main() {
14+
core.info('Running Renovate Changesets');
15+
16+
const isMultipleWorkspaces = core.getBooleanInput('multiple-workspaces', {
17+
required: false,
18+
});
19+
20+
const branchName = await getBranchName();
21+
22+
if (!branchName.startsWith('renovate/')) {
23+
core.info('Not a renovate branch, skipping');
24+
return;
25+
}
26+
27+
const allPackages = await listPackages({
28+
isMultipleWorkspaces,
29+
includeRoots: true,
30+
});
31+
32+
// Need to remove the topmost package if we're in a multi-workspace setup
33+
const packageList = isMultipleWorkspaces
34+
? allPackages.filter(p => p.dir !== process.cwd())
35+
: allPackages;
36+
37+
const changedFiles = await getChangedFiles();
38+
39+
// Group file changes by workspace, and drop workspaces without changes
40+
const changedFilesByWorkspace = new Map<string, string[]>(
41+
packageList
42+
.filter(p => p.isRoot)
43+
.map(p => [
44+
p.dir,
45+
changedFiles
46+
.filter(f => f.startsWith(p.relativeDir))
47+
.map(f => relativePath(p.dir, f)),
48+
])
49+
.filter((workspaceChanges): workspaceChanges is [string, string[]] => {
50+
const [_, files] = workspaceChanges;
51+
return files.length > 0;
52+
}),
53+
);
54+
55+
// Check if those workspaces have changesets
56+
const changedWorkspacesWithChangeset = new Map<string, boolean>(
57+
Array.from(changedFilesByWorkspace.entries()).map(([workspace, files]) => [
58+
workspace,
59+
files.some(f => f.startsWith('.changeset/')),
60+
]),
61+
);
62+
63+
// If all packages have a changeset already then exit early.
64+
if (
65+
!changedWorkspacesWithChangeset.size ||
66+
Array.from(changedWorkspacesWithChangeset.values()).every(v => v)
67+
) {
68+
core.info(
69+
'No changesets to create, or all workspaces have changesets already',
70+
);
71+
return;
72+
}
73+
74+
// Get all package.jsons that were changed
75+
const changedPackageJsons = new Map<
76+
string,
77+
{
78+
path: string;
79+
localPath: string;
80+
packageJson: { name: string; version: string };
81+
}[]
82+
>(
83+
Array.from(changedFilesByWorkspace.entries())
84+
.map(([workspace, files]) => [
85+
workspace,
86+
files.filter(f => f.endsWith('package.json')),
87+
])
88+
.filter((workspaceChanges): workspaceChanges is [string, string[]] => {
89+
const [_, files] = workspaceChanges;
90+
return files.length > 0;
91+
})
92+
.map(([workspace, files]) => [
93+
workspace,
94+
files.map(f => ({
95+
path: f,
96+
localPath: relativePath(process.cwd(), resolvePath(workspace, f)),
97+
packageJson: require(resolvePath(workspace, f)),
98+
})),
99+
]),
100+
);
101+
102+
if (!changedPackageJsons.size) {
103+
core.info('Seems that no package.jsons were changed in this PR');
104+
return;
105+
}
106+
107+
// Get the bumps that happened in the last commit made by rennovate in the diff
108+
const bumps = await Promise.all(
109+
Array.from(changedPackageJsons.entries()).map(
110+
async ([workspace, packages]) => {
111+
const changes = await getBumps(packages.map(p => p.localPath));
112+
113+
return {
114+
workspace,
115+
packages,
116+
changes,
117+
};
118+
},
119+
),
120+
);
121+
122+
const changesetFilename = await getChangesetFilename();
123+
const changesetFiles: string[] = [];
124+
125+
// Create a changeset for each of the workspaces in the right place
126+
for (const bump of bumps) {
127+
const changesetFilePath = resolvePath(bump.workspace, changesetFilename);
128+
changesetFiles.push(changesetFilePath);
129+
130+
await createChangeset(
131+
changesetFilePath,
132+
bump.changes,
133+
bump.packages.map(p => p.packageJson.name),
134+
);
135+
}
136+
137+
// Commit and push all the changesets.
138+
await commitAndPush(changesetFiles);
139+
}
140+
141+
main().catch(error => {
142+
core.error(error.stack);
143+
core.setFailed(String(error));
144+
process.exit(1);
145+
});
+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { getExecOutput, exec } from '@actions/exec';
2+
import fs from 'fs/promises';
3+
import { resolve as resolvePath, relative as relativePath } from 'path';
4+
import { getPackages, type Package } from '@manypkg/get-packages';
5+
6+
export async function getBranchName() {
7+
const { stdout } = await getExecOutput('git', ['branch', '--show-current']);
8+
return stdout;
9+
}
10+
11+
const findPackagesInDir = async ({
12+
dir,
13+
includeRoots,
14+
}: {
15+
includeRoots: boolean;
16+
dir: string;
17+
}) => {
18+
const { packages, rootPackage } = await getPackages(dir).catch(() => ({
19+
packages: [],
20+
rootPackage: undefined,
21+
}));
22+
23+
return [...packages, rootPackage && { ...rootPackage, isRoot: true }]
24+
.filter((p): p is Package & { isRoot?: boolean } => Boolean(p))
25+
.map(p => ({
26+
...p,
27+
isRoot: p.isRoot ?? false,
28+
relativeDir: relativePath(process.cwd(), resolvePath(dir, p.relativeDir)),
29+
}))
30+
.filter(({ isRoot }) => (!includeRoots ? !isRoot : true));
31+
};
32+
33+
export async function getChangesetFilename() {
34+
const { stdout: shortHash } = await getExecOutput(
35+
'git rev-parse --short HEAD',
36+
);
37+
return `.changeset/renovate-${shortHash.trim()}.md`;
38+
}
39+
40+
export async function createChangeset(
41+
fileName: string,
42+
packageBumps: Map<string, string>,
43+
packages: string[],
44+
) {
45+
let message = '';
46+
for (const [pkg, bump] of packageBumps) {
47+
message = message + `Updated dependency \`${pkg}\` to \`${bump}\`.\n`;
48+
}
49+
50+
const pkgs = packages.map(pkg => `'${pkg}': patch`).join('\n');
51+
const body = `---\n${pkgs}\n---\n\n${message.trim()}\n`;
52+
await fs.writeFile(fileName, body);
53+
}
54+
55+
export const getChangedFiles = async () => {
56+
const diffOutput = await getExecOutput('git diff --name-only HEAD~1');
57+
return diffOutput.stdout.split('\n');
58+
};
59+
60+
export async function getBumps(files: string[]) {
61+
const bumps = new Map();
62+
for (const file of files) {
63+
const { stdout: changes } = await getExecOutput('git', ['show', file]);
64+
for (const change of changes.split('\n')) {
65+
if (!change.startsWith('+ ')) {
66+
continue;
67+
}
68+
const match = change.match(/"(.*?)"/g);
69+
if (match) {
70+
bumps.set(match[0].replace(/"/g, ''), match[1].replace(/"/g, ''));
71+
}
72+
}
73+
}
74+
return bumps;
75+
}
76+
77+
export async function commitAndPush(fileNames: string[]) {
78+
await exec('git', ['add', ...fileNames]);
79+
await exec('git commit -C HEAD --amend --no-edit');
80+
await exec('git push --force');
81+
}
82+
83+
export async function listPackages({
84+
isMultipleWorkspaces,
85+
includeRoots = false,
86+
}: {
87+
isMultipleWorkspaces?: boolean;
88+
includeRoots?: boolean;
89+
}): Promise<(Package & { isRoot: boolean })[]> {
90+
if (!isMultipleWorkspaces) {
91+
return findPackagesInDir({ dir: process.cwd(), includeRoots });
92+
}
93+
94+
const workspacesRoot = resolvePath(process.cwd(), 'workspaces');
95+
const workspaceDirs = await fs.readdir(workspacesRoot);
96+
97+
return await Promise.all(
98+
workspaceDirs.map(workspace =>
99+
findPackagesInDir({
100+
dir: resolvePath(workspacesRoot, workspace),
101+
includeRoots,
102+
}),
103+
),
104+
).then(packages => packages.flat());
105+
}

‎yarn.lock

+17
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ __metadata:
1515
languageName: node
1616
linkType: hard
1717

18+
"@actions/exec@npm:^1.1.1":
19+
version: 1.1.1
20+
resolution: "@actions/exec@npm:1.1.1"
21+
dependencies:
22+
"@actions/io": ^1.0.1
23+
checksum: d976e66dd51ab03d76a143da8e1406daa1bcdee06046168e6e0bec681c87a12999eefaad7a81cb81f28e4190610f55a58b8458ae4b82cbaaba13200490f4e8c2
24+
languageName: node
25+
linkType: hard
26+
1827
"@actions/github@npm:^5.0.3":
1928
version: 5.0.3
2029
resolution: "@actions/github@npm:5.0.3"
@@ -36,6 +45,13 @@ __metadata:
3645
languageName: node
3746
linkType: hard
3847

48+
"@actions/io@npm:^1.0.1":
49+
version: 1.1.3
50+
resolution: "@actions/io@npm:1.1.3"
51+
checksum: 42841ac2b8a7afb29456b9edb5534dbe00148893c794bdbc17d29166847c51c884e2a7c087a489a428250a78e7b54bc761ba3b55eb2f97d9600e9193b60caf0b
52+
languageName: node
53+
linkType: hard
54+
3955
"@ampproject/remapping@npm:^2.1.0":
4056
version: 2.2.0
4157
resolution: "@ampproject/remapping@npm:2.2.0"
@@ -3770,6 +3786,7 @@ __metadata:
37703786
resolution: "root-workspace-0b6124@workspace:."
37713787
dependencies:
37723788
"@actions/core": ^1.9.0
3789+
"@actions/exec": ^1.1.1
37733790
"@actions/github": ^5.0.3
37743791
"@jest/globals": ^28.1.1
37753792
"@manypkg/get-packages": ^2.2.1

0 commit comments

Comments
 (0)
Please sign in to comment.