Skip to content

Commit

Permalink
add dependabot.yml (#2353)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
rtpascual and sobolk authored Jan 2, 2025
1 parent 866bc76 commit 9baf6e9
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .changeset/blue-meals-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .eslint_dictionary.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"debounce",
"declarator",
"decrypt",
"dependabot",
"deployer",
"deprecations",
"deprecator",
Expand All @@ -62,6 +63,7 @@
"formatter",
"frontend",
"frontends",
"frontmatter",
"fullname",
"func",
"geofence",
Expand Down
16 changes: 16 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Dependabot version updates raises a maximum of five pull requests each time it checks dependencies.
# Note that there is some overlap with Dependabot security updates so some options can effect security updates as well,
# see https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file.

version: 2
updates:
# Maintain dependencies for npm ecosystem
- package-ecosystem: 'npm'
# Checks all directories from the current layer and below recursively for package.json files
directories:
- '**/*'
schedule:
# Runs every Monday
interval: 'weekly'
# Update package.json files if new version is outside of version range specified there. Otherwise lock file only.
versioning-strategy: increase-if-necessary
21 changes: 21 additions & 0 deletions .github/workflows/health_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,31 @@ jobs:
run: |
mkdir api-validation-projects
npx tsx scripts/check_api_changes.ts base-branch-content api-validation-projects
generate_changeset:
if: github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]'
runs-on: ubuntu-latest
needs:
- install
- resolve_inputs
steps:
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # version 4.1.4
- uses: ./.github/actions/setup_node
with:
node-version: 18
- uses: ./.github/actions/restore_install_cache
with:
node-version: 18
cdk-version: ${{ needs.resolve_inputs.outputs.cdk_version }}
- name: Generate changesets for packages with version updates
run: npx tsx scripts/dependabot_handle_version_update.ts "$BASE_SHA" "$HEAD_SHA"
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
do_include_e2e:
needs:
- install
- resolve_inputs
- generate_changeset
runs-on: ubuntu-latest
permissions:
# This is required so that the step can read the labels on the pull request
Expand Down
199 changes: 199 additions & 0 deletions scripts/components/dependabot_version_update_handler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { randomUUID } from 'crypto';
import { $ as chainableExeca } from 'execa';
import fsp from 'fs/promises';
import { after, before, beforeEach, describe, it, mock } from 'node:test';
import { EOL, tmpdir } from 'os';
import path from 'path';
import { GitClient } from './git_client.js';
import { GithubClient } from './github_client.js';
import { NpmClient } from './npm_client.js';
import {
readPackageJson,
writePackageJson,
} from './package-json/package_json.js';
import { DependabotVersionUpdateHandler } from './dependabot_version_update_handler.js';
import assert from 'assert';

const originalEnv = process.env;

/**
* This test suite is more of an integration test than a unit test.
* It uses the real file system and git repo but mocks the GitHub API client and GitHub context
*/
void describe('dependabot version update handler', async () => {
let testWorkingDir: string;
let gitClient: GitClient;
let npmClient: NpmClient;

let cantaloupePackageName: string;
let cantaloupePackagePath: string;
let platypusPackageName: string;
let platypusPackagePath: string;

let baseRef: string;

const pullRequestBody = 'Bumps testDep from 1.0.0 to 1.1.0.';

before(async () => {
process.env.GITHUB_TOKEN = 'testToken';
});

after(async () => {
process.env = originalEnv;
});

beforeEach(async ({ name: testName }) => {
// create temp dir
const shortId = randomUUID().split('-')[0];
const testNameNormalized = testName.slice(0, 15).replaceAll(/\s/g, '');
testWorkingDir = await fsp.mkdtemp(
path.join(tmpdir(), `${testNameNormalized}-${shortId}`)
);
console.log(testWorkingDir);

gitClient = new GitClient(testWorkingDir);
npmClient = new NpmClient(null, testWorkingDir);

const $ = chainableExeca({ stdio: 'inherit', cwd: testWorkingDir });

// converting to lowercase because npm init creates packages with all lowercase
cantaloupePackageName =
`${testNameNormalized}-cantaloupe-${shortId}`.toLocaleLowerCase();
platypusPackageName =
`${testNameNormalized}-platypus-${shortId}`.toLocaleLowerCase();

cantaloupePackagePath = path.join(
testWorkingDir,
'packages',
cantaloupePackageName
);
platypusPackagePath = path.join(
testWorkingDir,
'packages',
platypusPackageName
);

await gitClient.init();
await gitClient.switchToBranch('main');
await npmClient.init();

await npmClient.initWorkspacePackage(cantaloupePackageName);
await setPackageToPublic(cantaloupePackagePath);

await npmClient.initWorkspacePackage(platypusPackageName);
await setPackageToPublic(platypusPackagePath);

await npmClient.install(['@changesets/cli']);
await setPackageDependencies(cantaloupePackagePath, { testDep: '^1.0.0' });
await setPackageDependencies(platypusPackagePath, { testDep: '^1.0.0' });

await $`npx changeset init`;
await gitClient.commitAllChanges('Initial setup');
baseRef = await gitClient.getHashForCurrentCommit();
});

void it('can generate changeset with version updates', async () => {
const githubClient = new GithubClient('garbage');
const labelPullRequestMocked = mock.method(
githubClient,
'labelPullRequest',
async () => {}
);
const gitPushMocked = mock.method(gitClient, 'push', async () => {});
const ghContextMocked = {
eventName: '',
sha: '',
ref: '',
workflow: '',
action: '',
actor: '',
job: '',
runNumber: 0,
runId: 0,
apiUrl: '',
serverUrl: '',
graphqlUrl: '',
payload: {
pull_request: {
number: 1,
body: pullRequestBody,
},
},
issue: {
owner: '',
repo: '',
number: 0,
},
repo: {
owner: '',
repo: '',
},
};

// Update package.json files for both packages and commit to match what Dependabot will do for a version update PR
await gitClient.switchToBranch('dependabot/test_update');
await setPackageDependencies(cantaloupePackagePath, { testDep: '^1.1.0' });
await setPackageDependencies(platypusPackagePath, { testDep: '^1.1.0' });
await gitClient.commitAllChanges('Bump dependencies');
const headRef = await gitClient.getHashForCurrentCommit();

const dependabotVersionUpdateHandler = new DependabotVersionUpdateHandler(
baseRef,
headRef,
gitClient,
githubClient,
testWorkingDir,
ghContextMocked
);

await dependabotVersionUpdateHandler.handleVersionUpdate();

const changesetFilePath = path.join(
testWorkingDir,
`.changeset/dependabot-${headRef}.md`
);

await assertChangesetFile(
changesetFilePath,
[cantaloupePackageName, platypusPackageName],
pullRequestBody
);
assert.deepEqual(labelPullRequestMocked.mock.calls[0].arguments, [
1,
['run-e2e'],
]);
assert.deepEqual(gitPushMocked.mock.callCount(), 1);
});
});

const setPackageToPublic = async (packagePath: string) => {
const packageJson = await readPackageJson(packagePath);
packageJson.publishConfig = {
access: 'public',
};
await writePackageJson(packagePath, packageJson);
};

const setPackageDependencies = async (
packagePath: string,
dependencies: Record<string, string>
) => {
const packageJson = await readPackageJson(packagePath);
packageJson.dependencies = dependencies;
await writePackageJson(packagePath, packageJson);
};

const assertChangesetFile = async (
filePath: string,
packageNames: string[],
message: string
) => {
const changesetFileContent = await fsp.readFile(filePath, 'utf-8');
const frontmatterContent = packageNames
.map((name) => `'${name}': patch`)
.join(EOL);

const expectedContent = `---${EOL}${frontmatterContent}${EOL}---${EOL}${EOL}${message}${EOL}`;

assert.deepEqual(changesetFileContent, expectedContent);
};
Loading

0 comments on commit 9baf6e9

Please sign in to comment.