* Archive all non-archived workspaces within a project whose GitHub PR is merged. * * This is intended for a single command-palette action (one backend call), to avoid * O(n) frontend→backend loops.
(projectPath: string)
| 6016 | * O(n) frontend→backend loops. |
| 6017 | */ |
| 6018 | async archiveMergedInProject(projectPath: string): Promise<Result<ArchiveMergedInProjectResult>> { |
| 6019 | const targetProjectPath = projectPath.trim(); |
| 6020 | if (!targetProjectPath) { |
| 6021 | return Err("projectPath is required"); |
| 6022 | } |
| 6023 | |
| 6024 | const archivedWorkspaceIds: string[] = []; |
| 6025 | const skippedWorkspaceIds: string[] = []; |
| 6026 | const errors: Array<{ workspaceId: string; error: string }> = []; |
| 6027 | |
| 6028 | try { |
| 6029 | const allMetadata = await this.config.getAllWorkspaceMetadata(); |
| 6030 | |
| 6031 | const candidates = allMetadata.filter((metadata) => { |
| 6032 | if (metadata.projectPath !== targetProjectPath) { |
| 6033 | return false; |
| 6034 | } |
| 6035 | return !isWorkspaceArchived(metadata.archivedAt, metadata.unarchivedAt); |
| 6036 | }); |
| 6037 | |
| 6038 | const mergedWorkspaceIds: string[] = []; |
| 6039 | |
| 6040 | const GH_CONCURRENCY_LIMIT = 4; |
| 6041 | const GH_TIMEOUT_SECS = 15; |
| 6042 | |
| 6043 | await forEachWithConcurrencyLimit(candidates, GH_CONCURRENCY_LIMIT, async (metadata) => { |
| 6044 | const workspaceId = metadata.id; |
| 6045 | |
| 6046 | try { |
| 6047 | const result = await this.executeBash( |
| 6048 | workspaceId, |
| 6049 | `gh pr view --json state 2>/dev/null || echo '{"no_pr":true}'`, |
| 6050 | { |
| 6051 | timeout_secs: GH_TIMEOUT_SECS, |
| 6052 | // gh requires the runtime environment — devcontainer auth/CLI |
| 6053 | // may only exist inside the container. |
| 6054 | } |
| 6055 | ); |
| 6056 | |
| 6057 | if (!result.success) { |
| 6058 | errors.push({ workspaceId, error: result.error }); |
| 6059 | return; |
| 6060 | } |
| 6061 | |
| 6062 | if (!result.data.success) { |
| 6063 | errors.push({ workspaceId, error: result.data.error }); |
| 6064 | return; |
| 6065 | } |
| 6066 | |
| 6067 | const output = result.data.output; |
| 6068 | if (!output || output.trim().length === 0) { |
| 6069 | errors.push({ workspaceId, error: "gh pr view returned empty output" }); |
| 6070 | return; |
| 6071 | } |
| 6072 | |
| 6073 | let parsed: unknown; |
| 6074 | try { |
| 6075 | parsed = JSON.parse(output); |
no test coverage detected