Skip to content

Commit 9baf6e9

Browse files
rtpascualKamil Sobol
andauthored
add dependabot.yml (#2353)
* add dependabot.yml * add increase-if-necessary strategy * Update .github/dependabot.yml Co-authored-by: Kamil Sobol <[email protected]> * dependabot adds e2e label and action to add changesets to dependabot PRs * fix health checks * fix this * try this * add to eslint dictionary * PR feedback * add testing * try this * try that * mock git push * fix changeset file path * update way ghContext is mocked * try this * try this * PR feedback * Delete scripts/components/test-resources/github_pull_request_event.json --------- Co-authored-by: Kamil Sobol <[email protected]>
1 parent 866bc76 commit 9baf6e9

9 files changed

+426
-0
lines changed

.changeset/blue-meals-kneel.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.eslint_dictionary.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"debounce",
4242
"declarator",
4343
"decrypt",
44+
"dependabot",
4445
"deployer",
4546
"deprecations",
4647
"deprecator",
@@ -62,6 +63,7 @@
6263
"formatter",
6364
"frontend",
6465
"frontends",
66+
"frontmatter",
6567
"fullname",
6668
"func",
6769
"geofence",

.github/dependabot.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Dependabot version updates raises a maximum of five pull requests each time it checks dependencies.
2+
# Note that there is some overlap with Dependabot security updates so some options can effect security updates as well,
3+
# see https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file.
4+
5+
version: 2
6+
updates:
7+
# Maintain dependencies for npm ecosystem
8+
- package-ecosystem: 'npm'
9+
# Checks all directories from the current layer and below recursively for package.json files
10+
directories:
11+
- '**/*'
12+
schedule:
13+
# Runs every Monday
14+
interval: 'weekly'
15+
# Update package.json files if new version is outside of version range specified there. Otherwise lock file only.
16+
versioning-strategy: increase-if-necessary

.github/workflows/health_checks.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,31 @@ jobs:
269269
run: |
270270
mkdir api-validation-projects
271271
npx tsx scripts/check_api_changes.ts base-branch-content api-validation-projects
272+
generate_changeset:
273+
if: github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]'
274+
runs-on: ubuntu-latest
275+
needs:
276+
- install
277+
- resolve_inputs
278+
steps:
279+
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4
280+
- uses: ./.github/actions/setup_node
281+
with:
282+
node-version: 18
283+
- uses: ./.github/actions/restore_install_cache
284+
with:
285+
node-version: 18
286+
cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }}
287+
- name: Generate changesets for packages with version updates
288+
run: npx tsx scripts/dependabot_handle_version_update.ts "$BASE_SHA" "$HEAD_SHA"
289+
env:
290+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
291+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
272292
do_include_e2e:
273293
needs:
274294
- install
275295
- resolve_inputs
296+
- generate_changeset
276297
runs-on: ubuntu-latest
277298
permissions:
278299
# This is required so that the step can read the labels on the pull request
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import { randomUUID } from 'crypto';
2+
import { $ as chainableExeca } from 'execa';
3+
import fsp from 'fs/promises';
4+
import { after, before, beforeEach, describe, it, mock } from 'node:test';
5+
import { EOL, tmpdir } from 'os';
6+
import path from 'path';
7+
import { GitClient } from './git_client.js';
8+
import { GithubClient } from './github_client.js';
9+
import { NpmClient } from './npm_client.js';
10+
import {
11+
readPackageJson,
12+
writePackageJson,
13+
} from './package-json/package_json.js';
14+
import { DependabotVersionUpdateHandler } from './dependabot_version_update_handler.js';
15+
import assert from 'assert';
16+
17+
const originalEnv = process.env;
18+
19+
/**
20+
* This test suite is more of an integration test than a unit test.
21+
* It uses the real file system and git repo but mocks the GitHub API client and GitHub context
22+
*/
23+
void describe('dependabot version update handler', async () => {
24+
let testWorkingDir: string;
25+
let gitClient: GitClient;
26+
let npmClient: NpmClient;
27+
28+
let cantaloupePackageName: string;
29+
let cantaloupePackagePath: string;
30+
let platypusPackageName: string;
31+
let platypusPackagePath: string;
32+
33+
let baseRef: string;
34+
35+
const pullRequestBody = 'Bumps testDep from 1.0.0 to 1.1.0.';
36+
37+
before(async () => {
38+
process.env.GITHUB_TOKEN = 'testToken';
39+
});
40+
41+
after(async () => {
42+
process.env = originalEnv;
43+
});
44+
45+
beforeEach(async ({ name: testName }) => {
46+
// create temp dir
47+
const shortId = randomUUID().split('-')[0];
48+
const testNameNormalized = testName.slice(0, 15).replaceAll(/\s/g, '');
49+
testWorkingDir = await fsp.mkdtemp(
50+
path.join(tmpdir(), `${testNameNormalized}-${shortId}`)
51+
);
52+
console.log(testWorkingDir);
53+
54+
gitClient = new GitClient(testWorkingDir);
55+
npmClient = new NpmClient(null, testWorkingDir);
56+
57+
const $ = chainableExeca({ stdio: 'inherit', cwd: testWorkingDir });
58+
59+
// converting to lowercase because npm init creates packages with all lowercase
60+
cantaloupePackageName =
61+
`${testNameNormalized}-cantaloupe-${shortId}`.toLocaleLowerCase();
62+
platypusPackageName =
63+
`${testNameNormalized}-platypus-${shortId}`.toLocaleLowerCase();
64+
65+
cantaloupePackagePath = path.join(
66+
testWorkingDir,
67+
'packages',
68+
cantaloupePackageName
69+
);
70+
platypusPackagePath = path.join(
71+
testWorkingDir,
72+
'packages',
73+
platypusPackageName
74+
);
75+
76+
await gitClient.init();
77+
await gitClient.switchToBranch('main');
78+
await npmClient.init();
79+
80+
await npmClient.initWorkspacePackage(cantaloupePackageName);
81+
await setPackageToPublic(cantaloupePackagePath);
82+
83+
await npmClient.initWorkspacePackage(platypusPackageName);
84+
await setPackageToPublic(platypusPackagePath);
85+
86+
await npmClient.install(['@changesets/cli']);
87+
await setPackageDependencies(cantaloupePackagePath, { testDep: '^1.0.0' });
88+
await setPackageDependencies(platypusPackagePath, { testDep: '^1.0.0' });
89+
90+
await $`npx changeset init`;
91+
await gitClient.commitAllChanges('Initial setup');
92+
baseRef = await gitClient.getHashForCurrentCommit();
93+
});
94+
95+
void it('can generate changeset with version updates', async () => {
96+
const githubClient = new GithubClient('garbage');
97+
const labelPullRequestMocked = mock.method(
98+
githubClient,
99+
'labelPullRequest',
100+
async () => {}
101+
);
102+
const gitPushMocked = mock.method(gitClient, 'push', async () => {});
103+
const ghContextMocked = {
104+
eventName: '',
105+
sha: '',
106+
ref: '',
107+
workflow: '',
108+
action: '',
109+
actor: '',
110+
job: '',
111+
runNumber: 0,
112+
runId: 0,
113+
apiUrl: '',
114+
serverUrl: '',
115+
graphqlUrl: '',
116+
payload: {
117+
pull_request: {
118+
number: 1,
119+
body: pullRequestBody,
120+
},
121+
},
122+
issue: {
123+
owner: '',
124+
repo: '',
125+
number: 0,
126+
},
127+
repo: {
128+
owner: '',
129+
repo: '',
130+
},
131+
};
132+
133+
// Update package.json files for both packages and commit to match what Dependabot will do for a version update PR
134+
await gitClient.switchToBranch('dependabot/test_update');
135+
await setPackageDependencies(cantaloupePackagePath, { testDep: '^1.1.0' });
136+
await setPackageDependencies(platypusPackagePath, { testDep: '^1.1.0' });
137+
await gitClient.commitAllChanges('Bump dependencies');
138+
const headRef = await gitClient.getHashForCurrentCommit();
139+
140+
const dependabotVersionUpdateHandler = new DependabotVersionUpdateHandler(
141+
baseRef,
142+
headRef,
143+
gitClient,
144+
githubClient,
145+
testWorkingDir,
146+
ghContextMocked
147+
);
148+
149+
await dependabotVersionUpdateHandler.handleVersionUpdate();
150+
151+
const changesetFilePath = path.join(
152+
testWorkingDir,
153+
`.changeset/dependabot-${headRef}.md`
154+
);
155+
156+
await assertChangesetFile(
157+
changesetFilePath,
158+
[cantaloupePackageName, platypusPackageName],
159+
pullRequestBody
160+
);
161+
assert.deepEqual(labelPullRequestMocked.mock.calls[0].arguments, [
162+
1,
163+
['run-e2e'],
164+
]);
165+
assert.deepEqual(gitPushMocked.mock.callCount(), 1);
166+
});
167+
});
168+
169+
const setPackageToPublic = async (packagePath: string) => {
170+
const packageJson = await readPackageJson(packagePath);
171+
packageJson.publishConfig = {
172+
access: 'public',
173+
};
174+
await writePackageJson(packagePath, packageJson);
175+
};
176+
177+
const setPackageDependencies = async (
178+
packagePath: string,
179+
dependencies: Record<string, string>
180+
) => {
181+
const packageJson = await readPackageJson(packagePath);
182+
packageJson.dependencies = dependencies;
183+
await writePackageJson(packagePath, packageJson);
184+
};
185+
186+
const assertChangesetFile = async (
187+
filePath: string,
188+
packageNames: string[],
189+
message: string
190+
) => {
191+
const changesetFileContent = await fsp.readFile(filePath, 'utf-8');
192+
const frontmatterContent = packageNames
193+
.map((name) => `'${name}': patch`)
194+
.join(EOL);
195+
196+
const expectedContent = `---${EOL}${frontmatterContent}${EOL}---${EOL}${EOL}${message}${EOL}`;
197+
198+
assert.deepEqual(changesetFileContent, expectedContent);
199+
};

0 commit comments

Comments
 (0)