@@ -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