Skip to content
Draft
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8c48d1b
update dependencies update-dependencies-github-action to avoid openin…
anegg0 Jul 31, 2025
6029d94
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
b55acb3
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
b826737
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
0d15e84
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
5833f99
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
0f196d3
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
cf7c37a
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
57944dc
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
d23d451
use force:true when branch is behind
anegg0 Jul 31, 2025
cfa7bd8
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
c9981fd
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
cbeeaba
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
7591afa
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
185aaed
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
1518ff7
create a backup branch before force pushing in both "ahead" and "dive…
anegg0 Jul 31, 2025
e2b6d98
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
c625262
solve duplicate functions and undefined functions
anegg0 Jul 31, 2025
0db7609
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
dc5d8b6
fix: pr finding logic in check-releases script
anegg0 Jul 31, 2025
725868c
Merge branch 'master' into update-dependencies-github-action
anegg0 Jul 31, 2025
cad1422
Update scripts/check-releases.ts
anegg0 Jul 31, 2025
0ec2c7c
Merge branch 'master' into update-dependencies-github-action
pete-vielhaber Aug 4, 2025
6b23f86
Merge branch 'master' into update-dependencies-github-action
anegg0 Sep 9, 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
187 changes: 161 additions & 26 deletions scripts/check-releases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ interface DependenciesConfig {
projects: Project[];
}

async function createBackupBranch(
octokit: ReturnType<typeof github.getOctokit>,
context: typeof github.context,
baseBranchName: string,
sha: string,
): Promise<string> {
const backupBranchName = `${baseBranchName}-backup-${Date.now()}`;
await octokit.rest.git.createRef({
...context.repo,
ref: `refs/heads/${backupBranchName}`,
sha: sha,
});
return backupBranchName;
}

function extractRepoInfo(repoUrl: string): { owner: string; repo: string } | null {
try {
const url = new URL(repoUrl);
Expand Down Expand Up @@ -89,7 +104,7 @@ function isNewerVersion(newVersion: string, currentVersion: string): boolean {
return semver.gt(cleanNew, cleanCurrent);
}

async function createPullRequest(updatedProjects: Project[]) {
async function createOrUpdatePullRequest(updatedProjects: Project[]) {
const token = process.env.GITHUB_TOKEN;
if (!token) {
throw new Error('GITHUB_TOKEN not found in environment');
Expand All @@ -98,27 +113,132 @@ async function createPullRequest(updatedProjects: Project[]) {
const octokit = github.getOctokit(token);
const context = github.context;

// Create a new branch
const branchName = `docs/update-dependencies-${new Date().toISOString().split('T')[0]}`;
const prTitle = 'chore: update dependencies to latest versions';
const branchName = 'docs/update-dependencies';

try {
// Get the current commit SHA
const { data: ref } = await octokit.rest.git.getRef({
// Check for existing open PRs with the same title
const { data: existingPrs } = await octokit.rest.pulls.list({
...context.repo,
ref: 'heads/master',
state: 'open',
});
let existingPr = existingPrs.find((pr) => pr.title === prTitle && pr.head.ref === branchName); // Ensure both title and branch match
if (!existingPr) {
console.log(`No existing pull request found with title "${prTitle}" on branch "${branchName}".`);
}

// Create a new branch
await octokit.rest.git.createRef({
// Get the current commit SHA from master
const { data: masterRef } = await octokit.rest.git.getRef({
...context.repo,
ref: `refs/heads/${branchName}`,
sha: ref.object.sha,
ref: 'heads/master',
});

// Update dependencies.json in the new branch
let branchExists = false;
try {
// Check if branch already exists
await octokit.rest.git.getRef({
...context.repo,
ref: `heads/${branchName}`,
});
branchExists = true;
} catch (error: unknown) {
if (isUnexpectedError(error)) {
throw error;
}
}

function isUnexpectedError(error: unknown): boolean {
return (
error &&
typeof error === 'object' &&
'status' in error &&
(error as { status?: number }).status !== 404
);
}
if (!branchExists) {
// Create a new branch
await octokit.rest.git.createRef({
...context.repo,
ref: `refs/heads/${branchName}`,
sha: masterRef.object.sha,
});
console.log(`Created new branch: ${branchName}`);
} else {
// Update existing branch to latest master
const { data: branchRef } = await octokit.rest.git.getRef({
...context.repo,
ref: `heads/${branchName}`,
});

if (branchRef.object.sha === masterRef.object.sha) {
console.log(`Branch ${branchName} is already up-to-date.`);
} else {
const { data: comparison } = await octokit.rest.repos.compareCommits({
...context.repo,
base: branchRef.object.sha,
head: masterRef.object.sha,
});

if (comparison.status === 'identical') {
console.log(`Branch ${branchName} is already up-to-date.`);
} else if (comparison.status === 'ahead') {
// Create a backup branch to preserve commits
const backupBranchName = await createBackupBranch(
octokit,
context,
branchName,
branchRef.object.sha,
);
console.log(`Created backup branch: ${backupBranchName} to preserve commits.`);

// Update the branch to match master
await octokit.rest.git.updateRef({
...context.repo,
ref: `heads/${branchName}`,
sha: masterRef.object.sha,
force: true,
});
console.log(`Updated branch ${branchName} to match master.`);
} else if (comparison.status === 'diverged') {
// Create a backup branch to preserve commits before force updating
const backupBranchName = await createBackupBranch(
octokit,
context,
branchName,
branchRef.object.sha,
);
console.log(`Created backup branch: ${backupBranchName} to preserve diverged commits.`);

await octokit.rest.git.updateRef({
...context.repo,
ref: `heads/${branchName}`,
sha: masterRef.object.sha,
force: true,
Copy link

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Force pushing to update the branch in 'diverged' state will permanently lose any commits that were made to the branch. This could result in data loss if the branch contains important changes that haven't been merged.

Copilot uses AI. Check for mistakes.

});
console.warn(
`Forcefully updated branch ${branchName} to match master. Previous commits preserved in ${backupBranchName}.`,
);
} else if (comparison.status === 'behind') {
await octokit.rest.git.updateRef({
...context.repo,
ref: `heads/${branchName}`,
sha: masterRef.object.sha,
force: false,
});
console.log(`Updated branch ${branchName} to match master.`);
} else {
const errorMessage = `Unexpected comparison status: ${comparison.status}. Manual intervention required.`;
console.error(errorMessage);
throw new Error(errorMessage);
}
}
}

// Update dependencies.json in the branch
const { data: content } = await octokit.rest.repos.getContent({
...context.repo,
path: DEPENDENCIES_FILE,
ref: branchName,
});

if (!('content' in content)) {
Expand Down Expand Up @@ -146,26 +266,41 @@ async function createPullRequest(updatedProjects: Project[]) {
sha: content.sha,
});

// Create pull request
const prBody = `This PR updates the following dependencies to their latest versions:\n\n${updatedProjects
.map((p) => `- ${p.name}: ${p.latestRelease} (released on ${p.latestReleaseDate})`)
.join('\n')}\n\nPlease review the changes and update the documentation accordingly.`;

const { data: pr } = await octokit.rest.pulls.create({
...context.repo,
title: 'chore: update dependencies to latest versions',
head: branchName,
base: 'master',
body: prBody,
});
if (existingPr) {
// Update existing PR
const { data: updatedPr } = await octokit.rest.pulls.update({
...context.repo,
pull_number: existingPr.number,
body: prBody,
});

console.log(`Created PR #${pr.number}: ${pr.html_url}`);
console.log(`Updated existing PR #${updatedPr.number}: ${updatedPr.html_url}`);

// Set output for GitHub Actions
core.setOutput('pr_number', pr.number);
core.setOutput('pr_url', pr.html_url);
// Set output for GitHub Actions
core.setOutput('pr_number', updatedPr.number);
core.setOutput('pr_url', updatedPr.html_url);
} else {
// Create new pull request
const { data: pr } = await octokit.rest.pulls.create({
...context.repo,
title: prTitle,
head: branchName,
base: 'master',
body: prBody,
});

console.log(`Created new PR #${pr.number}: ${pr.html_url}`);

// Set output for GitHub Actions
core.setOutput('pr_number', pr.number);
core.setOutput('pr_url', pr.html_url);
}
} catch (error) {
console.error('Error creating pull request:', error);
console.error('Error creating/updating pull request:', error);
throw error;
}
}
Expand Down Expand Up @@ -206,8 +341,8 @@ async function main() {
}

if (updatedProjects.length > 0) {
console.log('Creating pull request with updates...');
await createPullRequest(updatedProjects);
console.log('Creating or updating pull request with updates...');
await createOrUpdatePullRequest(updatedProjects);
} else {
console.log('All dependencies are up to date.');
}
Expand Down