Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

changelog checker #54

Open
wants to merge 42 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7b25c0d
changelog checker
jake-perkins Mar 13, 2025
5ab5c52
updated syntax
jake-perkins Mar 13, 2025
8a89ebd
updated logic
jake-perkins Mar 13, 2025
c8d98fd
updates to ts approach
jake-perkins Mar 13, 2025
f94591b
remove pr number arg
jake-perkins Mar 13, 2025
0e2c3dd
script updates
jake-perkins Mar 13, 2025
cc3acd8
fmt
jake-perkins Mar 13, 2025
a2325bf
lint
jake-perkins Mar 13, 2025
8c3846a
remove un-used dependencies
jake-perkins Mar 14, 2025
2c7bbae
yarn.lock
jake-perkins Mar 14, 2025
8338f2c
dedup
jake-perkins Mar 14, 2025
f61d45a
add label check
jake-perkins Mar 14, 2025
4281ed0
pr
jake-perkins Mar 14, 2025
6bb2bb5
fix label logic
jake-perkins Mar 14, 2025
c71a795
syntax
jake-perkins Mar 14, 2025
b2e4abf
syntax
jake-perkins Mar 14, 2025
b32e2b9
fix
jake-perkins Mar 14, 2025
778c758
syntax
jake-perkins Mar 14, 2025
1df1e5d
changelog
jake-perkins Mar 14, 2025
2f2ee48
globbing lint
jake-perkins Mar 14, 2025
1d2a658
diff
jake-perkins Mar 14, 2025
947179d
shellcheck
jake-perkins Mar 14, 2025
d49b71c
shellcheck
jake-perkins Mar 14, 2025
39d6379
shellcheck
jake-perkins Mar 14, 2025
7bdaf82
test
jake-perkins Mar 14, 2025
1fcd744
lint
jake-perkins Mar 14, 2025
70ca876
testing
jake-perkins Mar 14, 2025
9e9ca3f
lint
jake-perkins Mar 14, 2025
b8d06f3
fmt
jake-perkins Mar 14, 2025
fe80e61
fmt
jake-perkins Mar 14, 2025
c25f415
fmt
jake-perkins Mar 14, 2025
265ca44
lint
jake-perkins Mar 14, 2025
3b79198
chore: remove comments
jake-perkins Mar 24, 2025
4c8b7d4
Update src/changelog-check.ts
jake-perkins Mar 24, 2025
0020d1a
Update src/changelog-check.ts
jake-perkins Mar 24, 2025
03836c5
code-review-updates
jake-perkins Apr 8, 2025
38b0053
updates
jake-perkins Apr 8, 2025
a94b63b
lock
jake-perkins Apr 8, 2025
87a2684
code-review-fixes
jake-perkins Apr 8, 2025
6331586
lint
jake-perkins Apr 8, 2025
a568305
Merge branch 'main' into changelog-checker
jake-perkins Apr 8, 2025
160fa58
yarn dedup
jake-perkins Apr 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/actionlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ paths:
ignore:
# We need to ignore the expected missing inputs in test-checkout-and-setup.yml
- 'missing input "is-high-risk-environment".+'
.github/workflows/changelog-check.yml:
ignore:
# Ignore SC2086 warnings related to shell commands
- 'SC2086:.+'
74 changes: 74 additions & 0 deletions .github/workflows/changelog-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Changelog Check

on:
workflow_call:
inputs:
base-branch:
required: true
type: string
feature-branch:
required: true
type: string
pr-number:
required: false
type: string
secrets:
gh-token:
required: true

jobs:
check-changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout github-tools repository
uses: actions/checkout@v4
with:
repository: MetaMask/github-tools
ref: changelog-checker
path: github-tools

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version-file: ./github-tools/.nvmrc
cache-dependency-path: ./github-tools/yarn.lock
cache: yarn

- name: Enable Corepack
run: corepack enable
shell: bash
working-directory: ./github-tools

- name: Install dependencies
run: yarn --immutable
shell: bash
working-directory: ./github-tools

- name: Check PR Labels
id: label-check
run: |
# Fetch labels from the GitHub API
labels=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/issues/${{ inputs.pr-number }}/labels")

# Proceed with checking for the 'no-changelog' label using jq
if echo "$labels" | jq -e '.[] | select(.name == "no-changelog")'; then
echo "No-changelog label found, skipping changelog check."
echo "SKIP_CHANGELOG=true" >> $GITHUB_ENV
else
echo "SKIP_CHANGELOG=false" >> $GITHUB_ENV
echo "No-changelog label not found, proceeding with changelog check."
fi
env:
GITHUB_TOKEN: ${{ secrets.gh-token }}
shell: bash

- name: Check Changelog
if: env.SKIP_CHANGELOG != 'true'
id: changelog-check
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.gh-token }}
working-directory: ./github-tools
run: |
yarn run changelog-check ${{ github.repository }} ${{ inputs.base-branch }} ${{ inputs.feature-branch }}
18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"description": "Tools for interacting with the GitHub API to do metrics gathering",
"scripts": {
"changelog-check": "ts-node src/changelog-check.ts",
"count-references-to-contributor-docs": "ts-node --swc src/scripts/count-references-to-contributor-docs/cli.ts",
"gen:commits": "node .github/scripts/generate-rc-commits.mjs",
"get-review-metrics": "ts-node src/get-review-metrics.ts",
Expand All @@ -20,6 +21,7 @@
"update-release-sheet": "node .github/scripts/update-release-sheet.mjs"
},
"dependencies": {
"@metamask/auto-changelog": "4.1.0",
"@metamask/utils": "^7.1.0",
"@octokit/graphql": "^7.0.1",
"@octokit/request": "^8.1.1",
Expand All @@ -30,6 +32,7 @@
"csv-parse": "5.6.0",
"googleapis": "144.0.0",
"luxon": "^3.3.0",
"node-fetch": "2.6.12",
"ora": "^5.4.1",
"simple-git": "3.27.0"
},
Expand All @@ -42,23 +45,27 @@
"@metamask/eslint-config-typescript": "^12.0.0",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.80",
"@types/axios": "^0.14.4",
"@types/diff": "^7",
"@types/fs-extra": "^11.0.4",
"@types/jest": "^28.1.6",
"@types/node": "^20.3.2",
"@types/node-fetch": "^2.6.12",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"depcheck": "^1.4.3",
"eslint": "^8.44.0",
"eslint-config-prettier": "^8.8.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.2",
"eslint-plugin-jsdoc": "^39.9.1",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-promise": "^6.1.1",
"jest": "^28.1.3",
"jest-it-up": "^2.0.2",
"prettier": "^2.7.1",
"prettier-plugin-packagejson": "^2.3.0",
"prettier": "^3.3.3",
"prettier-plugin-packagejson": "^2.5.2",
"ts-jest": "^28.0.7",
"ts-node": "^10.9.1",
"typescript": "^5.1.3"
Expand All @@ -67,6 +74,9 @@
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"prettier": ">=3.0.0"
},
"lavamoat": {
"allowScripts": {
"@lavamoat/preinstall-always-fail": false,
Expand Down
154 changes: 154 additions & 0 deletions src/changelog-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { parseChangelog } from '@metamask/auto-changelog';
import nodeFetch from 'node-fetch';

/**
* Asynchronously fetches the CHANGELOG.md file content from a specified GitHub repository and branch.
* The function constructs a URL to access the raw content of the file using GitHub's raw content service.
* It handles authorization using an optional GitHub token from environment variables.
*
* @param repo - The full name of the repository (e.g., "owner/repo").
* @param branch - The branch from which to fetch the CHANGELOG.md file.
* @returns A promise that resolves to the content of the CHANGELOG.md file as a string.
* If the fetch operation fails, it logs an error and returns an empty string.
*/
async function fetchChangelogFromGitHub(
repo: string,
branch: string,
): Promise<string> {
const url = `https://raw.githubusercontent.com/${repo}/${branch}/CHANGELOG.md`;
// eslint-disable-next-line n/no-process-env
const token = process.env.GITHUB_TOKEN ?? '';

try {
const headers = token ? { Authorization: `Bearer ${token}` } : {};
const response = await nodeFetch(url, {
headers,
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

return await response.text();
} catch (error) {
console.error(
`❌ Error fetching CHANGELOG.md from ${branch} on ${repo}:`,
error,
);
throw error;
}
}

/**
* Compares the changelog entries between the base and feature branches to determine if there are differences.
*
* @param baseChanges - The content of the '[Unreleased]' section from the base branch's CHANGELOG.md.
* @param featureChanges - The content of the '[Unreleased]' section from the feature branch's CHANGELOG.md.
* @returns Returns true if there are new or differing number of entries in the feature branch compared to the base branch.
*/
function compareChangeLogs(
baseChanges: string[],
featureChanges: string[],
): boolean {
const newEntries = featureChanges.filter(
(entry) => !baseChanges.includes(entry),
);

// Log and return true if there are new entries
if (newEntries.length > 0) {
console.log('New entries in feature branch:', newEntries);
return true;
}

// Check if the number of entries has changed
if (baseChanges.length !== featureChanges.length) {
console.log(
'The number of entries has changed. Base branch has',
baseChanges.length,
'entries, while feature branch has',
featureChanges.length,
'entries.',
);
return true;
}

// If no new entries and the size has not changed, return false
return false;
}

/**
* Validates that the CHANGELOG.md in a feature branch has been updated correctly by comparing it
* against the CHANGELOG.md in the base branch.
* @param repo - The GitHub repository from which to fetch the CHANGELOG.md file.
* @param baseBranch - The base branch (typically 'main' or 'master') to compare against.
* @param featureBranch - The feature branch that should contain the updated CHANGELOG.md.
*/
async function validateChangelog(
repo: string,
baseBranch: string,
featureBranch: string,
) {
console.log(`🔍 Fetching CHANGELOG.md from GitHub repository: ${repo}`);

const [baseChangelogContent, featureChangelogContent] = await Promise.all([
fetchChangelogFromGitHub(repo, baseBranch),
fetchChangelogFromGitHub(repo, featureBranch),
]);

if (!featureChangelogContent) {
throw new Error('❌ CHANGELOG.md is missing in the feature branch.');
}

const baseUnreleasedChanges = parseChangelog({
changelogContent: baseChangelogContent,
repoUrl: '', // Not needed as we're only parsing unreleased changes
}).getReleaseChanges('Unreleased');

const featureUnreleasedChanges = parseChangelog({
changelogContent: featureChangelogContent,
repoUrl: '', // Not needed as we're only parsing unreleased changes
}).getReleaseChanges('Unreleased');

const baseChanges = Object.values(baseUnreleasedChanges).flat();
const featureChanges = Object.values(featureUnreleasedChanges).flat();

console.log('🔍 Comparing changelog entries...');

console.log('Base unreleased section:', baseUnreleasedChanges);
console.log('Feature unreleased section:', featureUnreleasedChanges);

const hasChanges = compareChangeLogs(baseChanges, featureChanges);

if (!hasChanges) {
throw new Error(
"❌ No new entries detected under '## Unreleased'. Please update the changelog.",
);
}

console.log('✅ CHANGELOG.md has been correctly updated.');
}

// Parse command-line arguments
const args = process.argv.slice(2);
if (args.length < 3) {
console.error(
'❌ Usage: ts-node scripts/check-changelog.js <github-repo> <base-branch> <feature-branch>',
);
throw new Error('❌ Missing required arguments.');
}

const [githubRepo, baseBranch, featureBranch] = args;

// Ensure all required arguments are provided
if (!githubRepo || !baseBranch || !featureBranch) {
console.error(
'❌ Usage: ts-node src/check-changelog.ts <github-repo> <base-branch> <feature-branch>',
);
throw new Error('❌ Missing required arguments.');
}

// Run the validation
validateChangelog(githubRepo, baseBranch, featureBranch).catch((error) => {
console.error('❌ Unexpected error:', error);
throw error;
});
2 changes: 1 addition & 1 deletion src/scripts/count-references-to-contributor-docs/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const REPOSITORY_NAMES = [
'snaps',
] as const;

type RepositoryName = typeof REPOSITORY_NAMES[number];
type RepositoryName = (typeof REPOSITORY_NAMES)[number];

/**
* It is not necessary for us to query all of the pull requests or pull requests
Expand Down
Loading
Loading