Files
bircni 93346f2c42 feat(actions): add artifact listing and download (#210)
Adds the ability to list and download Gitea Actions artifacts. Extends the existing `actions_run_read` tool with four new methods rather than adding a separate tool, keeping artifacts alongside the runs/jobs/logs they belong to.

### New methods (`actions_run_read`)

| Method               | Description                                | Key params                                             |
| -------------------- | ------------------------------------------ | ------------------------------------------------------ |
| `list_artifacts`     | List all artifacts in a repository         | `owner`, `repo`, optional `artifact_name` filter       |
| `list_run_artifacts` | List artifacts for one workflow run        | `owner`, `repo`, `run_id`                              |
| `get_artifact`       | Get metadata for a single artifact         | `owner`, `repo`, `artifact_id`                         |
| `download_artifact`  | Download an artifact's zip archive to disk | `owner`, `repo`, `artifact_id`, optional `output_path` |Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/210
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-07-19 19:50:17 +00:00

30 lines
1.1 KiB
Go

package actions
import "testing"
func TestArtifactFilename(t *testing.T) {
tests := []struct {
name string
meta map[string]any
artifactID int64
want string
}{
{"uses name", map[string]any{"name": "build-output"}, 7, "build-output.zip"},
{"trims whitespace", map[string]any{"name": " logs "}, 7, "logs.zip"},
{"missing name falls back to id", map[string]any{}, 7, "7.zip"},
{"empty name falls back to id", map[string]any{"name": ""}, 7, "7.zip"},
{"non-string name falls back to id", map[string]any{"name": 42}, 7, "7.zip"},
{"rejects forward slash traversal", map[string]any{"name": "../etc/passwd"}, 7, "7.zip"},
{"rejects backslash traversal", map[string]any{"name": `..\win`}, 7, "7.zip"},
{"rejects dot", map[string]any{"name": "."}, 7, "7.zip"},
{"rejects dotdot", map[string]any{"name": ".."}, 7, "7.zip"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := artifactFilename(tt.meta, tt.artifactID); got != tt.want {
t.Errorf("artifactFilename() = %q, want %q", got, tt.want)
}
})
}
}