Skip to content

Commit 92f0294

Browse files
committed
chore: move logic to a script
--- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: na - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: na - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: na - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: passed - task: lint_typescript_declarations status: passed - task: lint_typescript_tests status: na - task: lint_license_headers status: passed ---
1 parent 572ac55 commit 92f0294

3 files changed

Lines changed: 327 additions & 177 deletions

File tree

.github/workflows/create_todo_issue.yml

Lines changed: 15 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -74,139 +74,24 @@ jobs:
7474

7575
# Define the job's steps:
7676
steps:
77+
# Checkout the repository:
78+
- name: 'Checkout repository'
79+
# Pin action to full length commit SHA
80+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
81+
with:
82+
# Ensure we have access to the scripts directory:
83+
sparse-checkout: |
84+
.github/workflows/scripts
85+
sparse-checkout-cone-mode: false
86+
timeout-minutes: 10
87+
7788
# Create a new todo issue:
7889
- name: 'Create a new todo issue'
79-
# Pin action to full length commit SHA
80-
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
8190
env:
91+
GITHUB_TOKEN: ${{ secrets.STDLIB_BOT_GITHUB_TOKEN || secrets.STDLIB_BOT_PAT_REPO_WRITE }}
8292
COMMENT_BODY: ${{ inputs.comment_body }}
8393
COMMENTER: ${{ inputs.user }}
8494
PR_NUMBER: ${{ inputs.pull_request_number }}
85-
with:
86-
github-token: ${{ secrets.STDLIB_BOT_GITHUB_TOKEN }}
87-
script: |
88-
const commentBody = process.env.COMMENT_BODY.trim();
89-
const commenter = process.env.COMMENTER;
90-
const prNumber = parseInt( process.env.PR_NUMBER, 10 );
91-
92-
// Parse the fenced code block following '/stdlib todo':
93-
const RE_CODE_BLOCK = /```text\s*\{([^}]*)\}\s*\n([\s\S]*?)```/;
94-
const blockMatch = commentBody.match( RE_CODE_BLOCK );
95-
if ( !blockMatch ) {
96-
const lines = commentBody.split( '\n' );
97-
const quote = lines.map( line => `> ${line}` ).join( '\n' );
98-
await github.rest.issues.createComment({
99-
'owner': context.repo.owner,
100-
'repo': context.repo.repo,
101-
'issue_number': prNumber,
102-
'body': `${quote}\n\n@${commenter}, failed to parse the \`/stdlib todo\` command. Expected a fenced code block with attributes, e.g.,\n\n\`\`\`\n/stdlib todo\n\n\\\`\\\`\\\`text {stdlib=public labels="foo,bar"}\n[TODO]: Issue title\n\nIssue body\n\\\`\\\`\\\`\n\`\`\``
103-
});
104-
core.setFailed( 'No code block found in /stdlib todo comment.' );
105-
return;
106-
}
107-
108-
const attrs = blockMatch[ 1 ];
109-
const content = blockMatch[ 2 ].trim();
110-
111-
// Parse 'stdlib' attribute (values: 'public' or 'private'):
112-
const stdlibAttrMatch = attrs.match( /stdlib=(\w+)/i );
113-
const stdlibTarget = stdlibAttrMatch ? stdlibAttrMatch[ 1 ].toLowerCase() : 'public';
114-
if ( stdlibTarget !== 'public' && stdlibTarget !== 'private' ) {
115-
const lines = commentBody.split( '\n' );
116-
const quote = lines.map( line => `> ${line}` ).join( '\n' );
117-
await github.rest.issues.createComment({
118-
'owner': context.repo.owner,
119-
'repo': context.repo.repo,
120-
'issue_number': prNumber,
121-
'body': `${quote}\n\n@${commenter}, unrecognized \`stdlib\` attribute value \`${stdlibTarget}\`. Valid values are \`public\` (opens issue on \`stdlib-js/stdlib\`) or \`private\` (opens issue on the internal todo repository).`
122-
});
123-
core.setFailed( `Unrecognized stdlib attribute value: ${stdlibTarget}` );
124-
return;
125-
}
126-
127-
// Parse 'labels' attribute (comma-separated list):
128-
const labelsAttrMatch = attrs.match( /labels="([^"]*)"/i );
129-
const labels = labelsAttrMatch
130-
? labelsAttrMatch[ 1 ].split( ',' ).map( l => l.trim() ).filter( Boolean )
131-
: [];
132-
133-
// Parse the issue title from the '[TODO]:' line:
134-
const contentLines = content.split( '\n' );
135-
const RE_TITLE = /^\[TODO\]:\s*(.+)/i;
136-
const titleMatch = contentLines[ 0 ].match( RE_TITLE );
137-
if ( !titleMatch ) {
138-
const lines = commentBody.split( '\n' );
139-
const quote = lines.map( line => `> ${line}` ).join( '\n' );
140-
await github.rest.issues.createComment({
141-
'owner': context.repo.owner,
142-
'repo': context.repo.repo,
143-
'issue_number': prNumber,
144-
'body': `${quote}\n\n@${commenter}, failed to parse the todo title. The first line of the code block must be of the form \`[TODO]: Issue title\`.`
145-
});
146-
core.setFailed( 'No [TODO]: line found in the code block.' );
147-
return;
148-
}
149-
150-
const title = titleMatch[ 1 ].trim();
151-
const body = contentLines.slice( 1 ).join( '\n' ).trim();
152-
153-
// Determine the target repository:
154-
const targetOwner = 'stdlib-js';
155-
const targetRepo = ( stdlibTarget === 'private' ) ? 'todo' : 'stdlib';
156-
157-
// Verify the commenter is an org member:
158-
try {
159-
await github.rest.orgs.checkMembershipForUser({
160-
'org': targetOwner,
161-
'username': commenter
162-
});
163-
} catch ( err ) {
164-
console.log( 'Error checking org membership: %s', err.message );
165-
const lines = commentBody.split( '\n' );
166-
const quote = lines.map( line => `> ${line}` ).join( '\n' );
167-
await github.rest.issues.createComment({
168-
'owner': context.repo.owner,
169-
'repo': context.repo.repo,
170-
'issue_number': prNumber,
171-
'body': `${quote}\n\n@${commenter}, you must be a member of the \`${targetOwner}\` organization to use the \`/stdlib todo\` command.`
172-
});
173-
core.setFailed( `User ${commenter} is not a member of ${targetOwner}.` );
174-
return;
175-
}
176-
177-
// Build issue creation parameters:
178-
const issueParams = {
179-
'owner': targetOwner,
180-
'repo': targetRepo,
181-
'title': title
182-
};
183-
// Build provenance footer:
184-
const prUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/pull/${prNumber}`;
185-
const provenance = `\n\n---\n*Created via \`/stdlib todo\` from [${context.repo.owner}/${context.repo.repo}#${prNumber}](${prUrl}) by @${commenter}.*`;
186-
187-
if ( body ) {
188-
issueParams.body = body + provenance;
189-
} else {
190-
issueParams.body = provenance.trim();
191-
}
192-
if ( labels.length > 0 ) {
193-
issueParams.labels = labels;
194-
}
195-
196-
// Create the issue:
197-
const issue = await github.rest.issues.create( issueParams );
198-
199-
// Check for labels that were silently dropped (do not exist in the target repo):
200-
const appliedLabels = issue.data.labels.map( l => l.name );
201-
const droppedLabels = labels.filter( l => !appliedLabels.includes( l ) );
202-
203-
// Post a confirmation comment:
204-
const confirmBody = droppedLabels.length > 0
205-
? `@${commenter}, the following todo issue has been created: ${issue.data.html_url}\n\n> [!WARNING]\n> The following labels were not applied because they do not exist on \`${targetOwner}/${targetRepo}\`: ${droppedLabels.map( l => `\`${l}\`` ).join( ', ' )}.`
206-
: `@${commenter}, the following todo issue has been created: ${issue.data.html_url}`;
207-
await github.rest.issues.createComment({
208-
'owner': context.repo.owner,
209-
'repo': context.repo.repo,
210-
'issue_number': prNumber,
211-
'body': confirmBody
212-
});
95+
run: |
96+
. "$GITHUB_WORKSPACE/.github/workflows/scripts/create_todo_issue/run"
97+
timeout-minutes: 10

0 commit comments

Comments
 (0)