Skip to content

Commit 8a9ebcc

Browse files
authored
fix: add retry logic to username fetching and improve dispatching (#69)
- Implemented retry logic for fetching the username from email. - Added an additional local matching to handle specific situations. - Removed workflow dispatching as that did not work. Instead used a PAT when creating the pull request to ensure proper workflows run.
1 parent 8ca6f6e commit 8a9ebcc

File tree

6 files changed

+93
-24
lines changed

6 files changed

+93
-24
lines changed

.github/scripts/changelog.js

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,22 @@ Important formatting rules:
4242
- Only include sub-bullets if they are necessary to clarify the change.
4343
- Avoid level 4 headings.
4444
- Use level 3 (###) for sections.
45-
- Omit sections with no content.
45+
- Omit sections with no content silently - do not add any notes or explanations about omitted sections.
4646
`;
4747

4848
// In-memory cache for username lookups
4949
const usernameCache = new Map();
5050

51+
/**
52+
* Pauses execution for a specified amount of time.
53+
*
54+
* @param {number} ms - The number of milliseconds to sleep.
55+
* @returns {Promise<void>} A promise that resolves after the specified time has passed.
56+
*/
57+
function sleep(ms) {
58+
new Promise((resolve) => setTimeout(resolve, ms));
59+
}
60+
5161
/**
5262
* Validates required environment variables
5363
*/
@@ -120,6 +130,25 @@ function githubApiRequest(path) {
120130
});
121131
}
122132

133+
/**
134+
* Makes a request to the GitHub API with retries
135+
* @param {string} path - The API endpoint path including query parameters
136+
* @param {number} retries - Number of retries remaining
137+
* @returns {Promise<object|null>} - Parsed JSON response or null for 404s
138+
*/
139+
async function githubApiRequestWithRetry(path, retries = 2) {
140+
try {
141+
return await githubApiRequest(path);
142+
} catch (error) {
143+
if (retries > 0 && error.message.includes('403')) {
144+
console.log(`Rate limited, retrying after 2 seconds... (${retries} retries left)`);
145+
await sleep(2000);
146+
return githubApiRequestWithRetry(path, retries - 1);
147+
}
148+
throw error;
149+
}
150+
}
151+
123152
/**
124153
* Attempts to resolve a GitHub username from a commit email address
125154
* using multiple GitHub API endpoints.
@@ -129,10 +158,44 @@ function githubApiRequest(path) {
129158
*/
130159
async function resolveGitHubUsername(commitEmail) {
131160
console.log('Attempting to resolve username:', commitEmail);
161+
162+
// Local resolution - Handle various GitHub email patterns
163+
const emailMatches = email.match(/^(?:(?:[^@]+)?@)?([^@]+)$/);
164+
if (emailMatches) {
165+
const [, domain] = emailMatches;
166+
167+
// Handle github.com email variations
168+
if (domain === 'users.noreply.github.com') {
169+
// Extract username from 1234567+username@users.noreply.github.com
170+
// or username@users.noreply.github.com
171+
const matches = email.match(/^(?:(\d+)\+)?([^@]+)@users\.noreply\.github\.com$/);
172+
return matches ? matches[2] : null;
173+
}
174+
175+
// Handle organization emails like username@organization.github.com
176+
if (domain.endsWith('.gh.loli.garden')) {
177+
const matches = email.match(/^([^@]+)@[^@]+\.github\.com$/);
178+
return matches ? matches[1] : null;
179+
}
180+
181+
// Handle GitHub Enterprise emails
182+
// Pattern: username@github.{enterprise}.com
183+
const enterpriseMatches = email.match(/^([^@]+)@github\.[^@]+\.com$/);
184+
if (enterpriseMatches) {
185+
return enterpriseMatches[1];
186+
}
187+
188+
// Handle GitHub staff emails
189+
if (email.endsWith('@github.com')) {
190+
const matches = email.match(/^([^@]+)@github\.com$/);
191+
return matches ? matches[1] : null;
192+
}
193+
}
194+
132195
try {
133196
// First attempt: Direct API search for user by email
134197
console.log(`[${commitEmail}] Querying user API`);
135-
const searchResponse = await githubApiRequest(
198+
const searchResponse = await githubApiRequestWithRetry(
136199
`https://api.github.com/search/users?q=${encodeURIComponent(commitEmail)}+in:email`,
137200
);
138201
if (searchResponse?.items && searchResponse.items.length > 0) {
@@ -148,7 +211,7 @@ async function resolveGitHubUsername(commitEmail) {
148211
try {
149212
console.log(`[${commitEmail}] Querying commit API`);
150213
// Second attempt: Check commit API for associated username
151-
const commitSearchResponse = await githubApiRequest(
214+
const commitSearchResponse = await githubApiRequestWithRetry(
152215
`https://api.github.com/search/commits?q=author-email:${encodeURIComponent(commitEmail)}&per_page=25`,
153216
);
154217
if (commitSearchResponse?.items?.length > 0) {

.github/workflows/check-dist.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ on:
66
pull_request:
77
branches:
88
- main
9-
repository_dispatch:
10-
types: [release-preview]
119

1210
permissions:
1311
contents: read

.github/workflows/ci.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ on:
99
types: [opened, reopened, synchronize]
1010
branches:
1111
- main
12-
repository_dispatch:
13-
types: [release-preview]
14-
workflow_dispatch:
1512

1613
permissions:
1714
contents: write # Required to create tags, creaste releases and update wiki

.github/workflows/codeql-analysis.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ on:
77
push:
88
branches:
99
- main
10-
repository_dispatch:
11-
types: [release-preview]
1210
schedule:
1311
- cron: "31 7 * * 3"
1412

.github/workflows/lint.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@ on:
77
push:
88
branches:
99
- main
10-
repository_dispatch:
11-
types: [release-preview]
1210

1311
permissions:
1412
contents: read
1513
packages: read
16-
statuses: write
1714

1815
jobs:
1916
lint:

.github/workflows/release-start.yml

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424
permissions:
2525
contents: write # Required to create a new pull request
2626
pull-requests: write # Required to comment on pull requests
27+
actions: write # Required to trigger workflows
2728
steps:
2829
- uses: actions/checkout@v4
2930
with:
@@ -43,7 +44,6 @@ jobs:
4344
# In order to create signed commits, we need to ensure that we commit without an author name and email.
4445
# However, this can't be done via git as this is required. We need to leverage the GitHub REST/GraphQL
4546
# API endpoints.
46-
#
4747
# https://github.com/orgs/community/discussions/24664#discussioncomment-5084236
4848
- name: Setup ghup [GitHub API Client]
4949
uses: nexthink-oss/ghup/actions/setup@main
@@ -101,19 +101,14 @@ jobs:
101101
core.setFailed(error.message);
102102
}
103103
104-
- name: Create new PR
104+
# Note: We can't change the head branch once a PR is opened. Thus we need to delete any branches
105+
# that exist from any existing open pull requests.
106+
- name: Close existing release pull requests
105107
uses: actions/github-script@v7
106108
with:
107109
script: |
108-
const version = '${{ env.VERSION }}';
109-
const prTitle = `chore(release): v${version}`;
110-
const branchName = `release-v${version}`;
111-
const changelog = ${{ steps.changelog.outputs.result }};
112110
const prTitleRegex = /^chore\(release\): v\d+\.\d+\.\d+$/;
113111
114-
// Note: We can't change the head branch once a PR is opened. Thus we need to delete any branches
115-
// that exist from any existing open pull requests.
116-
117112
console.log('Searching for existing open PRs ...');
118113
const { data: existingPRs } = await github.rest.pulls.list({
119114
owner: context.repo.owner,
@@ -150,14 +145,33 @@ jobs:
150145
}
151146
}
152147
148+
# Additional caveat:
149+
# When you use the repository's GITHUB_TOKEN to perform tasks, events triggered by the GITHUB_TOKEN,
150+
# with the exception of workflow_dispatch and repository_dispatch, will not create a new workflow run.
151+
# This prevents you from accidentally creating recursive workflow runs.
152+
#
153+
# There is no way to even trigger this with a repository_dispatch. Therefore, currently the only way
154+
# is to use a separate PAT. In the future we could release a bot to help automate a lot of this.
155+
#
156+
# https://github.com/orgs/community/discussions/65321
157+
- name: Create new pull request
158+
uses: actions/github-script@v7
159+
id: pull-request
160+
with:
161+
github-token: ${{ secrets.GH_TOKEN_RELEASE_AUTOMATION }}
162+
script: |
163+
const version = '${{ env.VERSION }}';
164+
const prTitle = `chore(release): v${version}`;
165+
const branchName = `release-v${version}`;
166+
const changelog = ${{ steps.changelog.outputs.result }};
167+
153168
const prCreateData = {
154169
owner: context.repo.owner,
155170
repo: context.repo.repo,
156171
title: prTitle,
157172
head: '${{ env.BRANCH_NAME }}',
158173
base: 'main',
159174
body: changelog,
160-
labels: ['release']
161175
};
162176
console.log('Creating new PR. Context:');
163177
console.dir(prCreateData);
@@ -166,10 +180,12 @@ jobs:
166180
console.log(`Created new PR #${pr.number}`);
167181
168182
// Add labels if they don't exist
169-
console.log('Updating PR labels')
183+
console.log('Creating PR labels')
170184
await github.rest.issues.addLabels({
171185
owner: context.repo.owner,
172186
repo: context.repo.repo,
173187
issue_number: pr.number,
174188
labels: ['release']
175189
});
190+
191+
return pr.number;

0 commit comments

Comments
 (0)