From b4eb49af771a93880ac142058355414dda6e4624 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:53:21 +0000 Subject: [PATCH 1/4] Initial plan From 0cf91964072ad97ee3b2e55d1b8e4c816030d0a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 22:01:56 +0000 Subject: [PATCH 2/4] Fix agentics-maintenance.yml syntax errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change /// to // on TypeScript reference directive (line 21) - Fix indentation of entire JavaScript block to match YAML structure - Remove trailing spaces on lines with 'comment {', 'id', 'url', 'discussion {' - Remove extra blank line at end of file - Verify workflow name exists: "Agentics Maintenance" ✓ Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/agentics-maintenance.yml | 557 ++++++++++----------- 1 file changed, 278 insertions(+), 279 deletions(-) diff --git a/.github/workflows/agentics-maintenance.yml b/.github/workflows/agentics-maintenance.yml index 978c48c15f..1112179462 100644 --- a/.github/workflows/agentics-maintenance.yml +++ b/.github/workflows/agentics-maintenance.yml @@ -18,285 +18,284 @@ jobs: with: script: | // @ts-check -/// - -/** - * Maximum number of discussions to update per run - */ -const MAX_UPDATES_PER_RUN = 100; - -/** - * Delay between GraphQL API calls in milliseconds to avoid rate limiting - */ -const GRAPHQL_DELAY_MS = 500; - -/** - * Delay execution for a specified number of milliseconds - * @param {number} ms - Milliseconds to delay - * @returns {Promise} - */ -function delay(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -/** - * Search for open discussions with expiration markers - * @param {any} github - GitHub GraphQL instance - * @param {string} owner - Repository owner - * @param {string} repo - Repository name - * @returns {Promise>} Matching discussions - */ -async function searchDiscussionsWithExpiration(github, owner, repo) { - const discussions = []; - let hasNextPage = true; - let cursor = null; - - while (hasNextPage) { - const query = ` - query($owner: String!, $repo: String!, $cursor: String) { - repository(owner: $owner, name: $repo) { - discussions(first: 100, after: $cursor, states: [OPEN]) { - pageInfo { - hasNextPage - endCursor + // + + /** + * Maximum number of discussions to update per run + */ + const MAX_UPDATES_PER_RUN = 100; + + /** + * Delay between GraphQL API calls in milliseconds to avoid rate limiting + */ + const GRAPHQL_DELAY_MS = 500; + + /** + * Delay execution for a specified number of milliseconds + * @param {number} ms - Milliseconds to delay + * @returns {Promise} + */ + function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); } - nodes { - id - number - title - url - body - createdAt + + /** + * Search for open discussions with expiration markers + * @param {any} github - GitHub GraphQL instance + * @param {string} owner - Repository owner + * @param {string} repo - Repository name + * @returns {Promise>} Matching discussions + */ + async function searchDiscussionsWithExpiration(github, owner, repo) { + const discussions = []; + let hasNextPage = true; + let cursor = null; + + while (hasNextPage) { + const query = ` + query($owner: String!, $repo: String!, $cursor: String) { + repository(owner: $owner, name: $repo) { + discussions(first: 100, after: $cursor, states: [OPEN]) { + pageInfo { + hasNextPage + endCursor + } + nodes { + id + number + title + url + body + createdAt + } + } + } + } + `; + + const result = await github.graphql(query, { + owner: owner, + repo: repo, + cursor: cursor, + }); + + if (!result || !result.repository || !result.repository.discussions) { + break; + } + + const nodes = result.repository.discussions.nodes || []; + + // Filter for discussions with agentic workflow markers and expiration comments + for (const discussion of nodes) { + // Check if created by an agentic workflow (body contains "> AI generated by" at start of line) + const agenticPattern = /^> AI generated by/m; + const isAgenticWorkflow = discussion.body && agenticPattern.test(discussion.body); + + if (!isAgenticWorkflow) { + continue; + } + + // Check if has expiration marker + const expirationPattern = //; + const match = discussion.body ? discussion.body.match(expirationPattern) : null; + + if (match) { + discussions.push(discussion); + } + } + + hasNextPage = result.repository.discussions.pageInfo.hasNextPage; + cursor = result.repository.discussions.pageInfo.endCursor; + } + + return discussions; + } + + /** + * Extract expiration date from discussion body + * @param {string} body - Discussion body + * @returns {Date|null} Expiration date or null if not found/invalid + */ + function extractExpirationDate(body) { + const expirationPattern = //; + const match = body.match(expirationPattern); + + if (!match) { + return null; + } + + const expirationISO = match[1].trim(); + const expirationDate = new Date(expirationISO); + + // Validate the date + if (isNaN(expirationDate.getTime())) { + return null; + } + + return expirationDate; + } + + /** + * Validate discussion creation date + * @param {string} createdAt - ISO 8601 creation date + * @returns {boolean} True if valid + */ + function validateCreationDate(createdAt) { + const creationDate = new Date(createdAt); + return !isNaN(creationDate.getTime()); + } + + /** + * Add comment to a GitHub Discussion using GraphQL + * @param {any} github - GitHub GraphQL instance + * @param {string} discussionId - Discussion node ID + * @param {string} message - Comment body + * @returns {Promise<{id: string, url: string}>} Comment details + */ + async function addDiscussionComment(github, discussionId, message) { + const result = await github.graphql( + ` + mutation($dId: ID!, $body: String!) { + addDiscussionComment(input: { discussionId: $dId, body: $body }) { + comment { + id + url + } + } + }`, + { dId: discussionId, body: message } + ); + + return result.addDiscussionComment.comment; + } + + /** + * Close a GitHub Discussion as OUTDATED using GraphQL + * @param {any} github - GitHub GraphQL instance + * @param {string} discussionId - Discussion node ID + * @returns {Promise<{id: string, url: string}>} Discussion details + */ + async function closeDiscussionAsOutdated(github, discussionId) { + const result = await github.graphql( + ` + mutation($dId: ID!) { + closeDiscussion(input: { discussionId: $dId, reason: OUTDATED }) { + discussion { + id + url + } + } + }`, + { dId: discussionId } + ); + + return result.closeDiscussion.discussion; + } + + async function main() { + const owner = context.repo.owner; + const repo = context.repo.repo; + + core.info(`Searching for expired discussions in ${owner}/${repo}`); + + // Search for discussions with expiration markers + const discussionsWithExpiration = await searchDiscussionsWithExpiration(github, owner, repo); + + if (discussionsWithExpiration.length === 0) { + core.info("No discussions with expiration markers found"); + return; + } + + core.info(`Found ${discussionsWithExpiration.length} discussion(s) with expiration markers`); + + // Check which discussions are expired + const now = new Date(); + const expiredDiscussions = []; + + for (const discussion of discussionsWithExpiration) { + // Validate creation date + if (!validateCreationDate(discussion.createdAt)) { + core.warning(`Discussion #${discussion.number} has invalid creation date, skipping`); + continue; + } + + // Extract and validate expiration date + const expirationDate = extractExpirationDate(discussion.body); + if (!expirationDate) { + core.warning(`Discussion #${discussion.number} has invalid expiration date, skipping`); + continue; + } + + // Check if expired + if (now >= expirationDate) { + expiredDiscussions.push({ + ...discussion, + expirationDate: expirationDate, + }); + } + } + + if (expiredDiscussions.length === 0) { + core.info("No expired discussions found"); + return; + } + + core.info(`Found ${expiredDiscussions.length} expired discussion(s)`); + + // Limit to MAX_UPDATES_PER_RUN + const discussionsToClose = expiredDiscussions.slice(0, MAX_UPDATES_PER_RUN); + + if (expiredDiscussions.length > MAX_UPDATES_PER_RUN) { + core.warning(`Found ${expiredDiscussions.length} expired discussions, but only closing the first ${MAX_UPDATES_PER_RUN}`); + } + + let closedCount = 0; + const closedDiscussions = []; + + for (let i = 0; i < discussionsToClose.length; i++) { + const discussion = discussionsToClose[i]; + + try { + const closingMessage = `This discussion was automatically closed because it expired on ${discussion.expirationDate.toISOString()}.`; + + // Add comment first + core.info(`Adding closing comment to discussion #${discussion.number}`); + await addDiscussionComment(github, discussion.id, closingMessage); + + // Then close the discussion as outdated + core.info(`Closing discussion #${discussion.number} as outdated`); + await closeDiscussionAsOutdated(github, discussion.id); + + closedDiscussions.push({ + number: discussion.number, + url: discussion.url, + title: discussion.title, + }); + + closedCount++; + core.info(`✓ Closed discussion #${discussion.number}: ${discussion.url}`); + } catch (error) { + core.error(`✗ Failed to close discussion #${discussion.number}: ${error instanceof Error ? error.message : String(error)}`); + // Continue with other discussions even if one fails + } + + // Add delay between GraphQL operations to avoid rate limiting (except for the last item) + if (i < discussionsToClose.length - 1) { + await delay(GRAPHQL_DELAY_MS); + } + } + + // Write summary + if (closedCount > 0) { + let summaryContent = `## Closed Expired Discussions\n\n`; + summaryContent += `Closed **${closedCount}** expired discussion(s):\n\n`; + for (const closed of closedDiscussions) { + summaryContent += `- Discussion #${closed.number}: [${closed.title}](${closed.url})\n`; + } + await core.summary.addRaw(summaryContent).write(); + } + + core.info(`Successfully closed ${closedCount} expired discussion(s)`); } - } - } - } - `; - - const result = await github.graphql(query, { - owner: owner, - repo: repo, - cursor: cursor, - }); - - if (!result || !result.repository || !result.repository.discussions) { - break; - } - - const nodes = result.repository.discussions.nodes || []; - - // Filter for discussions with agentic workflow markers and expiration comments - for (const discussion of nodes) { - // Check if created by an agentic workflow (body contains "> AI generated by" at start of line) - const agenticPattern = /^> AI generated by/m; - const isAgenticWorkflow = discussion.body && agenticPattern.test(discussion.body); - - if (!isAgenticWorkflow) { - continue; - } - - // Check if has expiration marker - const expirationPattern = //; - const match = discussion.body ? discussion.body.match(expirationPattern) : null; - - if (match) { - discussions.push(discussion); - } - } - - hasNextPage = result.repository.discussions.pageInfo.hasNextPage; - cursor = result.repository.discussions.pageInfo.endCursor; - } - - return discussions; -} - -/** - * Extract expiration date from discussion body - * @param {string} body - Discussion body - * @returns {Date|null} Expiration date or null if not found/invalid - */ -function extractExpirationDate(body) { - const expirationPattern = //; - const match = body.match(expirationPattern); - - if (!match) { - return null; - } - - const expirationISO = match[1].trim(); - const expirationDate = new Date(expirationISO); - - // Validate the date - if (isNaN(expirationDate.getTime())) { - return null; - } - - return expirationDate; -} - -/** - * Validate discussion creation date - * @param {string} createdAt - ISO 8601 creation date - * @returns {boolean} True if valid - */ -function validateCreationDate(createdAt) { - const creationDate = new Date(createdAt); - return !isNaN(creationDate.getTime()); -} - -/** - * Add comment to a GitHub Discussion using GraphQL - * @param {any} github - GitHub GraphQL instance - * @param {string} discussionId - Discussion node ID - * @param {string} message - Comment body - * @returns {Promise<{id: string, url: string}>} Comment details - */ -async function addDiscussionComment(github, discussionId, message) { - const result = await github.graphql( - ` - mutation($dId: ID!, $body: String!) { - addDiscussionComment(input: { discussionId: $dId, body: $body }) { - comment { - id - url - } - } - }`, - { dId: discussionId, body: message } - ); - - return result.addDiscussionComment.comment; -} - -/** - * Close a GitHub Discussion as OUTDATED using GraphQL - * @param {any} github - GitHub GraphQL instance - * @param {string} discussionId - Discussion node ID - * @returns {Promise<{id: string, url: string}>} Discussion details - */ -async function closeDiscussionAsOutdated(github, discussionId) { - const result = await github.graphql( - ` - mutation($dId: ID!) { - closeDiscussion(input: { discussionId: $dId, reason: OUTDATED }) { - discussion { - id - url - } - } - }`, - { dId: discussionId } - ); - - return result.closeDiscussion.discussion; -} - -async function main() { - const owner = context.repo.owner; - const repo = context.repo.repo; - - core.info(`Searching for expired discussions in ${owner}/${repo}`); - - // Search for discussions with expiration markers - const discussionsWithExpiration = await searchDiscussionsWithExpiration(github, owner, repo); - - if (discussionsWithExpiration.length === 0) { - core.info("No discussions with expiration markers found"); - return; - } - - core.info(`Found ${discussionsWithExpiration.length} discussion(s) with expiration markers`); - - // Check which discussions are expired - const now = new Date(); - const expiredDiscussions = []; - - for (const discussion of discussionsWithExpiration) { - // Validate creation date - if (!validateCreationDate(discussion.createdAt)) { - core.warning(`Discussion #${discussion.number} has invalid creation date, skipping`); - continue; - } - - // Extract and validate expiration date - const expirationDate = extractExpirationDate(discussion.body); - if (!expirationDate) { - core.warning(`Discussion #${discussion.number} has invalid expiration date, skipping`); - continue; - } - - // Check if expired - if (now >= expirationDate) { - expiredDiscussions.push({ - ...discussion, - expirationDate: expirationDate, - }); - } - } - - if (expiredDiscussions.length === 0) { - core.info("No expired discussions found"); - return; - } - - core.info(`Found ${expiredDiscussions.length} expired discussion(s)`); - - // Limit to MAX_UPDATES_PER_RUN - const discussionsToClose = expiredDiscussions.slice(0, MAX_UPDATES_PER_RUN); - - if (expiredDiscussions.length > MAX_UPDATES_PER_RUN) { - core.warning(`Found ${expiredDiscussions.length} expired discussions, but only closing the first ${MAX_UPDATES_PER_RUN}`); - } - - let closedCount = 0; - const closedDiscussions = []; - - for (let i = 0; i < discussionsToClose.length; i++) { - const discussion = discussionsToClose[i]; - - try { - const closingMessage = `This discussion was automatically closed because it expired on ${discussion.expirationDate.toISOString()}.`; - - // Add comment first - core.info(`Adding closing comment to discussion #${discussion.number}`); - await addDiscussionComment(github, discussion.id, closingMessage); - - // Then close the discussion as outdated - core.info(`Closing discussion #${discussion.number} as outdated`); - await closeDiscussionAsOutdated(github, discussion.id); - - closedDiscussions.push({ - number: discussion.number, - url: discussion.url, - title: discussion.title, - }); - - closedCount++; - core.info(`✓ Closed discussion #${discussion.number}: ${discussion.url}`); - } catch (error) { - core.error(`✗ Failed to close discussion #${discussion.number}: ${error instanceof Error ? error.message : String(error)}`); - // Continue with other discussions even if one fails - } - - // Add delay between GraphQL operations to avoid rate limiting (except for the last item) - if (i < discussionsToClose.length - 1) { - await delay(GRAPHQL_DELAY_MS); - } - } - - // Write summary - if (closedCount > 0) { - let summaryContent = `## Closed Expired Discussions\n\n`; - summaryContent += `Closed **${closedCount}** expired discussion(s):\n\n`; - for (const closed of closedDiscussions) { - summaryContent += `- Discussion #${closed.number}: [${closed.title}](${closed.url})\n`; - } - await core.summary.addRaw(summaryContent).write(); - } - - core.info(`Successfully closed ${closedCount} expired discussion(s)`); -} - -await main(); + await main(); \ No newline at end of file From 25c78ee45893bbefbbbf74bfb3ca08098dfe477f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 22:03:39 +0000 Subject: [PATCH 3/4] Fix comment indentation in agentics-maintenance.yml Fix excessive indentation (24 spaces) on first JSDoc comment block to match the consistent 12-space indentation used throughout the JavaScript code. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/agentics-maintenance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/agentics-maintenance.yml b/.github/workflows/agentics-maintenance.yml index 1112179462..169e62ee5d 100644 --- a/.github/workflows/agentics-maintenance.yml +++ b/.github/workflows/agentics-maintenance.yml @@ -20,7 +20,7 @@ jobs: // @ts-check // - /** + /** * Maximum number of discussions to update per run */ const MAX_UPDATES_PER_RUN = 100; From ed8ffad28440adc2cd99800d2ab40fe8c739a46a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 22:20:55 +0000 Subject: [PATCH 4/4] Fix agentics-maintenance.yml generation using bundler - Changed /// to // in close_expired_discussions.cjs source file - Updated maintenance_workflow.go to use WriteJavaScriptToYAML helper - This properly formats JS with indentation and removes comments - Generated workflow now has valid YAML syntax Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/agentics-maintenance.yml | 114 +----------------- .github/workflows/release.lock.yml | 6 +- pkg/workflow/js/close_expired_discussions.cjs | 2 +- pkg/workflow/maintenance_workflow.go | 17 ++- 4 files changed, 20 insertions(+), 119 deletions(-) diff --git a/.github/workflows/agentics-maintenance.yml b/.github/workflows/agentics-maintenance.yml index 169e62ee5d..bcebc75c79 100644 --- a/.github/workflows/agentics-maintenance.yml +++ b/.github/workflows/agentics-maintenance.yml @@ -17,40 +17,15 @@ jobs: uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | - // @ts-check - // - - /** - * Maximum number of discussions to update per run - */ const MAX_UPDATES_PER_RUN = 100; - - /** - * Delay between GraphQL API calls in milliseconds to avoid rate limiting - */ const GRAPHQL_DELAY_MS = 500; - - /** - * Delay execution for a specified number of milliseconds - * @param {number} ms - Milliseconds to delay - * @returns {Promise} - */ function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } - - /** - * Search for open discussions with expiration markers - * @param {any} github - GitHub GraphQL instance - * @param {string} owner - Repository owner - * @param {string} repo - Repository name - * @returns {Promise>} Matching discussions - */ async function searchDiscussionsWithExpiration(github, owner, repo) { const discussions = []; let hasNextPage = true; let cursor = null; - while (hasNextPage) { const query = ` query($owner: String!, $repo: String!, $cursor: String) { @@ -72,115 +47,70 @@ jobs: } } `; - const result = await github.graphql(query, { owner: owner, repo: repo, cursor: cursor, }); - if (!result || !result.repository || !result.repository.discussions) { break; } - const nodes = result.repository.discussions.nodes || []; - - // Filter for discussions with agentic workflow markers and expiration comments for (const discussion of nodes) { - // Check if created by an agentic workflow (body contains "> AI generated by" at start of line) const agenticPattern = /^> AI generated by/m; const isAgenticWorkflow = discussion.body && agenticPattern.test(discussion.body); - if (!isAgenticWorkflow) { continue; } - - // Check if has expiration marker const expirationPattern = //; const match = discussion.body ? discussion.body.match(expirationPattern) : null; - if (match) { discussions.push(discussion); } } - hasNextPage = result.repository.discussions.pageInfo.hasNextPage; cursor = result.repository.discussions.pageInfo.endCursor; } - return discussions; } - - /** - * Extract expiration date from discussion body - * @param {string} body - Discussion body - * @returns {Date|null} Expiration date or null if not found/invalid - */ function extractExpirationDate(body) { const expirationPattern = //; const match = body.match(expirationPattern); - if (!match) { return null; } - const expirationISO = match[1].trim(); const expirationDate = new Date(expirationISO); - - // Validate the date if (isNaN(expirationDate.getTime())) { return null; } - return expirationDate; } - - /** - * Validate discussion creation date - * @param {string} createdAt - ISO 8601 creation date - * @returns {boolean} True if valid - */ function validateCreationDate(createdAt) { const creationDate = new Date(createdAt); return !isNaN(creationDate.getTime()); } - - /** - * Add comment to a GitHub Discussion using GraphQL - * @param {any} github - GitHub GraphQL instance - * @param {string} discussionId - Discussion node ID - * @param {string} message - Comment body - * @returns {Promise<{id: string, url: string}>} Comment details - */ async function addDiscussionComment(github, discussionId, message) { const result = await github.graphql( ` mutation($dId: ID!, $body: String!) { addDiscussionComment(input: { discussionId: $dId, body: $body }) { - comment { - id + comment { + id url } } }`, { dId: discussionId, body: message } ); - return result.addDiscussionComment.comment; } - - /** - * Close a GitHub Discussion as OUTDATED using GraphQL - * @param {any} github - GitHub GraphQL instance - * @param {string} discussionId - Discussion node ID - * @returns {Promise<{id: string, url: string}>} Discussion details - */ async function closeDiscussionAsOutdated(github, discussionId) { const result = await github.graphql( ` mutation($dId: ID!) { closeDiscussion(input: { discussionId: $dId, reason: OUTDATED }) { - discussion { + discussion { id url } @@ -188,45 +118,30 @@ jobs: }`, { dId: discussionId } ); - return result.closeDiscussion.discussion; } - async function main() { const owner = context.repo.owner; const repo = context.repo.repo; - core.info(`Searching for expired discussions in ${owner}/${repo}`); - - // Search for discussions with expiration markers const discussionsWithExpiration = await searchDiscussionsWithExpiration(github, owner, repo); - if (discussionsWithExpiration.length === 0) { core.info("No discussions with expiration markers found"); return; } - core.info(`Found ${discussionsWithExpiration.length} discussion(s) with expiration markers`); - - // Check which discussions are expired const now = new Date(); const expiredDiscussions = []; - for (const discussion of discussionsWithExpiration) { - // Validate creation date if (!validateCreationDate(discussion.createdAt)) { core.warning(`Discussion #${discussion.number} has invalid creation date, skipping`); continue; } - - // Extract and validate expiration date const expirationDate = extractExpirationDate(discussion.body); if (!expirationDate) { core.warning(`Discussion #${discussion.number} has invalid expiration date, skipping`); continue; } - - // Check if expired if (now >= expirationDate) { expiredDiscussions.push({ ...discussion, @@ -234,58 +149,39 @@ jobs: }); } } - if (expiredDiscussions.length === 0) { core.info("No expired discussions found"); return; } - core.info(`Found ${expiredDiscussions.length} expired discussion(s)`); - - // Limit to MAX_UPDATES_PER_RUN const discussionsToClose = expiredDiscussions.slice(0, MAX_UPDATES_PER_RUN); - if (expiredDiscussions.length > MAX_UPDATES_PER_RUN) { core.warning(`Found ${expiredDiscussions.length} expired discussions, but only closing the first ${MAX_UPDATES_PER_RUN}`); } - let closedCount = 0; const closedDiscussions = []; - for (let i = 0; i < discussionsToClose.length; i++) { const discussion = discussionsToClose[i]; - try { const closingMessage = `This discussion was automatically closed because it expired on ${discussion.expirationDate.toISOString()}.`; - - // Add comment first core.info(`Adding closing comment to discussion #${discussion.number}`); await addDiscussionComment(github, discussion.id, closingMessage); - - // Then close the discussion as outdated core.info(`Closing discussion #${discussion.number} as outdated`); await closeDiscussionAsOutdated(github, discussion.id); - closedDiscussions.push({ number: discussion.number, url: discussion.url, title: discussion.title, }); - closedCount++; core.info(`✓ Closed discussion #${discussion.number}: ${discussion.url}`); } catch (error) { core.error(`✗ Failed to close discussion #${discussion.number}: ${error instanceof Error ? error.message : String(error)}`); - // Continue with other discussions even if one fails } - - // Add delay between GraphQL operations to avoid rate limiting (except for the last item) if (i < discussionsToClose.length - 1) { await delay(GRAPHQL_DELAY_MS); } } - - // Write summary if (closedCount > 0) { let summaryContent = `## Closed Expired Discussions\n\n`; summaryContent += `Closed **${closedCount}** expired discussion(s):\n\n`; @@ -294,8 +190,6 @@ jobs: } await core.summary.addRaw(summaryContent).write(); } - core.info(`Successfully closed ${closedCount} expired discussion(s)`); } - - await main(); \ No newline at end of file + await main(); diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 160a26701e..a73e060474 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -6119,19 +6119,19 @@ jobs: - name: Download Go modules run: go mod download - name: Generate SBOM (SPDX format) - uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 with: artifact-name: sbom.spdx.json format: spdx-json output-file: sbom.spdx.json - name: Generate SBOM (CycloneDX format) - uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 with: artifact-name: sbom.cdx.json format: cyclonedx-json output-file: sbom.cdx.json - name: Upload SBOM artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 with: name: sbom-artifacts path: | diff --git a/pkg/workflow/js/close_expired_discussions.cjs b/pkg/workflow/js/close_expired_discussions.cjs index 7af39b7a6c..8cf285b39d 100644 --- a/pkg/workflow/js/close_expired_discussions.cjs +++ b/pkg/workflow/js/close_expired_discussions.cjs @@ -1,5 +1,5 @@ // @ts-check -/// +// /** * Maximum number of discussions to update per run diff --git a/pkg/workflow/maintenance_workflow.go b/pkg/workflow/maintenance_workflow.go index 86d57e164f..eb1cc910a2 100644 --- a/pkg/workflow/maintenance_workflow.go +++ b/pkg/workflow/maintenance_workflow.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/githubnext/gh-aw/pkg/logger" ) @@ -34,9 +35,10 @@ func GenerateMaintenanceWorkflow(workflowDataList []*WorkflowData, workflowDir s maintenanceLog.Print("Generating maintenance workflow for expired discussions") - // Create the maintenance workflow content - script := getMaintenanceScript() - content := fmt.Sprintf(`name: Agentics Maintenance + // Create the maintenance workflow content using strings.Builder + var yaml strings.Builder + + yaml.WriteString(`name: Agentics Maintenance on: schedule: @@ -55,8 +57,13 @@ jobs: uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | - %s -`, script) +`) + + // Add the JavaScript script with proper indentation + script := getMaintenanceScript() + WriteJavaScriptToYAML(&yaml, script) + + content := yaml.String() // Write the maintenance workflow file maintenanceFile := filepath.Join(workflowDir, "agentics-maintenance.yml")