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

To apply the patch locally:

\`\`\`sh
# Download the artifact from the workflow run ${runUrl}
# (Use GitHub MCP tools if gh CLI is not available)
gh run download ${runId} -n aw.patch
gh run download ${runId} -n agent-artifacts

# The patch file will be at agent-artifacts/tmp/gh-aw/aw.patch after download
# Apply the patch
git am aw.patch
git am agent-artifacts/tmp/gh-aw/aw.patch
\`\`\`
${patchPreview}`;

Expand Down
46 changes: 20 additions & 26 deletions pkg/cli/logs_awinfo_resolution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,28 +119,31 @@ func TestAwInfoResolutionWithoutFlattening(t *testing.T) {
assert.Positive(t, warnCount, "Fallback parser should detect warnings")
}

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

// Create multiple single-file artifacts as they would be downloaded
// 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")
err := os.MkdirAll(nestedPath, 0755)
require.NoError(t, err)

artifacts := map[string]string{
"aw-info/aw_info.json": `{"engine_id":"copilot"}`,
"safe-output/safe_output.jsonl": `{"type":"create_issue"}`,
"aw-patch/aw.patch": "diff --git a/test.txt",
"prompt/prompt.txt": "Test prompt",
"aw_info.json": `{"engine_id":"copilot"}`,
"safe_output.jsonl": `{"type":"create_issue"}`,
"aw.patch": "diff --git a/test.txt",
"prompt.txt": "Test prompt",
}

for path, content := range artifacts {
fullPath := filepath.Join(tempDir, path)
err := os.MkdirAll(filepath.Dir(fullPath), 0755)
require.NoError(t, err)
err = os.WriteFile(fullPath, []byte(content), 0644)
for filename, content := range artifacts {
fullPath := filepath.Join(nestedPath, filename)
err := os.WriteFile(fullPath, []byte(content), 0644)
require.NoError(t, err)
}

// Flatten all artifacts
err := flattenSingleFileArtifacts(tempDir, true)
// Flatten unified artifact
err = flattenUnifiedArtifact(tempDir, true)
require.NoError(t, err)

// Verify all files are at root level
Expand All @@ -157,17 +160,8 @@ func TestMultipleArtifactFlattening(t *testing.T) {
require.NoError(t, err, "File %s should exist at root", file)
}

// Verify artifact directories are removed
artifactDirs := []string{
"aw-info",
"safe-output",
"aw-patch",
"prompt",
}

for _, dir := range artifactDirs {
path := filepath.Join(tempDir, dir)
_, err := os.Stat(path)
assert.True(t, os.IsNotExist(err), "Directory %s should be removed", dir)
}
// Verify agent-artifacts directory is removed
agentArtifactsDir := filepath.Join(tempDir, "agent-artifacts")
_, err = os.Stat(agentArtifactsDir)
assert.True(t, os.IsNotExist(err), "agent-artifacts directory should be removed")
}
75 changes: 47 additions & 28 deletions pkg/cli/logs_flatten_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,21 +172,37 @@ func TestFlattenSingleFileArtifactsInvalidDirectory(t *testing.T) {

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

// Create artifact structure as it would be downloaded by gh run download
// Using NEW artifact names: aw-info, safe-output, agent-output, prompt
artifacts := map[string]string{
"aw-info/aw_info.json": `{"engine_id":"claude","workflow_name":"test"}`,
"safe-output/safe_output.jsonl": `{"action":"create_issue","title":"test"}`,
"aw-patch/aw.patch": "diff --git a/test.txt b/test.txt\n",
// 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")
if err := os.MkdirAll(nestedPath, 0755); err != nil {
t.Fatalf("Failed to create agent-artifacts directory: %v", err)
}

unifiedArtifacts := map[string]string{
"aw_info.json": `{"engine_id":"claude","workflow_name":"test"}`,
"safe_output.jsonl": `{"action":"create_issue","title":"test"}`,
"aw.patch": "diff --git a/test.txt b/test.txt\n",
}

for filename, content := range unifiedArtifacts {
fullPath := filepath.Join(nestedPath, filename)
if err := os.WriteFile(fullPath, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write file %s: %v", filename, err)
}
}

// Also create multi-file artifact directories (these remain separate)
multiFileArtifacts := map[string]string{
"agent_outputs/output1.txt": "log output 1",
"agent_outputs/output2.txt": "log output 2",
"agent_outputs/nested/subfile.txt": "nested file",
}

for path, content := range artifacts {
for path, content := range multiFileArtifacts {
fullPath := filepath.Join(tmpDir, path)
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
t.Fatalf("Failed to create directory for %s: %v", path, err)
Expand All @@ -196,7 +212,12 @@ func TestFlattenSingleFileArtifactsWithAuditFiles(t *testing.T) {
}
}

// Run flattening
// Run flattening for unified artifact
if err := flattenUnifiedArtifact(tmpDir, true); err != nil {
t.Fatalf("flattenUnifiedArtifact failed: %v", err)
}

// Also run single file artifact flattening for any remaining separate artifacts
if err := flattenSingleFileArtifacts(tmpDir, true); err != nil {
t.Fatalf("flattenSingleFileArtifacts failed: %v", err)
}
Expand Down Expand Up @@ -245,41 +266,39 @@ func TestFlattenSingleFileArtifactsWithAuditFiles(t *testing.T) {
}
}

// Verify original artifact directories are removed
removedDirs := []string{"aw-info", "safe-output", "aw-patch"}
for _, dir := range removedDirs {
path := filepath.Join(tmpDir, dir)
if _, err := os.Stat(path); err == nil {
t.Errorf("Single-file artifact directory %s should be removed after flattening", dir)
}
// Verify agent-artifacts directory is removed
agentArtifactsDir := filepath.Join(tmpDir, "agent-artifacts")
if _, err := os.Stat(agentArtifactsDir); err == nil {
t.Errorf("agent-artifacts 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 NEW artifact names (upload-artifact@v5 compliant)
// This test uses unified agent-artifacts structure
tmpDir := testutil.TempDir(t, "test-*")

// Create realistic artifact structure before flattening
// Using NEW artifact names: aw-info, safe-output
// Create realistic unified artifact structure before flattening
nestedPath := filepath.Join(tmpDir, "agent-artifacts", "tmp", "gh-aw")
if err := os.MkdirAll(nestedPath, 0755); err != nil {
t.Fatalf("Setup failed: %v", err)
}

testArtifacts := map[string]string{
"aw-info/aw_info.json": `{"engine_id":"claude","workflow_name":"github-mcp-tools-report","run_id":123456}`,
"safe-output/safe_output.jsonl": `{"action":"create_discussion","title":"GitHub MCP Tools Report"}`,
"aw-patch/aw.patch": "diff --git a/report.md b/report.md\nnew file mode 100644\n",
"aw_info.json": `{"engine_id":"claude","workflow_name":"github-mcp-tools-report","run_id":123456}`,
"safe_output.jsonl": `{"action":"create_discussion","title":"GitHub MCP Tools Report"}`,
"aw.patch": "diff --git a/report.md b/report.md\nnew file mode 100644\n",
}

for path, content := range testArtifacts {
fullPath := filepath.Join(tmpDir, path)
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
t.Fatalf("Setup failed: %v", err)
}
for filename, content := range testArtifacts {
fullPath := filepath.Join(nestedPath, filename)
if err := os.WriteFile(fullPath, []byte(content), 0644); err != nil {
t.Fatalf("Setup failed: %v", err)
}
}

// Flatten artifacts (this happens during download)
if err := flattenSingleFileArtifacts(tmpDir, false); err != nil {
if err := flattenUnifiedArtifact(tmpDir, false); err != nil {
t.Fatalf("Flattening failed: %v", err)
}

Expand Down
14 changes: 8 additions & 6 deletions specs/safe-output-messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -505,17 +505,18 @@ index 1234567..abcdefg 100644
>
> **Workflow Run:** [View run details and download patch artifact](run_url)
>
> The patch file is available as an artifact (`aw.patch`) in the workflow run linked above.
> The patch file is available in the `agent-artifacts` artifact in the workflow run linked above.

To apply the patch locally:

\`\`\`sh
# Download the artifact from the workflow run [url]
# (Use GitHub MCP tools if gh CLI is not available)
gh run download [run_id] -n aw.patch
gh run download [run_id] -n agent-artifacts

# The patch file will be at agent-artifacts/tmp/gh-aw/aw.patch after download
# Apply the patch
git am aw.patch
git am agent-artifacts/tmp/gh-aw/aw.patch
\`\`\`

[patch preview if available]
Expand All @@ -530,17 +531,18 @@ git am aw.patch
>
> **Workflow Run:** [View run details and download patch artifact](https://github.com/example/repo/actions/runs/12345)
>
> The patch file is available as an artifact (`aw.patch`) in the workflow run linked above.
> The patch file is available in the `agent-artifacts` artifact in the workflow run linked above.

To apply the patch locally:

```sh
# Download the artifact from the workflow run
# (Use GitHub MCP tools if gh CLI is not available)
gh run download 12345 -n aw.patch
gh run download 12345 -n agent-artifacts

# The patch file will be at agent-artifacts/tmp/gh-aw/aw.patch after download
# Apply the patch
git am aw.patch
git am agent-artifacts/tmp/gh-aw/aw.patch
```text

<details><summary>Show patch (45 lines)</summary>
Expand Down
Loading