Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 51 additions & 10 deletions .github/workflows/agentics-maintenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -359,19 +359,20 @@ jobs:
restore-keys: |
${{ runner.os }}-activity-report-logs-${{ github.repository }}-
${{ runner.os }}-activity-report-logs-
- name: Generate agentic workflow activity report
uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
- name: Download activity report logs
timeout-minutes: 20
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step timeout (20 minutes) is lower than the script’s worst-case runtime: run_activity_report.cjs runs two sequential log downloads with a default 20-minute timeout each, plus settle delays. The step can be terminated by Actions before the script’s own timeout/cleanup completes or before the second range runs. Consider increasing the step timeout above the script’s maximum expected duration, or reducing the per-download timeout so the step stays within 20 minutes end-to-end.

Suggested change
timeout-minutes: 20
timeout-minutes: 50

Copilot uses AI. Check for mistakes.
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_AW_CMD_PREFIX: ./gh-aw
GH_AW_ACTIVITY_REPORT_OUTPUT_DIR: ./.cache/gh-aw/activity-report-logs
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/run_activity_report.cjs');
await main();
run: |
${GH_AW_CMD_PREFIX} logs \
--repo "${{ github.repository }}" \
--start-date -1w \
--count 100 \
--output ./.cache/gh-aw/activity-report-logs \
--format markdown \
> ./.cache/gh-aw/activity-report-logs/report.md

- name: Save activity report logs cache
if: ${{ always() }}
Expand All @@ -380,6 +381,46 @@ jobs:
path: ./.cache/gh-aw/activity-report-logs
key: ${{ steps.activity_report_logs_cache.outputs.cache-primary-key }}

- name: Generate activity report issue
uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('node:fs');
const reportPath = './.cache/gh-aw/activity-report-logs/report.md';
if (!fs.existsSync(reportPath)) {
core.warning(`Activity report markdown not found at ${reportPath}; skipping issue creation.`);
return;
}
let reportBody = '';
try {
reportBody = fs.readFileSync(reportPath, 'utf8').trim();
} catch (error) {
core.warning(`Failed to read activity report markdown at ${reportPath}: ${error.message}`);
return;
}
if (!reportBody) {
core.warning(`Activity report markdown is empty at ${reportPath}; skipping issue creation.`);
return;
}
const repoSlug = `${context.repo.owner}/${context.repo.repo}`;
const body = [
'### Agentic workflow activity report',
'',
`Repository: ${repoSlug}`,
`Generated at: ${new Date().toISOString()}`,
'',
reportBody,
].join('\n');
const createdIssue = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '[aw] agentic status report',
body,
labels: ['agentic-workflows'],
});
core.info(`Created issue #${createdIssue.data.number}: ${createdIssue.data.html_url}`);

close_agentic_workflows_issues:
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'close_agentic_workflows_issues' && (!(github.event.repository.fork)) }}
runs-on: ubuntu-slim
Expand Down
28 changes: 26 additions & 2 deletions pkg/workflow/maintenance_workflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,32 @@ func TestGenerateMaintenanceWorkflow_OperationJobConditions(t *testing.T) {
t.Errorf("Job activity_report cache key should include run_id for latest-cache resolution in:\n%s", yaml)
}

if !strings.Contains(yaml, "GH_AW_ACTIVITY_REPORT_OUTPUT_DIR: ./.cache/gh-aw/activity-report-logs") {
t.Errorf("Job activity_report should set GH_AW_ACTIVITY_REPORT_OUTPUT_DIR in:\n%s", yaml)
if !strings.Contains(yaml, "Download activity report logs") {
t.Errorf("Job activity_report should include direct logs download step in:\n%s", yaml)
}
if !strings.Contains(yaml, "timeout-minutes: 20") {
t.Errorf("Job activity_report logs download step should set timeout-minutes: 20 in:\n%s", yaml)
}
if !strings.Contains(yaml, "${GH_AW_CMD_PREFIX} logs") {
t.Errorf("Job activity_report should run gh aw logs directly in:\n%s", yaml)
}
if !strings.Contains(yaml, "--start-date -1w") {
t.Errorf("Job activity_report gh aw logs command should include --start-date -1w in:\n%s", yaml)
}
if !strings.Contains(yaml, "--count 100") {
t.Errorf("Job activity_report gh aw logs command should include --count 100 in:\n%s", yaml)
}
if !strings.Contains(yaml, "--format markdown") {
t.Errorf("Job activity_report gh aw logs command should include --format markdown in:\n%s", yaml)
}
if !strings.Contains(yaml, "./.cache/gh-aw/activity-report-logs/report.md") {
t.Errorf("Job activity_report gh aw logs command should write report markdown output to report.md in:\n%s", yaml)
}
if !strings.Contains(yaml, "Generate activity report issue") {
t.Errorf("Job activity_report should include issue generation step after cache save in:\n%s", yaml)
}
if !strings.Contains(yaml, "title: '[aw] agentic status report'") {
t.Errorf("Job activity_report issue generation step should create the activity report issue title in:\n%s", yaml)
}

// close_agentic_workflows_issues job should be triggered when operation == 'close_agentic_workflows_issues'
Expand Down
61 changes: 51 additions & 10 deletions pkg/workflow/maintenance_workflow_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,26 +394,67 @@ jobs:
${{ runner.os }}-activity-report-logs-${{ github.repository }}-
${{ runner.os }}-activity-report-logs-
`)
yaml.WriteString(` - name: Generate agentic workflow activity report
uses: ` + getCachedActionPinFromResolver("actions/github-script", resolver) + `
yaml.WriteString(` - name: Download activity report logs
timeout-minutes: 20
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The step-level timeout-minutes: 20 conflicts with the script’s per-command timeout defaults: run_activity_report.cjs can run two sequential log downloads, each allowed up to 20 minutes, plus settle delays and issue creation. With a 20-minute step timeout, the step can be killed before the script-level timeout/cleanup logic completes (and before the second range runs). Consider making the step timeout comfortably larger than the worst-case script runtime, or reducing the per-command timeout so the overall step stays within 20 minutes.

Suggested change
timeout-minutes: 20
timeout-minutes: 60

Copilot uses AI. Check for mistakes.
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_AW_CMD_PREFIX: ` + getCLICmdPrefix(actionMode) + `
GH_AW_ACTIVITY_REPORT_OUTPUT_DIR: ./.cache/gh-aw/activity-report-logs
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/run_activity_report.cjs');
await main();
run: |
${GH_AW_CMD_PREFIX} logs \
--repo "${{ github.repository }}" \
--start-date -1w \
--count 100 \
--output ./.cache/gh-aw/activity-report-logs \
--format markdown \
> ./.cache/gh-aw/activity-report-logs/report.md

- name: Save activity report logs cache
if: ${{ always() }}
uses: ` + getActionPin("actions/cache/save") + `
with:
path: ./.cache/gh-aw/activity-report-logs
key: ${{ steps.activity_report_logs_cache.outputs.cache-primary-key }}

- name: Generate activity report issue
uses: ` + getCachedActionPinFromResolver("actions/github-script", resolver) + `
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('node:fs');
const reportPath = './.cache/gh-aw/activity-report-logs/report.md';
if (!fs.existsSync(reportPath)) {
core.warning('Activity report markdown not found at ' + reportPath + '; skipping issue creation.');
return;
}
let reportBody = '';
try {
reportBody = fs.readFileSync(reportPath, 'utf8').trim();
} catch (error) {
core.warning('Failed to read activity report markdown at ' + reportPath + ': ' + error.message);
return;
}
if (!reportBody) {
core.warning('Activity report markdown is empty at ' + reportPath + '; skipping issue creation.');
return;
}
const repoSlug = context.repo.owner + '/' + context.repo.repo;
const body = [
'### Agentic workflow activity report',
'',
'Repository: ' + repoSlug,
'Generated at: ' + new Date().toISOString(),
'',
reportBody,
].join('\n');
const createdIssue = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '[aw] agentic status report',
body,
labels: ['agentic-workflows'],
});
core.info('Created issue #' + createdIssue.data.number + ': ' + createdIssue.data.html_url);
`)

// Add close_agentic_workflows_issues job for workflow_dispatch with operation == 'close_agentic_workflows_issues'
Expand Down
66 changes: 56 additions & 10 deletions pkg/workflow/side_repo_maintenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,27 +469,73 @@ jobs:
${{ runner.os }}-activity-report-logs-` + repoSlug + `-
${{ runner.os }}-activity-report-logs-
`)
yaml.WriteString(` - name: Generate agentic workflow activity report in target repository
uses: ` + getCachedActionPinFromResolver("actions/github-script", resolver) + `
yaml.WriteString(` - name: Download activity report logs in target repository
timeout-minutes: 20
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The step-level timeout-minutes: 20 may be too low given the script’s behavior: run_activity_report.cjs runs multiple log downloads sequentially with a default 20-minute timeout per download, plus settle delays. This means the workflow step can time out before the script’s own timeout/cleanup kicks in or before all ranges complete. Align the step timeout with the script’s worst-case runtime, or lower the per-download timeout accordingly.

Suggested change
timeout-minutes: 20
timeout-minutes: 120

Copilot uses AI. Check for mistakes.
shell: bash
env:
GH_TOKEN: ` + token + `
GH_AW_CMD_PREFIX: ` + getCLICmdPrefix(actionMode) + `
GH_AW_TARGET_REPO_SLUG: "` + repoSlug + `"
GH_AW_ACTIVITY_REPORT_OUTPUT_DIR: ./.cache/gh-aw/activity-report-logs
with:
github-token: ` + token + `
script: |
const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
setupGlobals(core, github, context, exec, io, getOctokit);
const { main } = require('${{ runner.temp }}/gh-aw/actions/run_activity_report.cjs');
await main();
run: |
${GH_AW_CMD_PREFIX} logs \
--repo "${GH_AW_TARGET_REPO_SLUG}" \
--start-date -1w \
--count 100 \
--output ./.cache/gh-aw/activity-report-logs \
--format markdown \
> ./.cache/gh-aw/activity-report-logs/report.md

- name: Save activity report logs cache
if: ${{ always() }}
uses: ` + getActionPin("actions/cache/save") + `
with:
path: ./.cache/gh-aw/activity-report-logs
key: ${{ steps.activity_report_logs_cache.outputs.cache-primary-key }}

- name: Generate activity report issue in target repository
uses: ` + getCachedActionPinFromResolver("actions/github-script", resolver) + `
with:
github-token: ` + token + `
script: |
const fs = require('node:fs');
const reportPath = './.cache/gh-aw/activity-report-logs/report.md';
if (!fs.existsSync(reportPath)) {
core.warning('Activity report markdown not found at ' + reportPath + '; skipping issue creation.');
return;
}
let reportBody = '';
try {
reportBody = fs.readFileSync(reportPath, 'utf8').trim();
} catch (error) {
core.warning('Failed to read activity report markdown at ' + reportPath + ': ' + error.message);
return;
}
if (!reportBody) {
core.warning('Activity report markdown is empty at ' + reportPath + '; skipping issue creation.');
return;
}
const repoSlug = process.env.GH_AW_TARGET_REPO_SLUG || '';
const [owner, repo] = repoSlug.split('/');
if (!owner || !repo) {
core.setFailed('Invalid GH_AW_TARGET_REPO_SLUG: ' + repoSlug);
return;
}
const body = [
'### Agentic workflow activity report',
'',
'Repository: ' + repoSlug,
'Generated at: ' + new Date().toISOString(),
'',
reportBody,
].join('\n');
const createdIssue = await github.rest.issues.create({
owner,
repo,
title: '[aw] agentic status report',
body,
labels: ['agentic-workflows'],
});
core.info('Created issue #' + createdIssue.data.number + ': ' + createdIssue.data.html_url);
`)

// Add validate_workflows job for workflow_dispatch/workflow_call with operation == 'validate'
Expand Down
20 changes: 18 additions & 2 deletions pkg/workflow/side_repo_maintenance_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,24 @@ This workflow operates on a separate repository.
"generated workflow should save activity_report logs cache even if report generation fails")
assert.Contains(t, contentStr, "steps.activity_report_logs_cache.outputs.cache-primary-key",
"generated workflow should save activity_report logs using the cache primary key")
assert.Contains(t, contentStr, "GH_AW_ACTIVITY_REPORT_OUTPUT_DIR: ./.cache/gh-aw/activity-report-logs",
"generated workflow should set GH_AW_ACTIVITY_REPORT_OUTPUT_DIR for activity_report logs")
assert.Contains(t, contentStr, "Download activity report logs in target repository",
"generated workflow should include direct logs download step for activity_report")
assert.Contains(t, contentStr, "timeout-minutes: 20",
"generated workflow should set a 20-minute timeout for the activity_report logs download step")
assert.Contains(t, contentStr, "${GH_AW_CMD_PREFIX} logs",
"generated workflow should run gh aw logs directly")
assert.Contains(t, contentStr, "--start-date -1w",
"generated workflow should download 7 days of logs for activity_report")
assert.Contains(t, contentStr, "--count 100",
"generated workflow should limit activity_report log downloads to at most 100 runs")
assert.Contains(t, contentStr, "--format markdown",
"generated workflow should request markdown report output from gh aw logs")
assert.Contains(t, contentStr, "./.cache/gh-aw/activity-report-logs/report.md",
"generated workflow should write activity_report markdown output to report.md")
assert.Contains(t, contentStr, "Generate activity report issue in target repository",
"generated workflow should include activity_report issue generation step after cache save")
assert.Contains(t, contentStr, "title: '[aw] agentic status report'",
"generated workflow should create the activity_report issue with the expected title")
assert.Contains(t, contentStr, "actions: read\n contents: read\n issues: write",
"activity_report job should include contents: read with explicit permissions")
assert.Contains(t, contentStr, "timeout-minutes: 120",
Expand Down