chore(deps): bump fast-uri from 3.1.2 to 3.1.4 #3942
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Sync Issue Lifecycle State | |
| on: | |
| issues: | |
| types: [assigned, reopened, closed] | |
| pull_request: | |
| types: [closed] | |
| permissions: | |
| issues: write | |
| pull-requests: read | |
| jobs: | |
| sync-state: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Update lifecycle labels | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const lifecycleLabels = [ | |
| { name: 'ready', color: '0e8a16', description: 'Triaged and ready for an agent to pick up' }, | |
| { name: 'in-progress', color: 'fbca04', description: 'Issue is actively being worked on' }, | |
| { name: 'in-develop', color: '1d76db', description: 'Code is merged to develop, not yet on main' }, | |
| { name: 'in-main', color: '5319e7', description: 'Code is on main and will be included in the next release' }, | |
| { name: 'released', color: '0e8a16', description: 'Included in a published release' }, | |
| { name: 'needs-human', color: 'b60205', description: 'Agent could not safely proceed and needs maintainer help' }, | |
| { name: 'blocked-by-graduation', color: 'd4c5f9', description: 'Ready technically, but blocked by release phase or graduation policy' } | |
| ]; | |
| async function ensureLabel(label) { | |
| try { | |
| await github.rest.issues.getLabel({ owner, repo, name: label.name }); | |
| } catch (error) { | |
| if (error.status !== 404) throw error; | |
| await github.rest.issues.createLabel({ owner, repo, ...label }); | |
| } | |
| } | |
| async function addLabels(issueNumber, labels) { | |
| if (!labels.length) return; | |
| try { | |
| await github.rest.issues.addLabels({ owner, repo, issue_number: issueNumber, labels }); | |
| } catch (error) { | |
| if (error.status !== 403) throw error; | |
| core.warning(`Label add blocked (403) on issue #${issueNumber} — likely a fork PR with read-only token.`); | |
| } | |
| } | |
| async function removeLabel(issueNumber, label) { | |
| try { | |
| await github.rest.issues.removeLabel({ owner, repo, issue_number: issueNumber, name: label }); | |
| } catch (error) { | |
| if (error.status !== 404 && error.status !== 403) throw error; | |
| if (error.status === 403) { | |
| core.warning(`Label remove blocked (403) on issue #${issueNumber} — likely a fork PR with read-only token.`); | |
| } | |
| } | |
| } | |
| function extractLinkedIssueNumbers(text) { | |
| const matches = [...text.matchAll(/(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi)]; | |
| return [...new Set(matches.map((match) => Number(match[1])))]; | |
| } | |
| for (const label of lifecycleLabels) { | |
| await ensureLabel(label); | |
| } | |
| if (context.eventName === 'issues') { | |
| const issue = context.payload.issue; | |
| const issueNumber = issue.number; | |
| const action = context.payload.action; | |
| if (action === 'assigned') { | |
| await removeLabel(issueNumber, 'ready'); | |
| await addLabels(issueNumber, ['in-progress']); | |
| core.info(`Marked issue #${issueNumber} as in-progress.`); | |
| return; | |
| } | |
| if (action === 'reopened') { | |
| await removeLabel(issueNumber, 'released'); | |
| const assignees = issue.assignees || []; | |
| await addLabels(issueNumber, [assignees.length > 0 ? 'in-progress' : 'ready']); | |
| core.info(`Restored lifecycle state for issue #${issueNumber}.`); | |
| return; | |
| } | |
| if (action === 'closed') { | |
| for (const label of ['ready', 'in-progress', 'in-develop', 'in-main']) { | |
| await removeLabel(issueNumber, label); | |
| } | |
| const currentLabelNames = (issue.labels || []).map((label) => label.name); | |
| const nonReleaseClosure = currentLabelNames.some((label) => ['duplicate', 'invalid', 'wontfix'].includes(label)); | |
| if (issue.state_reason === 'completed' && !nonReleaseClosure) { | |
| await addLabels(issueNumber, ['released']); | |
| } | |
| core.info(`Finalized lifecycle labels for issue #${issueNumber}.`); | |
| } | |
| return; | |
| } | |
| if (context.eventName === 'pull_request') { | |
| const pr = context.payload.pull_request; | |
| if (!pr.merged) { | |
| core.info('Pull request was closed without merge; no lifecycle update needed.'); | |
| return; | |
| } | |
| const baseRef = pr.base.ref; | |
| if (!['develop', 'main'].includes(baseRef)) { | |
| core.info(`PR merged into ${baseRef}; lifecycle sync only applies to develop/main.`); | |
| return; | |
| } | |
| const issueNumbers = extractLinkedIssueNumbers(`${pr.title}\n${pr.body || ''}`); | |
| if (issueNumbers.length === 0) { | |
| core.info('No linked issues found in PR body/title.'); | |
| return; | |
| } | |
| const targetLabel = baseRef === 'develop' ? 'in-develop' : 'in-main'; | |
| for (const issueNumber of issueNumbers) { | |
| await removeLabel(issueNumber, 'ready'); | |
| await removeLabel(issueNumber, 'in-progress'); | |
| if (targetLabel === 'in-main') { | |
| await removeLabel(issueNumber, 'in-develop'); | |
| } | |
| await addLabels(issueNumber, [targetLabel]); | |
| } | |
| core.info(`Updated linked issues for PR #${pr.number} -> ${targetLabel}.`); | |
| } |