From 9aab44b1c765c944dbd381cfc30454658f83bf34 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 18:36:39 +0000 Subject: [PATCH 1/4] chore: outline GHE fix plan for githubnext/agentics Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/d55f95d1-71d5-4ae6-9c39-50c8195e5e69 --- .github/workflows/smoke-codex.lock.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index e93417720da..fc04bf871e3 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -450,13 +450,20 @@ jobs: { "description": "Add the 'smoked' label to the current pull request (can only be called once)", "inputSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { - "payload": { - "description": "JSON-encoded payload to pass to the action", + "labels": { + "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.", + "type": "string" + }, + "number": { + "description": "The number of the issue or pull request.", "type": "string" } }, + "required": [ + "labels" + ], "type": "object" }, "name": "add_smoked_label" @@ -1561,7 +1568,8 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} with: - payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }} + labels: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).labels }} + number: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).number }} - name: Upload safe output items if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 From e441faef883ebf079b566d8ccec573e85ecb03b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 18:48:51 +0000 Subject: [PATCH 2/4] fix: ensure githubnext/agentics always resolves to github.com in GHE mode When GH_HOST is set to a non-github.com GHE host, short-form workflow specs like `githubnext/agentics/workflow-name` would be resolved against the GHE host instead of github.com. - getGitHubHostForRepo: add githubnext/agentics to the list of repos that always use https://github.com (covers git-based downloads) - parseWorkflowSpec: set Host: "github.com" when the slug is githubnext/agentics or github/gh-aw (covers API-based downloads) - Update CLI help-text examples to use full https://github.com/... URLs - Update schema examples to use full URLs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/d55f95d1-71d5-4ae6-9c39-50c8195e5e69 --- pkg/cli/add_command.go | 11 ++++----- pkg/cli/add_wizard_command.go | 8 +++--- pkg/cli/github.go | 13 +++++++--- pkg/cli/spec.go | 16 ++++++++++-- pkg/cli/trial_command.go | 26 ++++++++++---------- pkg/parser/schemas/main_workflow_schema.json | 4 +-- 6 files changed, 48 insertions(+), 30 deletions(-) diff --git a/pkg/cli/add_command.go b/pkg/cli/add_command.go index 6b2020526e3..adaff1ad2b3 100644 --- a/pkg/cli/add_command.go +++ b/pkg/cli/add_command.go @@ -55,14 +55,13 @@ This command adds workflows directly without interactive prompts. Use 'add-wizar for a guided setup that configures secrets, creates a pull request, and more. Examples: - ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/daily-repo-status # Add workflow directly - ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor@v1.0.0 # Add with version - ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/workflows/ci-doctor.md@main + ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/main/workflows/daily-repo-status.md # Add workflow directly + ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/v1.0.0/workflows/ci-doctor.md # Add with version ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md - ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor --create-pull-request --force + ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md --create-pull-request --force ` + string(constants.CLIExtensionPrefix) + ` add ./my-workflow.md # Add local workflow ` + string(constants.CLIExtensionPrefix) + ` add ./*.md # Add all local workflows - ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor --dir shared # Add to .github/workflows/shared/ + ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md --dir shared # Add to .github/workflows/shared/ Workflow specifications: - Three parts: "owner/repo/workflow-name[@version]" (implicitly looks in workflows/ directory) @@ -81,7 +80,7 @@ Note: To create a new workflow from scratch, use the 'new' command instead. Note: For guided interactive setup, use the 'add-wizard' command instead.`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return fmt.Errorf("missing workflow specification\n\nUsage:\n %s ...\n\nExamples:\n %[1]s githubnext/agentics/daily-repo-status Add from repository\n %[1]s ./my-workflow.md Add local workflow\n\nRun '%[1]s --help' for more information", cmd.CommandPath()) + return fmt.Errorf("missing workflow specification\n\nUsage:\n %s ...\n\nExamples:\n %[1]s https://github.com/githubnext/agentics/blob/main/workflows/daily-repo-status.md Add from repository\n %[1]s ./my-workflow.md Add local workflow\n\nRun '%[1]s --help' for more information", cmd.CommandPath()) } return nil }, diff --git a/pkg/cli/add_wizard_command.go b/pkg/cli/add_wizard_command.go index e611e366bcb..f26da9cc04e 100644 --- a/pkg/cli/add_wizard_command.go +++ b/pkg/cli/add_wizard_command.go @@ -28,11 +28,11 @@ This command walks you through: Use 'add' for non-interactive workflow addition. Examples: - ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/daily-repo-status # Guided setup - ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor@v1.0.0 # Guided setup with version + ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://github.com/githubnext/agentics/blob/main/workflows/daily-repo-status.md # Guided setup + ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://github.com/githubnext/agentics/blob/v1.0.0/workflows/ci-doctor.md # Guided setup with version ` + string(constants.CLIExtensionPrefix) + ` add-wizard ./my-workflow.md # Guided setup for local workflow - ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --engine copilot # Pre-select engine - ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --skip-secret # Skip secret prompt + ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md --engine copilot # Pre-select engine + ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md --skip-secret # Skip secret prompt Workflow specifications: - Three parts: "owner/repo/workflow-name[@version]" (implicitly looks in workflows/ directory) diff --git a/pkg/cli/github.go b/pkg/cli/github.go index 0ea0f8a0ebf..a9780b48a2f 100644 --- a/pkg/cli/github.go +++ b/pkg/cli/github.go @@ -36,9 +36,10 @@ func getGitHubHost() string { } // getGitHubHostForRepo returns the GitHub host URL for a specific repository. -// The gh-aw repository (github/gh-aw) always uses public GitHub (https://github.com) -// regardless of enterprise GitHub host settings, since gh-aw itself is only available -// on public GitHub. For all other repositories, it uses getGitHubHost(). +// The gh-aw repository (github/gh-aw) and the agentics workflow library +// (githubnext/agentics) always use public GitHub (https://github.com) +// regardless of enterprise GitHub host settings, since these repositories are +// only available on public GitHub. For all other repositories, it uses getGitHubHost(). func getGitHubHostForRepo(repo string) string { // The gh-aw repository is always on public GitHub if repo == "github/gh-aw" || strings.HasPrefix(repo, "github/gh-aw/") { @@ -46,6 +47,12 @@ func getGitHubHostForRepo(repo string) string { return string(constants.PublicGitHubHost) } + // The agentics workflow library is always on public GitHub + if repo == "githubnext/agentics" || strings.HasPrefix(repo, "githubnext/agentics/") { + githubLog.Print("Using public GitHub host for githubnext/agentics repository") + return string(constants.PublicGitHubHost) + } + // For all other repositories, use the configured GitHub host return getGitHubHost() } diff --git a/pkg/cli/spec.go b/pkg/cli/spec.go index 02172136ebd..7ad5d798ba4 100644 --- a/pkg/cli/spec.go +++ b/pkg/cli/spec.go @@ -293,16 +293,27 @@ func parseWorkflowSpec(spec string) (*WorkflowSpec, error) { return nil, fmt.Errorf("invalid workflow specification: '%s/%s' does not look like a valid GitHub repository", owner, repo) } + repoSlug := fmt.Sprintf("%s/%s", owner, repo) + + // Certain well-known repositories are always on public GitHub, regardless of GH_HOST. + // Setting an explicit host ensures that API fetches target github.com even when + // the user has a GHE instance configured via GH_HOST. + var explicitHost string + if repoSlug == "githubnext/agentics" || repoSlug == "github/gh-aw" { + explicitHost = "github.com" + } + // Check if this is a wildcard specification (owner/repo/*) if workflowPath == "*" { return &WorkflowSpec{ RepoSpec: RepoSpec{ - RepoSlug: fmt.Sprintf("%s/%s", owner, repo), + RepoSlug: repoSlug, Version: version, }, WorkflowPath: "*", WorkflowName: "*", IsWildcard: true, + Host: explicitHost, }, nil } @@ -321,11 +332,12 @@ func parseWorkflowSpec(spec string) (*WorkflowSpec, error) { return &WorkflowSpec{ RepoSpec: RepoSpec{ - RepoSlug: fmt.Sprintf("%s/%s", owner, repo), + RepoSlug: repoSlug, Version: version, }, WorkflowPath: workflowPath, WorkflowName: strings.TrimSuffix(filepath.Base(workflowPath), ".md"), + Host: explicitHost, }, nil } diff --git a/pkg/cli/trial_command.go b/pkg/cli/trial_command.go index 97141023d7f..5809e5b858e 100644 --- a/pkg/cli/trial_command.go +++ b/pkg/cli/trial_command.go @@ -79,32 +79,32 @@ workflow(s) from their source repositories, and runs them in "trial mode" to cap making actual changes to the "simulated" host repository Single workflow: - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/weekly-research + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/weekly-research.md Outputs: stdout + local trials/weekly-research.DATETIME-ID.json + trial repo trials/ Multiple workflows (for comparison): - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/daily-plan githubnext/agentics/weekly-research + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/daily-plan.md https://github.com/githubnext/agentics/blob/main/workflows/weekly-research.md Outputs: stdout + local trials/ + trial repo trials/ (individual + combined results) Workflows from different repositories: - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/daily-plan myorg/myrepo/custom-workflow + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/daily-plan.md myorg/myrepo/custom-workflow Repository mode examples: - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --host-repo myorg/myrepo # Run directly in myorg/myrepo (no simulation) - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --logical-repo myorg/myrepo # Simulate running against myorg/myrepo - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --clone-repo myorg/myrepo # Clone myorg/myrepo contents into host + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --host-repo myorg/myrepo # Run directly in myorg/myrepo (no simulation) + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --logical-repo myorg/myrepo # Simulate running against myorg/myrepo + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --clone-repo myorg/myrepo # Clone myorg/myrepo contents into host Repeat and cleanup examples: - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --repeat 3 # Run 4 times total (1 initial + 3 repeats) - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --delete-host-repo-after # Delete repo after completion - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --host-repo my-trial # Custom host repo - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --dry-run # Show what would be done without changes + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --repeat 3 # Run 4 times total (1 initial + 3 repeats) + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --delete-host-repo-after # Delete repo after completion + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --host-repo my-trial # Custom host repo + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --dry-run # Show what would be done without changes Auto-merge examples: - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --auto-merge-prs # Auto-merge any PRs created during trial + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --auto-merge-prs # Auto-merge any PRs created during trial Advanced examples: - ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --host-repo . # Use current repo as host + ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --host-repo . # Use current repo as host ` + string(constants.CLIExtensionPrefix) + ` trial ./local-workflow.md --clone-repo upstream/repo --repeat 2 Repository modes: @@ -118,7 +118,7 @@ The host repository will be created as private and kept by default unless --dele Trial results are saved both locally (in trials/ directory) and in the host repository for future reference.`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return fmt.Errorf("missing workflow specification\n\nUsage:\n %s ...\n\nExamples:\n %[1]s githubnext/agentics/daily-plan Trial a workflow from a repository\n %[1]s ./local-workflow.md Trial a local workflow\n\nRun '%[1]s --help' for more information", cmd.CommandPath()) + return fmt.Errorf("missing workflow specification\n\nUsage:\n %s ...\n\nExamples:\n %[1]s https://github.com/githubnext/agentics/blob/main/workflows/daily-plan.md Trial a workflow from a repository\n %[1]s ./local-workflow.md Trial a local workflow\n\nRun '%[1]s --help' for more information", cmd.CommandPath()) } return nil }, diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 311f2816dd6..ec6495d1cf1 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -23,7 +23,7 @@ "source": { "type": "string", "description": "Optional source reference indicating where this workflow was added from. Format: owner/repo/path@ref (e.g., githubnext/agentics/workflows/ci-doctor.md@v1.0.0). Rendered as a comment in the generated lock file.", - "examples": ["githubnext/agentics/workflows/ci-doctor.md", "githubnext/agentics/workflows/daily-perf-improver.md@1f181b37d3fe5862ab590648f25a292e345b5de6"] + "examples": ["https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md", "https://github.com/githubnext/agentics/blob/1f181b37d3fe5862ab590648f25a292e345b5de6/workflows/daily-perf-improver.md"] }, "tracker-id": { "type": "string", @@ -67,7 +67,7 @@ }, "imports": { "type": "array", - "description": "Optional array of workflow specifications to import (similar to @include directives but defined in frontmatter). Format: owner/repo/path@ref (e.g., githubnext/agentics/workflows/shared/common.md@v1.0.0). Can be strings or objects with path and inputs. Any markdown files under .github/agents directory are treated as custom agent files and only one agent file is allowed per workflow.", + "description": "Optional array of workflow specifications to import (similar to @include directives but defined in frontmatter). Format: owner/repo/path@ref (e.g., githubnext/agentics/workflows/shared/common.md@v1.0.0) or full GitHub URL. Can be strings or objects with path and inputs. Any markdown files under .github/agents directory are treated as custom agent files and only one agent file is allowed per workflow.", "items": { "oneOf": [ { From 60a78746e7fdf79d7f32e484fa75efb2b5e87368 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 18:56:43 +0000 Subject: [PATCH 3/4] fix: only pin githubnext/agentics to github.com when GHE is configured - Revert help text examples in add/add-wizard/trial commands to the shorter githubnext/agentics/... syntax (keep intuitive form for non-GHE users) - Revert schema examples to shorter form - In parseWorkflowSpec, only set Host: "github.com" when a non-github.com GHE host is detected via getGitHubHost() Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/e1234e84-e2a8-467e-bb4e-a69ac58e56ed --- pkg/cli/add_command.go | 11 +++++---- pkg/cli/add_wizard_command.go | 8 +++--- pkg/cli/spec.go | 13 ++++++---- pkg/cli/trial_command.go | 26 ++++++++++---------- pkg/parser/schemas/main_workflow_schema.json | 4 +-- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/pkg/cli/add_command.go b/pkg/cli/add_command.go index adaff1ad2b3..6b2020526e3 100644 --- a/pkg/cli/add_command.go +++ b/pkg/cli/add_command.go @@ -55,13 +55,14 @@ This command adds workflows directly without interactive prompts. Use 'add-wizar for a guided setup that configures secrets, creates a pull request, and more. Examples: - ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/main/workflows/daily-repo-status.md # Add workflow directly - ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/v1.0.0/workflows/ci-doctor.md # Add with version + ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/daily-repo-status # Add workflow directly + ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor@v1.0.0 # Add with version + ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/workflows/ci-doctor.md@main ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md - ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md --create-pull-request --force + ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor --create-pull-request --force ` + string(constants.CLIExtensionPrefix) + ` add ./my-workflow.md # Add local workflow ` + string(constants.CLIExtensionPrefix) + ` add ./*.md # Add all local workflows - ` + string(constants.CLIExtensionPrefix) + ` add https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md --dir shared # Add to .github/workflows/shared/ + ` + string(constants.CLIExtensionPrefix) + ` add githubnext/agentics/ci-doctor --dir shared # Add to .github/workflows/shared/ Workflow specifications: - Three parts: "owner/repo/workflow-name[@version]" (implicitly looks in workflows/ directory) @@ -80,7 +81,7 @@ Note: To create a new workflow from scratch, use the 'new' command instead. Note: For guided interactive setup, use the 'add-wizard' command instead.`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return fmt.Errorf("missing workflow specification\n\nUsage:\n %s ...\n\nExamples:\n %[1]s https://github.com/githubnext/agentics/blob/main/workflows/daily-repo-status.md Add from repository\n %[1]s ./my-workflow.md Add local workflow\n\nRun '%[1]s --help' for more information", cmd.CommandPath()) + return fmt.Errorf("missing workflow specification\n\nUsage:\n %s ...\n\nExamples:\n %[1]s githubnext/agentics/daily-repo-status Add from repository\n %[1]s ./my-workflow.md Add local workflow\n\nRun '%[1]s --help' for more information", cmd.CommandPath()) } return nil }, diff --git a/pkg/cli/add_wizard_command.go b/pkg/cli/add_wizard_command.go index f26da9cc04e..e611e366bcb 100644 --- a/pkg/cli/add_wizard_command.go +++ b/pkg/cli/add_wizard_command.go @@ -28,11 +28,11 @@ This command walks you through: Use 'add' for non-interactive workflow addition. Examples: - ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://github.com/githubnext/agentics/blob/main/workflows/daily-repo-status.md # Guided setup - ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://github.com/githubnext/agentics/blob/v1.0.0/workflows/ci-doctor.md # Guided setup with version + ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/daily-repo-status # Guided setup + ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor@v1.0.0 # Guided setup with version ` + string(constants.CLIExtensionPrefix) + ` add-wizard ./my-workflow.md # Guided setup for local workflow - ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md --engine copilot # Pre-select engine - ` + string(constants.CLIExtensionPrefix) + ` add-wizard https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md --skip-secret # Skip secret prompt + ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --engine copilot # Pre-select engine + ` + string(constants.CLIExtensionPrefix) + ` add-wizard githubnext/agentics/ci-doctor --skip-secret # Skip secret prompt Workflow specifications: - Three parts: "owner/repo/workflow-name[@version]" (implicitly looks in workflows/ directory) diff --git a/pkg/cli/spec.go b/pkg/cli/spec.go index 7ad5d798ba4..c8ec78a41f3 100644 --- a/pkg/cli/spec.go +++ b/pkg/cli/spec.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/parser" ) @@ -295,12 +296,14 @@ func parseWorkflowSpec(spec string) (*WorkflowSpec, error) { repoSlug := fmt.Sprintf("%s/%s", owner, repo) - // Certain well-known repositories are always on public GitHub, regardless of GH_HOST. - // Setting an explicit host ensures that API fetches target github.com even when - // the user has a GHE instance configured via GH_HOST. + // When a GHE instance is configured, certain well-known repositories that exist only + // on public GitHub must have their host pinned to github.com so that API fetches target + // the correct server instead of the GHE instance. var explicitHost string - if repoSlug == "githubnext/agentics" || repoSlug == "github/gh-aw" { - explicitHost = "github.com" + if getGitHubHost() != string(constants.PublicGitHubHost) { + if repoSlug == "githubnext/agentics" || repoSlug == "github/gh-aw" { + explicitHost = "github.com" + } } // Check if this is a wildcard specification (owner/repo/*) diff --git a/pkg/cli/trial_command.go b/pkg/cli/trial_command.go index 5809e5b858e..97141023d7f 100644 --- a/pkg/cli/trial_command.go +++ b/pkg/cli/trial_command.go @@ -79,32 +79,32 @@ workflow(s) from their source repositories, and runs them in "trial mode" to cap making actual changes to the "simulated" host repository Single workflow: - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/weekly-research.md + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/weekly-research Outputs: stdout + local trials/weekly-research.DATETIME-ID.json + trial repo trials/ Multiple workflows (for comparison): - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/daily-plan.md https://github.com/githubnext/agentics/blob/main/workflows/weekly-research.md + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/daily-plan githubnext/agentics/weekly-research Outputs: stdout + local trials/ + trial repo trials/ (individual + combined results) Workflows from different repositories: - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/daily-plan.md myorg/myrepo/custom-workflow + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/daily-plan myorg/myrepo/custom-workflow Repository mode examples: - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --host-repo myorg/myrepo # Run directly in myorg/myrepo (no simulation) - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --logical-repo myorg/myrepo # Simulate running against myorg/myrepo - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --clone-repo myorg/myrepo # Clone myorg/myrepo contents into host + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --host-repo myorg/myrepo # Run directly in myorg/myrepo (no simulation) + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --logical-repo myorg/myrepo # Simulate running against myorg/myrepo + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --clone-repo myorg/myrepo # Clone myorg/myrepo contents into host Repeat and cleanup examples: - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --repeat 3 # Run 4 times total (1 initial + 3 repeats) - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --delete-host-repo-after # Delete repo after completion - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --host-repo my-trial # Custom host repo - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --dry-run # Show what would be done without changes + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --repeat 3 # Run 4 times total (1 initial + 3 repeats) + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --delete-host-repo-after # Delete repo after completion + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --host-repo my-trial # Custom host repo + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --dry-run # Show what would be done without changes Auto-merge examples: - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --auto-merge-prs # Auto-merge any PRs created during trial + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --auto-merge-prs # Auto-merge any PRs created during trial Advanced examples: - ` + string(constants.CLIExtensionPrefix) + ` trial https://github.com/githubnext/agentics/blob/main/workflows/my-workflow.md --host-repo . # Use current repo as host + ` + string(constants.CLIExtensionPrefix) + ` trial githubnext/agentics/my-workflow --host-repo . # Use current repo as host ` + string(constants.CLIExtensionPrefix) + ` trial ./local-workflow.md --clone-repo upstream/repo --repeat 2 Repository modes: @@ -118,7 +118,7 @@ The host repository will be created as private and kept by default unless --dele Trial results are saved both locally (in trials/ directory) and in the host repository for future reference.`, Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { - return fmt.Errorf("missing workflow specification\n\nUsage:\n %s ...\n\nExamples:\n %[1]s https://github.com/githubnext/agentics/blob/main/workflows/daily-plan.md Trial a workflow from a repository\n %[1]s ./local-workflow.md Trial a local workflow\n\nRun '%[1]s --help' for more information", cmd.CommandPath()) + return fmt.Errorf("missing workflow specification\n\nUsage:\n %s ...\n\nExamples:\n %[1]s githubnext/agentics/daily-plan Trial a workflow from a repository\n %[1]s ./local-workflow.md Trial a local workflow\n\nRun '%[1]s --help' for more information", cmd.CommandPath()) } return nil }, diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index ec6495d1cf1..311f2816dd6 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -23,7 +23,7 @@ "source": { "type": "string", "description": "Optional source reference indicating where this workflow was added from. Format: owner/repo/path@ref (e.g., githubnext/agentics/workflows/ci-doctor.md@v1.0.0). Rendered as a comment in the generated lock file.", - "examples": ["https://github.com/githubnext/agentics/blob/main/workflows/ci-doctor.md", "https://github.com/githubnext/agentics/blob/1f181b37d3fe5862ab590648f25a292e345b5de6/workflows/daily-perf-improver.md"] + "examples": ["githubnext/agentics/workflows/ci-doctor.md", "githubnext/agentics/workflows/daily-perf-improver.md@1f181b37d3fe5862ab590648f25a292e345b5de6"] }, "tracker-id": { "type": "string", @@ -67,7 +67,7 @@ }, "imports": { "type": "array", - "description": "Optional array of workflow specifications to import (similar to @include directives but defined in frontmatter). Format: owner/repo/path@ref (e.g., githubnext/agentics/workflows/shared/common.md@v1.0.0) or full GitHub URL. Can be strings or objects with path and inputs. Any markdown files under .github/agents directory are treated as custom agent files and only one agent file is allowed per workflow.", + "description": "Optional array of workflow specifications to import (similar to @include directives but defined in frontmatter). Format: owner/repo/path@ref (e.g., githubnext/agentics/workflows/shared/common.md@v1.0.0). Can be strings or objects with path and inputs. Any markdown files under .github/agents directory are treated as custom agent files and only one agent file is allowed per workflow.", "items": { "oneOf": [ { From 321e9e2247b4915ba04fc9952754e72b2f3fafa8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 20:07:33 +0000 Subject: [PATCH 4/4] fix: address review comments - centralize host pinning, add tests, fix lock yml - spec.go: replace duplicated allowlist with getGitHubHostForRepo() call; derive hostname by parsing URL instead of hard-coding "github.com" - github_test.go: add TestGetGitHubHostForRepo covering allowlisted and non-allowlisted repos with and without GHE configured - spec_test.go: add TestParseWorkflowSpecGHEHostPinning and TestParseWorkflowSpecNoGHEHostPinning to prevent regressions - smoke-codex.lock.yml: make "number" required in add_smoked_label schema; fix grammar "there're" -> "there are" Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/3781b8df-791b-49e8-8d11-97c82d95032a --- .github/workflows/smoke-codex.lock.yml | 5 +- pkg/cli/github_test.go | 66 +++++++++++++++++++ pkg/cli/spec.go | 15 ++--- pkg/cli/spec_test.go | 87 ++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 9 deletions(-) diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index fc04bf871e3..26e9d8cdfff 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -453,7 +453,7 @@ jobs: "additionalProperties": false, "properties": { "labels": { - "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.", + "description": "The label names to add, separated by line breaks if there are multiple labels.", "type": "string" }, "number": { @@ -462,7 +462,8 @@ jobs: } }, "required": [ - "labels" + "labels", + "number" ], "type": "object" }, diff --git a/pkg/cli/github_test.go b/pkg/cli/github_test.go index dce8ab1b02c..e248a8f3bd1 100644 --- a/pkg/cli/github_test.go +++ b/pkg/cli/github_test.go @@ -145,3 +145,69 @@ func TestGetGitHubHost(t *testing.T) { }) } } + +func TestGetGitHubHostForRepo(t *testing.T) { + tests := []struct { + name string + repo string + gheHost string + expectedHost string + }{ + { + name: "non-allowlisted repo uses configured public host", + repo: "owner/repo", + gheHost: "", + expectedHost: "https://github.com", + }, + { + name: "non-allowlisted repo uses GHE host", + repo: "owner/repo", + gheHost: "myorg.ghe.com", + expectedHost: "https://myorg.ghe.com", + }, + { + name: "github/gh-aw always uses public GitHub", + repo: "github/gh-aw", + gheHost: "myorg.ghe.com", + expectedHost: "https://github.com", + }, + { + name: "github/gh-aw with path always uses public GitHub", + repo: "github/gh-aw/workflows/ci-doctor.md", + gheHost: "myorg.ghe.com", + expectedHost: "https://github.com", + }, + { + name: "githubnext/agentics always uses public GitHub", + repo: "githubnext/agentics", + gheHost: "myorg.ghe.com", + expectedHost: "https://github.com", + }, + { + name: "githubnext/agentics with path always uses public GitHub", + repo: "githubnext/agentics/workflows/daily-plan.md", + gheHost: "myorg.ghe.com", + expectedHost: "https://github.com", + }, + { + name: "githubnext/agentics without GHE host uses public GitHub", + repo: "githubnext/agentics", + gheHost: "", + expectedHost: "https://github.com", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Setenv("GITHUB_SERVER_URL", "") + t.Setenv("GITHUB_ENTERPRISE_HOST", tt.gheHost) + t.Setenv("GITHUB_HOST", "") + t.Setenv("GH_HOST", "") + + host := getGitHubHostForRepo(tt.repo) + if host != tt.expectedHost { + t.Errorf("getGitHubHostForRepo(%q) = %q, want %q", tt.repo, host, tt.expectedHost) + } + }) + } +} diff --git a/pkg/cli/spec.go b/pkg/cli/spec.go index c8ec78a41f3..5ed1b3b55b0 100644 --- a/pkg/cli/spec.go +++ b/pkg/cli/spec.go @@ -7,7 +7,6 @@ import ( "path/filepath" "strings" - "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/parser" ) @@ -296,13 +295,15 @@ func parseWorkflowSpec(spec string) (*WorkflowSpec, error) { repoSlug := fmt.Sprintf("%s/%s", owner, repo) - // When a GHE instance is configured, certain well-known repositories that exist only - // on public GitHub must have their host pinned to github.com so that API fetches target - // the correct server instead of the GHE instance. + // Determine the API host for this repo. getGitHubHostForRepo returns the canonical + // host, which for well-known public-only repos (githubnext/agentics, github/gh-aw) + // is always public GitHub regardless of GHE configuration. If the repo's canonical + // host differs from the configured host, record the explicit hostname so API fetches + // target the correct server. var explicitHost string - if getGitHubHost() != string(constants.PublicGitHubHost) { - if repoSlug == "githubnext/agentics" || repoSlug == "github/gh-aw" { - explicitHost = "github.com" + if repoHost := getGitHubHostForRepo(repoSlug); repoHost != getGitHubHost() { + if u, parseErr := url.Parse(repoHost); parseErr == nil && u.Host != "" { + explicitHost = u.Host } } diff --git a/pkg/cli/spec_test.go b/pkg/cli/spec_test.go index fb920ab0aa1..5f72475f61e 100644 --- a/pkg/cli/spec_test.go +++ b/pkg/cli/spec_test.go @@ -347,6 +347,93 @@ func TestParseWorkflowSpec(t *testing.T) { } } +// TestParseWorkflowSpecGHEHostPinning verifies that well-known public-only repos +// (githubnext/agentics, github/gh-aw) always get Host pinned to "github.com" +// when a GHE environment is detected, while other repos use an empty host. +func TestParseWorkflowSpecGHEHostPinning(t *testing.T) { + tests := []struct { + name string + spec string + wantHost string + wantNoHost bool // expect empty host + }{ + { + name: "githubnext/agentics three-part spec gets github.com in GHE mode", + spec: "githubnext/agentics/daily-plan", + wantHost: "github.com", + }, + { + name: "githubnext/agentics wildcard gets github.com in GHE mode", + spec: "githubnext/agentics/*", + wantHost: "github.com", + }, + { + name: "github/gh-aw three-part spec gets github.com in GHE mode", + spec: "github/gh-aw/my-workflow", + wantHost: "github.com", + }, + { + name: "non-allowlisted repo has empty host in GHE mode", + spec: "owner/repo/workflow", + wantNoHost: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Simulate a GHE environment + t.Setenv("GITHUB_SERVER_URL", "") + t.Setenv("GITHUB_ENTERPRISE_HOST", "myorg.ghe.com") + t.Setenv("GITHUB_HOST", "") + t.Setenv("GH_HOST", "") + + spec, err := parseWorkflowSpec(tt.spec) + if err != nil { + t.Fatalf("parseWorkflowSpec(%q) unexpected error: %v", tt.spec, err) + } + + if tt.wantNoHost { + if spec.Host != "" { + t.Errorf("parseWorkflowSpec(%q) host = %q, want empty", tt.spec, spec.Host) + } + } else { + if spec.Host != tt.wantHost { + t.Errorf("parseWorkflowSpec(%q) host = %q, want %q", tt.spec, spec.Host, tt.wantHost) + } + } + }) + } +} + +// TestParseWorkflowSpecNoGHEHostPinning verifies that on public github.com the +// Host field is always empty for short-form specs (no pinning needed). +func TestParseWorkflowSpecNoGHEHostPinning(t *testing.T) { + // Clear all GHE env vars to simulate standard github.com environment + t.Setenv("GITHUB_SERVER_URL", "") + t.Setenv("GITHUB_ENTERPRISE_HOST", "") + t.Setenv("GITHUB_HOST", "") + t.Setenv("GH_HOST", "") + + specs := []string{ + "githubnext/agentics/daily-plan", + "githubnext/agentics/*", + "github/gh-aw/my-workflow", + "owner/repo/workflow", + } + + for _, s := range specs { + t.Run(s, func(t *testing.T) { + spec, err := parseWorkflowSpec(s) + if err != nil { + t.Fatalf("parseWorkflowSpec(%q) unexpected error: %v", s, err) + } + if spec.Host != "" { + t.Errorf("parseWorkflowSpec(%q) host = %q, want empty (no pinning on public GitHub)", s, spec.Host) + } + }) + } +} + func TestParseLocalWorkflowSpec(t *testing.T) { // Clear the repository slug cache to ensure clean test state ClearCurrentRepoSlugCache()