Skip to content

Commit 4e74571

Browse files
authored
Add push trigger on repository default branch for .github/workflows/*.md to agentic maintenance workflow generator (#28295)
1 parent 94f9b68 commit 4e74571

9 files changed

Lines changed: 258 additions & 54 deletions

.github/workflows/agentics-maintenance.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ name: Agentic Maintenance
3535
on:
3636
schedule:
3737
- cron: "37 */2 * * *" # Every 2 hours (based on minimum expires: 1 days)
38+
push:
39+
branches:
40+
- main
41+
paths:
42+
- '.github/workflows/*.md'
3843
workflow_dispatch:
3944
inputs:
4045
operation:
@@ -84,7 +89,7 @@ permissions: {}
8489

8590
jobs:
8691
close-expired-entities:
87-
if: ${{ (!(github.event.repository.fork)) && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '') }}
92+
if: ${{ (!(github.event.repository.fork)) && github.event_name != 'push' && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '') }}
8893
runs-on: ubuntu-slim
8994
permissions:
9095
discussions: write
@@ -131,7 +136,7 @@ jobs:
131136
await main();
132137
133138
cleanup-cache-memory:
134-
if: ${{ (!(github.event.repository.fork)) && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '' || inputs.operation == 'clean_cache_memories') }}
139+
if: ${{ (!(github.event.repository.fork)) && github.event_name != 'push' && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '' || inputs.operation == 'clean_cache_memories') }}
135140
runs-on: ubuntu-slim
136141
permissions:
137142
actions: write
@@ -552,6 +557,9 @@ jobs:
552557
compile-workflows:
553558
if: ${{ (!(github.event.repository.fork)) && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '') }}
554559
runs-on: ubuntu-slim
560+
concurrency:
561+
group: ${{ github.workflow }}-compile-workflows-${{ github.repository }}
562+
cancel-in-progress: true
555563
permissions:
556564
contents: read
557565
issues: write
@@ -590,7 +598,7 @@ jobs:
590598
await main();
591599
592600
secret-validation:
593-
if: ${{ (!(github.event.repository.fork)) && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '') }}
601+
if: ${{ (!(github.event.repository.fork)) && github.event_name != 'push' && (github.event_name != 'workflow_dispatch' && github.event_name != 'workflow_call' || inputs.operation == '') }}
594602
runs-on: ubuntu-slim
595603
permissions:
596604
contents: read

pkg/cli/compile_post_processing.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func generateMaintenanceWorkflowWrapper(
7575
repoConfig = nil
7676
}
7777

78-
if err := workflow.GenerateMaintenanceWorkflow(workflowDataList, workflowsDir, compiler.GetVersion(), compiler.GetActionMode(), compiler.GetActionTag(), verbose, repoConfig); err != nil {
78+
if err := workflow.GenerateMaintenanceWorkflow(workflowDataList, workflowsDir, compiler.GetVersion(), compiler.GetActionMode(), compiler.GetActionTag(), verbose, repoConfig, compiler.GetRepositorySlug()); err != nil {
7979
if strict {
8080
return fmt.Errorf("failed to generate maintenance workflow: %w", err)
8181
}

pkg/cli/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func ensureMaintenanceWorkflow(verbose bool) error {
238238
repoConfig = nil
239239
}
240240

241-
if err := workflow.GenerateMaintenanceWorkflow(workflowDataList, workflowsDir, GetVersion(), compiler.GetActionMode(), compiler.GetActionTag(), verbose, repoConfig); err != nil {
241+
if err := workflow.GenerateMaintenanceWorkflow(workflowDataList, workflowsDir, GetVersion(), compiler.GetActionMode(), compiler.GetActionTag(), verbose, repoConfig, compiler.GetRepositorySlug()); err != nil {
242242
return fmt.Errorf("failed to generate maintenance workflow: %w", err)
243243
}
244244

pkg/workflow/compiler_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ func (c *Compiler) SetRepositorySlug(slug string) {
278278
c.repositorySlug = slug
279279
}
280280

281+
// GetRepositorySlug returns the repository slug (owner/repo) set on this compiler instance.
282+
func (c *Compiler) GetRepositorySlug() string {
283+
return c.repositorySlug
284+
}
285+
281286
// GetScheduleWarnings returns all accumulated schedule warnings for this compiler instance
282287
func (c *Compiler) GetScheduleWarnings() []string {
283288
return c.scheduleWarnings

pkg/workflow/maintenance_conditions.go

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,31 +33,53 @@ func buildNotDispatchOrCallOrEmptyOperation() ConditionNode {
3333
)
3434
}
3535

36-
// buildNotForkAndScheduledOrOperation creates a condition for jobs that run on
37-
// schedule (or empty operation) AND when a specific operation is selected.
38-
// Condition: !fork && (not_dispatch_or_call || operation == ” || operation == op)
39-
func buildNotForkAndScheduledOrOperation(operation string) ConditionNode {
40-
maintenanceConditionsLog.Printf("Building not-fork-and-scheduled-or-operation condition: %s", operation)
36+
// buildNotForkAndScheduled creates a condition for jobs that should run on any
37+
// non-dispatch/call event including push, or on workflow_dispatch/workflow_call
38+
// with an empty operation, and never on forks. Unlike buildNotForkAndScheduleOnly,
39+
// this function does NOT exclude push events.
40+
// Condition: !fork && ((event_name != 'workflow_dispatch' && event_name != 'workflow_call') || operation == ”)
41+
func buildNotForkAndScheduled() ConditionNode {
4142
return BuildAnd(
4243
buildNotForkCondition(),
43-
BuildOr(
44-
buildNotDispatchOrCallOrEmptyOperation(),
45-
BuildEquals(
46-
BuildPropertyAccess("inputs.operation"),
47-
BuildStringLiteral(operation),
44+
buildNotDispatchOrCallOrEmptyOperation(),
45+
)
46+
}
47+
48+
// buildNotForkAndScheduleOnly creates a condition for jobs that should run on schedule
49+
// (or empty dispatch/call) but NOT on push events, and never on forks.
50+
func buildNotForkAndScheduleOnly() ConditionNode {
51+
return BuildAnd(
52+
buildNotForkCondition(),
53+
BuildAnd(
54+
BuildNotEquals(
55+
BuildPropertyAccess("github.event_name"),
56+
BuildStringLiteral("push"),
4857
),
58+
buildNotDispatchOrCallOrEmptyOperation(),
4959
),
5060
)
5161
}
5262

53-
// buildNotForkAndScheduled creates a condition for jobs that should run on any
54-
// non-dispatch/call event (e.g. schedule, push) or on workflow_dispatch/workflow_call
55-
// with an empty operation, and never on forks.
56-
// Condition: !fork && ((event_name != 'workflow_dispatch' && event_name != 'workflow_call') || operation == ”)
57-
func buildNotForkAndScheduled() ConditionNode {
63+
// buildNotForkAndScheduleOnlyOrOperation creates a condition for jobs that run on
64+
// schedule (or empty dispatch/call) or when a specific operation is selected,
65+
// but NOT on push events, and never on forks.
66+
func buildNotForkAndScheduleOnlyOrOperation(operation string) ConditionNode {
67+
maintenanceConditionsLog.Printf("Building not-fork-and-schedule-only-or-operation condition: %s", operation)
5868
return BuildAnd(
5969
buildNotForkCondition(),
60-
buildNotDispatchOrCallOrEmptyOperation(),
70+
BuildAnd(
71+
BuildNotEquals(
72+
BuildPropertyAccess("github.event_name"),
73+
BuildStringLiteral("push"),
74+
),
75+
BuildOr(
76+
buildNotDispatchOrCallOrEmptyOperation(),
77+
BuildEquals(
78+
BuildPropertyAccess("inputs.operation"),
79+
BuildStringLiteral(operation),
80+
),
81+
),
82+
),
6183
)
6284
}
6385

pkg/workflow/maintenance_workflow.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"strings"
78

89
"github.com/github/gh-aw/pkg/console"
910
"github.com/github/gh-aw/pkg/logger"
@@ -83,11 +84,37 @@ func getCLICmdPrefix(actionMode ActionMode) string {
8384
return "gh aw"
8485
}
8586

87+
// FetchDefaultBranch queries the GitHub API to determine the default branch of the
88+
// given repository slug (owner/repo). Returns "main" as a fallback when the slug is
89+
// empty, not in owner/repo format, or when the API call fails.
90+
func FetchDefaultBranch(slug string) string {
91+
const fallback = "main"
92+
if slug == "" || strings.Count(slug, "/") != 1 {
93+
maintenanceLog.Printf("No valid repository slug, using default branch fallback: %s", fallback)
94+
return fallback
95+
}
96+
maintenanceLog.Printf("Fetching default branch for repository: %s", slug)
97+
output, err := RunGH("Fetching default branch...", "api", "/repos/"+slug, "--jq", ".default_branch")
98+
if err != nil {
99+
maintenanceLog.Printf("Failed to fetch default branch for %s: %v, falling back to %s", slug, err, fallback)
100+
return fallback
101+
}
102+
branch := strings.TrimSpace(string(output))
103+
if branch == "" {
104+
maintenanceLog.Printf("Empty default branch response for %s, falling back to %s", slug, fallback)
105+
return fallback
106+
}
107+
maintenanceLog.Printf("Default branch for %s: %s", slug, branch)
108+
return branch
109+
}
110+
86111
// GenerateMaintenanceWorkflow generates the agentics-maintenance.yml workflow
87112
// if any workflows use the expires field for discussions or issues.
88113
// When repoConfig is non-nil and repoConfig.MaintenanceDisabled is true the
89114
// maintenance workflow is deleted and the function returns immediately.
90-
func GenerateMaintenanceWorkflow(workflowDataList []*WorkflowData, workflowDir string, version string, actionMode ActionMode, actionTag string, verbose bool, repoConfig *RepoConfig) error {
115+
// repoSlug is the owner/repo slug used to determine the default branch for the push
116+
// trigger; pass an empty string to fall back to "main".
117+
func GenerateMaintenanceWorkflow(workflowDataList []*WorkflowData, workflowDir string, version string, actionMode ActionMode, actionTag string, verbose bool, repoConfig *RepoConfig, repoSlug string) error {
91118
maintenanceLog.Print("Checking if maintenance workflow is needed")
92119

93120
// Respect explicit opt-out from aw.json: maintenance: false
@@ -144,8 +171,12 @@ func GenerateMaintenanceWorkflow(workflowDataList []*WorkflowData, workflowDir s
144171
cronSchedule, scheduleDesc := generateMaintenanceCron(minExpiresDays)
145172
maintenanceLog.Printf("Maintenance schedule: %s (%s)", cronSchedule, scheduleDesc)
146173

174+
// Fetch the default branch for the push trigger (dev mode only)
175+
// Resolved here to avoid passing it through multiple layers; empty slug falls back to "main"
176+
defaultBranch := FetchDefaultBranch(repoSlug)
177+
147178
// Generate the YAML content for the maintenance workflow
148-
content := buildMaintenanceWorkflowYAML(cronSchedule, scheduleDesc, minExpiresDays, runsOnValue, actionMode, version, actionTag, resolver, configuredRunsOn)
179+
content := buildMaintenanceWorkflowYAML(cronSchedule, scheduleDesc, minExpiresDays, runsOnValue, actionMode, version, actionTag, resolver, configuredRunsOn, defaultBranch)
149180

150181
// Write the maintenance workflow file
151182
maintenanceFile := filepath.Join(workflowDir, "agentics-maintenance.yml")

0 commit comments

Comments
 (0)