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
6 changes: 3 additions & 3 deletions actions/setup/js/create_pull_request.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -815,19 +815,19 @@ async function main(config = {}) {
>
> **Workflow Run:** [View run details and download patch artifact](${runUrl})
>
> The patch file is available in the \`agent-artifacts\` artifact in the workflow run linked above.
> The patch file is available in the \`agent\` artifact in the workflow run linked above.

To create a pull request with the changes:

\`\`\`sh
# Download the artifact from the workflow run
gh run download ${runId} -n agent-artifacts -D /tmp/agent-artifacts-${runId}
gh run download ${runId} -n agent -D /tmp/agent-${runId}

# Create a new branch
git checkout -b ${branchName}

# Apply the patch (--3way handles cross-repo patches where files may already exist)
git am --3way /tmp/agent-artifacts-${runId}/${patchFileName}
git am --3way /tmp/agent-${runId}/${patchFileName}

# Push the branch to origin
git push origin ${branchName}
Expand Down
6 changes: 3 additions & 3 deletions actions/setup/js/handle_agent_failure.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -423,16 +423,16 @@ function buildCodePushFailureContext(codePushFailureErrors, pullRequest = null,
if (runId) {
context += `\`\`\`sh
# Download the patch artifact from the workflow run
gh run download ${runId} -n agent-artifacts -D /tmp/agent-artifacts-${runId}
gh run download ${runId} -n agent -D /tmp/agent-${runId}

# List available patches
ls /tmp/agent-artifacts-${runId}/*.patch
ls /tmp/agent-${runId}/*.patch

# Create a new branch (adjust as needed)
git checkout -b aw/manual-apply

# Apply the patch (--3way handles cross-repo patches)
git am --3way /tmp/agent-artifacts-${runId}/YOUR_PATCH_FILE.patch
git am --3way /tmp/agent-${runId}/YOUR_PATCH_FILE.patch

# If there are conflicts, resolve them and continue (or abort):
# git am --continue
Expand Down
3 changes: 2 additions & 1 deletion actions/setup/js/handle_agent_failure.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ describe("handle_agent_failure", () => {
const result = buildCodePushFailureContext(errors, null, runUrl);
expect(result).toContain("🔀 Patch Apply Failed");
expect(result).toContain("gh run download 12345678");
expect(result).toContain("agent-artifacts");
expect(result).toContain("-n agent");
expect(result).toContain("/tmp/agent-");
expect(result).toContain("git am --3way");
expect(result).toContain(runUrl);
});
Expand Down
4 changes: 2 additions & 2 deletions actions/setup/js/setup_threat_detection.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async function main() {
}
const templateContent = fs.readFileSync(templatePath, "utf-8");
// Check if prompt file exists
// The agent-artifacts artifact is downloaded to /tmp/gh-aw/threat-detection/
// The agent artifact is downloaded to /tmp/gh-aw/threat-detection/
// GitHub Actions preserves the directory structure from the uploaded artifact
// (stripping the common /tmp/gh-aw/ prefix from the uploaded paths)
// So /tmp/gh-aw/aw-prompts/prompt.txt becomes /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt
Expand All @@ -52,7 +52,7 @@ async function main() {

// Check if patch file(s) exist
// Patches are now named aw-{branch}.patch (one per branch)
// The agent-artifacts artifact is downloaded to /tmp/gh-aw/threat-detection/
// The agent artifact is downloaded to /tmp/gh-aw/threat-detection/
const hasPatch = process.env.HAS_PATCH === "true";
const patchFiles = [];
try {
Expand Down
4 changes: 2 additions & 2 deletions actions/setup/md/manifest_protection_push_failed_fallback.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ To create a pull request with the changes:

```sh
# Download the patch from the workflow run
gh run download {run_id} -n agent-artifacts -D /tmp/agent-artifacts-{run_id}
gh run download {run_id} -n agent -D /tmp/agent-{run_id}

# Create a new branch
git checkout -b {branch_name} {base_branch}

# Apply the patch (--3way handles cross-repo patches)
git am --3way /tmp/agent-artifacts-{run_id}/{patch_file}
git am --3way /tmp/agent-{run_id}/{patch_file}

# Push the branch and create the pull request
git push origin {branch_name}
Expand Down
4 changes: 2 additions & 2 deletions actions/setup/md/manifest_protection_push_to_pr_fallback.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ To apply the patch after review:

```sh
# Download the artifact from the workflow run
gh run download {run_id} -n agent-artifacts -D /tmp/agent-artifacts-{run_id}
gh run download {run_id} -n agent -D /tmp/agent-{run_id}

# Apply the patch to the pull request branch
git fetch origin {branch_name}
git checkout {branch_name}
git am --3way /tmp/agent-artifacts-{run_id}/{patch_file_name}
git am --3way /tmp/agent-{run_id}/{patch_file_name}
git push origin {branch_name}
```

Expand Down
81 changes: 2 additions & 79 deletions docs/src/content/docs/reference/frontmatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -623,90 +623,13 @@ if: github.event_name == 'push'

Configure how `actions/checkout` is invoked in the agent job. Override default checkout settings or check out multiple repositories for cross-repository workflows.

By default, the agent job checks out the current repository at the triggering ref. Use `checkout:` to override this behavior.

### Disabling Checkout (`checkout: false`)

Set `checkout: false` to suppress the default `actions/checkout` step entirely. Use this for workflows that access repositories through MCP servers or other mechanisms that do not require a local clone:
Set `checkout: false` to disable the default repository checkout entirely — useful for workflows that access repositories through MCP servers or other mechanisms that do not require a local clone:

```yaml wrap
checkout: false
```

### Single-Repository Customization

Override default checkout settings for the current repository without defining a custom job:

```yaml wrap
checkout:
fetch-depth: 0 # Full git history
github-token: ${{ secrets.MY_TOKEN }} # Custom authentication
```

Use GitHub App authentication instead of a PAT:

```yaml wrap
checkout:
fetch-depth: 0
github-app:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
```

### Multiple Repositories

Check out additional repositories alongside the main repository using an array:

```yaml wrap
checkout:
- fetch-depth: 0 # main repo (full history)
- repository: owner/other-repo
path: ./libs/other
ref: main
github-token: ${{ secrets.CROSS_REPO_PAT }}
```

### Configuration Options

| Field | Type | Description |
|-------|------|-------------|
| `repository` | string | Repository in `owner/repo` format. Defaults to the current repository. |
| `ref` | string | Branch, tag, or SHA to checkout. Defaults to the triggering ref. |
| `path` | string | Relative path within `GITHUB_WORKSPACE`. Defaults to workspace root. |
| `github-token` | string | Token for authentication (`${{ secrets.MY_TOKEN }}` syntax). |
| `github-app` | object | GitHub App credentials (`app-id`, `private-key`, optional `owner`, `repositories`). Mutually exclusive with `github-token`. |
| `fetch-depth` | integer | Number of commits to fetch. `0` = full history, `1` = shallow (default). |
| `fetch` | string \| string[] | Additional Git refs to fetch after checkout (e.g., `["*"]` for all branches, `["refs/pulls/open/*"]` for open PRs). |
| `sparse-checkout` | string | Newline-separated patterns for sparse checkout (e.g., `.github/\nsrc/`). |
| `submodules` | string / bool | Submodule handling: `"recursive"`, `true`, or `false`. |
| `lfs` | boolean | Download Git LFS objects. |
| `current` | boolean | Mark this checkout as the agent's primary working repository. Only one entry may set `current: true`. |

### Fetching Additional Refs

Use `fetch:` to retrieve extra branches or refs after checkout — useful when the agent needs to inspect or modify branches beyond the default ref:

```yaml wrap
checkout:
- fetch: ["refs/pulls/open/*"] # fetch all open PR refs
fetch-depth: 0
```

The `fetch:` option emits a dedicated `git fetch` step after `actions/checkout`. No credentials are persisted to disk.

### Hub-and-Spoke: Setting a Primary Repository (`current: true`)

When running a workflow from a central repository that targets another repository, use `current: true` to tell the agent which checkout is its primary working target:

```yaml wrap
checkout:
- repository: org/target-repo
path: ./target
github-token: ${{ secrets.CROSS_REPO_PAT }}
current: true
```

See [Cross-Repository Operations](/gh-aw/reference/cross-repository/) for complete documentation on checkout merging behavior, multi-repo patterns, and advanced examples.
See [Cross-Repository Operations](/gh-aw/reference/cross-repository/) for complete documentation on checkout configuration options (including `fetch:`, `checkout: false`), merging behavior, and cross-repo examples.

## Custom Steps (`steps:`)

Expand Down
14 changes: 7 additions & 7 deletions pkg/cli/logs_awinfo_resolution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ func TestAwInfoResolutionWithoutFlattening(t *testing.T) {
// Error patterns have been removed - no error/warning detection
}

// TestMultipleArtifactFlattening tests that all files from unified agent-artifacts are flattened
// TestMultipleArtifactFlattening tests that all files from unified agent are flattened
func TestMultipleArtifactFlattening(t *testing.T) {
tempDir := t.TempDir()

// Create unified agent-artifacts structure as it would be downloaded
// All artifacts are now in a single agent-artifacts/tmp/gh-aw/ directory
nestedPath := filepath.Join(tempDir, "agent-artifacts", "tmp", "gh-aw")
// Create unified agent structure as it would be downloaded
// All artifacts are now in a single agent/tmp/gh-aw/ directory
nestedPath := filepath.Join(tempDir, "agent", "tmp", "gh-aw")
err := os.MkdirAll(nestedPath, 0755)
require.NoError(t, err)

Expand Down Expand Up @@ -133,8 +133,8 @@ func TestMultipleArtifactFlattening(t *testing.T) {
require.NoError(t, err, "File %s should exist at root", file)
}

// Verify agent-artifacts directory is removed
agentArtifactsDir := filepath.Join(tempDir, "agent-artifacts")
// Verify agent directory is removed
agentArtifactsDir := filepath.Join(tempDir, "agent")
_, err = os.Stat(agentArtifactsDir)
assert.True(t, os.IsNotExist(err), "agent-artifacts directory should be removed")
assert.True(t, os.IsNotExist(err), "agent directory should be removed")
}
2 changes: 1 addition & 1 deletion pkg/cli/logs_download.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ func downloadRunArtifacts(runID int64, outputDir string, verbose bool, owner, re
return fmt.Errorf("failed to flatten activation artifact: %w", err)
}

// Flatten unified agent-artifacts directory structure
// Flatten unified agent directory structure
if err := flattenUnifiedArtifact(outputDir, verbose); err != nil {
return fmt.Errorf("failed to flatten unified artifact: %w", err)
}
Expand Down
44 changes: 22 additions & 22 deletions pkg/cli/logs_flatten_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,14 @@ func TestFlattenSingleFileArtifactsInvalidDirectory(t *testing.T) {

func TestFlattenSingleFileArtifactsWithAuditFiles(t *testing.T) {
// Test that flattening works correctly for typical audit artifact files
// This test uses unified agent-artifacts structure
// This test uses unified agent structure
tmpDir := testutil.TempDir(t, "test-*")

// Create unified agent-artifacts structure as it would be downloaded by gh run download
// All single-file artifacts are now in agent-artifacts/tmp/gh-aw/
nestedPath := filepath.Join(tmpDir, "agent-artifacts", "tmp", "gh-aw")
// Create unified agent structure as it would be downloaded by gh run download
// All single-file artifacts are now in agent/tmp/gh-aw/
nestedPath := filepath.Join(tmpDir, "agent", "tmp", "gh-aw")
if err := os.MkdirAll(nestedPath, 0755); err != nil {
t.Fatalf("Failed to create agent-artifacts directory: %v", err)
t.Fatalf("Failed to create agent directory: %v", err)
}

unifiedArtifacts := map[string]string{
Expand Down Expand Up @@ -267,20 +267,20 @@ func TestFlattenSingleFileArtifactsWithAuditFiles(t *testing.T) {
}
}

// Verify agent-artifacts directory is removed
agentArtifactsDir := filepath.Join(tmpDir, "agent-artifacts")
// Verify agent directory is removed
agentArtifactsDir := filepath.Join(tmpDir, "agent")
if _, err := os.Stat(agentArtifactsDir); err == nil {
Comment on lines +271 to 272
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

The variable name agentArtifactsDir no longer matches the directory it points to (agent). Renaming it to something like agentDir (or unifiedArtifactDir) would reduce confusion, especially since this code was updated as part of the artifact rename.

Suggested change
agentArtifactsDir := filepath.Join(tmpDir, "agent")
if _, err := os.Stat(agentArtifactsDir); err == nil {
agentDir := filepath.Join(tmpDir, "agent")
if _, err := os.Stat(agentDir); err == nil {

Copilot uses AI. Check for mistakes.
t.Errorf("agent-artifacts directory should be removed after flattening")
t.Errorf("agent directory should be removed after flattening")
}
}

func TestAuditCanFindFlattenedArtifacts(t *testing.T) {
// Simulate what the audit command does - check that it can find artifacts after flattening
// This test uses unified agent-artifacts structure
// This test uses unified agent structure
tmpDir := testutil.TempDir(t, "test-*")

// Create realistic unified artifact structure before flattening
nestedPath := filepath.Join(tmpDir, "agent-artifacts", "tmp", "gh-aw")
nestedPath := filepath.Join(tmpDir, "agent", "tmp", "gh-aw")
if err := os.MkdirAll(nestedPath, 0755); err != nil {
t.Fatalf("Setup failed: %v", err)
}
Expand Down Expand Up @@ -352,8 +352,8 @@ func TestFlattenUnifiedArtifact(t *testing.T) {
{
name: "unified artifact with nested structure gets flattened",
setup: func(dir string) error {
// Create the structure: agent-artifacts/tmp/gh-aw/...
nestedPath := filepath.Join(dir, "agent-artifacts", "tmp", "gh-aw")
// Create the structure: agent/tmp/gh-aw/...
nestedPath := filepath.Join(dir, "agent", "tmp", "gh-aw")
if err := os.MkdirAll(nestedPath, 0755); err != nil {
return err
}
Expand Down Expand Up @@ -387,16 +387,16 @@ func TestFlattenUnifiedArtifact(t *testing.T) {
"aw-prompts",
"mcp-logs",
},
unexpectedDirs: []string{"agent-artifacts", "tmp", "gh-aw"},
unexpectedDirs: []string{"agent", "tmp", "gh-aw"},
unexpectedFiles: []string{
"agent-artifacts/tmp/gh-aw/aw_info.json",
"agent/tmp/gh-aw/aw_info.json",
"tmp/gh-aw/aw_info.json",
},
},
{
name: "no agent-artifacts directory - no-op",
name: "no agent directory - no-op",
setup: func(dir string) error {
// Create a regular file structure without agent-artifacts
// Create a regular file structure without agent
return os.WriteFile(filepath.Join(dir, "regular.txt"), []byte("test"), 0644)
},
expectedFiles: []string{"regular.txt"},
Expand Down Expand Up @@ -430,14 +430,14 @@ func TestFlattenUnifiedArtifact(t *testing.T) {
unexpectedFiles: []string{"agent/agent_output.json"},
},
{
name: "agent-artifacts without tmp/gh-aw structure - flatten directly",
name: "agent without tmp/gh-aw structure - flatten directly",
setup: func(dir string) error {
// Create agent-artifacts with new structure (files directly in agent-artifacts/)
artifactDir := filepath.Join(dir, "agent-artifacts")
// Create agent with new structure (files directly in agent/)
artifactDir := filepath.Join(dir, "agent")
if err := os.MkdirAll(artifactDir, 0755); err != nil {
return err
}
// Create file directly in agent-artifacts (new structure)
// Create file directly in agent (new structure)
if err := os.WriteFile(filepath.Join(artifactDir, "file.txt"), []byte("test"), 0644); err != nil {
return err
}
Expand All @@ -450,7 +450,7 @@ func TestFlattenUnifiedArtifact(t *testing.T) {
},
expectedDirs: []string{"subdir"},
expectedFiles: []string{"file.txt", "subdir/nested.txt"},
unexpectedFiles: []string{"agent-artifacts/file.txt"},
unexpectedFiles: []string{"agent/file.txt"},
},
{
name: "new 'agent' artifact takes precedence over legacy 'agent-artifacts'",
Expand Down Expand Up @@ -572,7 +572,7 @@ func TestFlattenArtifactTreeNestedDirs(t *testing.T) {
func TestFlattenArtifactTreeDifferentSourceAndArtifactDir(t *testing.T) {
// Covers the old-structure unified artifact case where sourceDir is a subdirectory of artifactDir.
outputDir := t.TempDir()
artifactDir := filepath.Join(outputDir, "agent-artifacts")
artifactDir := filepath.Join(outputDir, "agent")
sourceDir := filepath.Join(artifactDir, "tmp", "gh-aw")
require.NoError(t, os.MkdirAll(sourceDir, 0750), "setup: create old-structure sourceDir")
require.NoError(t, os.WriteFile(filepath.Join(sourceDir, "session.json"), []byte(`{}`), 0600), "setup: write file")
Expand Down
1 change: 0 additions & 1 deletion pkg/parser/remote_fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,6 @@ func downloadFileViaRawURL(owner, repo, filePath, ref string) ([]byte, error) {
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, resp.Body)
return nil, fmt.Errorf("raw URL returned HTTP %d for %s", resp.StatusCode, rawURL)
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/artifact_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type ArtifactManager struct {

// ArtifactUpload represents an artifact upload operation
type ArtifactUpload struct {
// Name is the artifact name (e.g., "agent-artifacts")
// Name is the artifact name (e.g., "agent")
Name string

// Paths are the file/directory paths being uploaded
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/test-yaml-import.lock.yml
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: agent-artifacts
name: agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
/tmp/gh-aw/aw_info.json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: agent-artifacts
name: agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
/tmp/gh-aw/aw_info.json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ jobs:
continue-on-error: true
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: agent-artifacts
name: agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
/tmp/gh-aw/aw_info.json
Expand Down
Loading
Loading