Merge branch 'main' into fix/1773-warn-logged-out-import · Entire

Home

Log in

Merge branch 'main' into fix/1773-warn-logged-out-import

e430a64→main·

karthik-rameshkumar·21h ago·16 files·+1,813 added/-64 removed

Changes

16

1 unmodified line

2
3
4
5
6
7
8
9
10
11
12
46 unmodified lines

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

1 unmodified line

import (
    "context"
    "encoding/json"
    "errors"
    "os"
    "os/exec"
    "path/filepath"
    "strings"
    "testing"

46 unmodified lines

}
}

func flagValue(args []string, name string) (string, bool) {
    for i, a := range args {
        if a == name && i+1 < len(args) {
            return args[i+1], true
        }
    }
    return "", false
}

func TestBuildGenerateArgs_IsolatesSettingSources(t *testing.T) {
    t.Parallel()
    // Isolation is the security-critical invariant: --setting-sources must be
    // empty so user-level hooks and tool permissions (e.g. bypassPermissions)
    // are never loaded for this internal, injection-exposed call.
    args := buildGenerateArgs("haiku", "")
    got, ok := flagValue(args, "--setting-sources")
    if !ok {
        t.Fatalf("--setting-sources flag missing from args: %v", args)
    }
    if got != "" {
        t.Fatalf("--setting-sources = %q, want %q (must load no sources)", got, "")
    }
    // With no settings path, we inject nothing extra.
    if _, ok := flagValue(args, "--settings"); ok {
        t.Fatalf("--settings must be absent when there is no settings path: %v", args)
    }
}

func TestBuildGenerateArgs_PassesSettingsAsPath(t *testing.T) {
    t.Parallel()
    // The injected settings must be passed as a file path, not inline JSON, so a
    // key-bearing apiKeyHelper never lands in argv (ps / /proc/<pid>/cmdline).
    path := "/tmp/entire-claude-auth-123.json"
    args := buildGenerateArgs("haiku", path)

if got, _ := flagValue(args, "--setting-sources"); got != "" {
        t.Fatalf("--setting-sources = %q, want empty", got)
    }
    got, ok := flagValue(args, "--settings")
    if !ok {
        t.Fatalf("--settings flag missing: %v", args)
    }
    if got != path {
        t.Fatalf("--settings = %q, want the file path %q", got, path)
    }
    // Guard against regressing to inline JSON in argv.
    if strings.Contains(got, "{") {
        t.Fatalf("--settings must be a path, not inline JSON: %q", got)
    }
}

func TestWriteAuthSettingsFile_WritesOnlyAPIKeyHelper0600(t *testing.T) {
    t.Parallel()
    helper := `echo "sk-ant-secret"` // could embed a literal key
    path, cleanup, err := writeAuthSettingsFile(helper)
    if err != nil {
        t.Fatalf("writeAuthSettingsFile: %v", err)
    }
    if cleanup == nil {
        t.Fatal("cleanup func is nil")
    }
    defer cleanup()

info, err := os.Stat(path)
    if err != nil {
        t.Fatalf("stat settings file: %v", err)
    }
    if perm := info.Mode().Perm(); perm != 0o600 {
        t.Fatalf("settings file perm = %o, want 0600", perm)
    }

data, err := os.ReadFile(path)
    if err != nil {
        t.Fatalf("read settings file: %v", err)
    }
    var settings map[string]any
    if err := json.Unmarshal(data, &settings); err != nil {
        t.Fatalf("settings file is not valid JSON: %v (%s)", err, data)
    }
    if settings["apiKeyHelper"] != helper {
        t.Fatalf("apiKeyHelper = %v, want %q", settings["apiKeyHelper"], helper)
    }
    if len(settings) != 1 {
        t.Fatalf("settings file must contain only apiKeyHelper, got %v", settings)
    }

cleanup()
    if _, err := os.Stat(path); !os.IsNotExist(err) {
        t.Fatalf("cleanup did not remove settings file (stat err=%v)", err)
    }
}

func TestWriteAuthSettingsFile_EmptyHelperNoFile(t *testing.T) {
    t.Parallel()
    path, cleanup, err := writeAuthSettingsFile("")
    if err != nil {
        t.Fatalf("writeAuthSettingsFile(\"\"): %v", err)
    }
    if path != "" {
        t.Fatalf("path = %q, want empty for no apiKeyHelper", path)
    }
    if cleanup != nil {
        t.Fatal("cleanup should be nil when no file is written")
    }
}

func TestReadUserAPIKeyHelper_FromClaudeConfigDir(t *testing.T) {
    dir := t.TempDir()
    t.Setenv("CLAUDE_CONFIG_DIR", dir)
    if err := os.WriteFile(filepath.Join(dir, "settings.json"),
        []byte(`{"apiKeyHelper":"echo secret-cmd","permissions":{"defaultMode":"bypassPermissions"}}`), 0o600); err != nil {
        t.Fatal(err)
    }
    if got := readUserAPIKeyHelper(); got != "echo secret-cmd" {
        t.Fatalf("readUserAPIKeyHelper() = %q, want %q", got, "echo secret-cmd")
    }
}

func TestReadUserAPIKeyHelper_MissingFileReturnsEmpty(t *testing.T) {
    t.Setenv("CLAUDE_CONFIG_DIR", t.TempDir()) // no settings.json inside
    if got := readUserAPIKeyHelper(); got != "" {
        t.Fatalf("readUserAPIKeyHelper() = %q, want empty for missing file", got)
    }
}

func TestGenerateText_ArrayResponse(t *testing.T) {
    t.Parallel()
    ag := &ClaudeCodeAgent{

Mcmd/entire/cli/agent/claudecode/claude_test.go+128

2 unmodified lines

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
17 unmodified lines

139
140
141
38
39
40
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

2 unmodified lines

import (
    "bytes"
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "os"
    "os/exec"
    "path/filepath"
    "strings"

"github.com/entireio/cli/cmd/entire/cli/agent"
)

// buildGenerateArgs assembles the claude CLI argv for a --print text-generation
// call.
//
// The subprocess must stay isolated from the user's project/local AND user
// settings: loading them would fire user-level hooks and, worse, honor
// user-level tool permissions (e.g. permissions.defaultMode=bypassPermissions),
// which would let prompt-injection in the untrusted dispatch data drive tool
// execution. So we pass --setting-sources "" (load nothing).
//
// The one thing we genuinely need from the user settings is auth. Users on API
// billing configure it with `apiKeyHelper` (a command that prints the key),
// which lives in user settings and is therefore dropped by --setting-sources "".
// Rather than load the whole settings file back (and re-inherit hooks and
// permissions), we extract only apiKeyHelper and re-inject it via a --settings
// file (settingsPath), so auth works while nothing else from the user's settings
// is loaded.
//
// The injected settings are passed as a file path, not an inline JSON string:
// apiKeyHelper can embed a literal key, and an inline value would land in the
// process argv (visible via ps / /proc/<pid>/cmdline / EDR tooling). The file is
// written 0600 (see writeAuthSettingsFile), matching settings.json's protection.
//
// Auth methods that do not live in user settings — an exported ANTHROPIC_API_KEY
// (survives StripGitEnv) and keychain/subscription credentials — keep working
// without any injection (settingsPath == "").
func buildGenerateArgs(model, settingsPath string) []string {
    args := []string{
        "--print", "--output-format", "json",
        "--model", model,
        "--setting-sources", "",
    }
    if settingsPath != "" {
        args = append(args, "--settings", settingsPath)
    }
    return args
}

// writeAuthSettingsFile writes a minimal claude settings file containing only
// the given apiKeyHelper and returns its path plus a cleanup func. The file is
// created 0600 so the (possibly key-bearing) helper is no more exposed than the
// user's own settings.json. Returns ("", nil, nil) when apiKeyHelper is empty.
func writeAuthSettingsFile(apiKeyHelper string) (string, func(), error) {
    if strings.TrimSpace(apiKeyHelper) == "" {
        return "", nil, nil
    }
    data, err := json.Marshal(map[string]string{"apiKeyHelper": apiKeyHelper})
    if err != nil {
        return "", nil, fmt.Errorf("marshal auth settings: %w", err)
    }
    f, err := os.CreateTemp("", "entire-claude-auth-*.json") // 0600 by default
    if err != nil {
        return "", nil, fmt.Errorf("create auth settings file: %w", err)
    }
    path := f.Name()
    cleanup := func() { _ = os.Remove(path) }
    if _, err := f.Write(data); err != nil {
        _ = f.Close()
        cleanup()
        return "", nil, fmt.Errorf("write auth settings file: %w", err)
    }
    if err := f.Close(); err != nil {
        cleanup()
        return "", nil, fmt.Errorf("close auth settings file: %w", err)
    }
    return path, cleanup, nil
}

// userClaudeSettingsPath resolves the user's claude settings.json the same way
// the claude CLI does: $CLAUDE_CONFIG_DIR/settings.json when set, otherwise
// ~/.claude/settings.json.
func userClaudeSettingsPath() (string, error) {
    if dir := strings.TrimSpace(os.Getenv("CLAUDE_CONFIG_DIR")); dir != "" {
        return filepath.Join(dir, "settings.json"), nil
    }
    home, err := os.UserHomeDir()
    if err != nil {
        return "", fmt.Errorf("resolve home directory: %w", err)
    }
    return filepath.Join(home, ".claude", "settings.json"), nil
}

// readUserAPIKeyHelper returns the apiKeyHelper field from the user's claude
// settings, or "" if absent. Best-effort: a missing file or malformed JSON
// yields "" so we fall back to env/keychain auth rather than failing.
func readUserAPIKeyHelper() string {
    path, err := userClaudeSettingsPath()
    if err != nil {
        return ""
    }
    data, err := os.ReadFile(path) //nolint:gosec // path is the user's own claude config, not attacker-controlled
    if err != nil {
        return ""
    }
    var settings struct {
        APIKeyHelper string `json:"apiKeyHelper"`
    }
    if err := json.Unmarshal(data, &settings); err != nil {
        return ""
    }
    return strings.TrimSpace(settings.APIKeyHelper)
}

// GenerateText sends a prompt to the Claude CLI and returns the raw text response.
// Implements the agent.TextGenerator interface.
// The model parameter hints which model to use (e.g., "haiku", "sonnet").
17 unmodified lines

commandRunner = exec.CommandContext
    }

cmd := commandRunner(ctx, claudePath,
        "--print", "--output-format", "json",
        "--model", model, "--setting-sources", "")
    // Run isolated from all setting sources (see buildGenerateArgs), re-injecting
    // only the user's apiKeyHelper (via a 0600 file, never argv) so API-billing
    // auth keeps working without re-inheriting user hooks or tool permissions.
    // Best-effort: if extracting/writing the helper fails, fall back to running
    // without it (env/keychain auth still work) rather than failing the call.
    settingsPath, cleanup, err := writeAuthSettingsFile(readUserAPIKeyHelper())
    if err != nil {
        settingsPath = ""
    }
    if cleanup != nil {
        defer cleanup()
    }

cmd := commandRunner(ctx, claudePath, buildGenerateArgs(model, settingsPath)...)

// Isolate from the user's git repo to prevent recursive hook triggers
    // and index pollution (matches agent.RunIsolatedTextGeneratorCLI behavior).

Mcmd/entire/cli/agent/claudecode/generate.go+118/-3

11 unmodified lines

12
13
14
15
16
17
18
143 unmodified lines

162
163
164
164
165
166
166
167
168
169
170
171
172
173
174
17 unmodified lines

192
193
194
195
196
197
198
16 unmodified lines

215
216
217
212
213
214
215
216
217
218
219
220
221
222
218
219
220
1 unmodified line

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
237
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
2 unmodified lines

331
332
333
334
335
336
248
337
338
339
340
341
342
343
255
256
257
258
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
3 unmodified lines

369
370
371
268
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

11 unmodified lines

"github.com/entireio/cli/cmd/entire/cli/auth"
    "github.com/entireio/cli/cmd/entire/cli/checkpoint"
    checkpointid "github.com/entireio/cli/cmd/entire/cli/checkpoint/id"
    "github.com/entireio/cli/cmd/entire/cli/gitrepo"
    "github.com/entireio/cli/cmd/entire/cli/logging"
    "github.com/entireio/cli/cmd/entire/cli/paths"
143 unmodified lines

for _, branch := range branches {
        branchSet[branch] = struct{}{}
    }
    reachableCheckpointIDs := map[string]struct{}{}
    reachableCheckpointIDs := map[string]time.Time{}
    if opts.ImplicitCurrentBranch && !opts.AllBranches {
        reachableCheckpointIDs, err = reachableCheckpointIDsInRange(ctx, repoRoot, branchLocalRevRange(ctx, repoRoot), since)
        currentBranch := ""
        if len(branches) > 0 {
            currentBranch = branches[0]
        }
        reachableCheckpointIDs, err = reachableCheckpointIDsInRange(ctx, repoRoot, branchLocalRevRange(ctx, repoRoot, currentBranch), since, until)
        if err != nil {
            return nil, err
        }
17 unmodified lines

}

candidates := make([]candidate, 0, len(infos))
    seen := make(map[string]struct{}, len(infos))
    for _, info := range infos {
        if info.CreatedAt.Before(since) || !info.CreatedAt.Before(until) {
            continue
16 unmodified lines

}
        }

localSummary := ""
        if len(summary.Sessions) > 0 {
            latestIndex := len(summary.Sessions) - 1
            if metadata, err := store.ReadSessionMetadata(ctx, info.CheckpointID, latestIndex); err == nil && metadata != nil && metadata.Summary != nil {
                localSummary = strings.TrimSpace(metadata.Summary.Outcome)
                if localSummary == "" {
                    localSummary = strings.TrimSpace(metadata.Summary.Intent)
                }
            }
        }

commitSubject := commitSubjectsByCheckpoint[info.CheckpointID.String()]
        candidates = append(candidates, candidate{
            CheckpointID:      info.CheckpointID.String(),
1 unmodified line

Branch:            summary.Branch,
            CreatedAt:         info.CreatedAt,
            CommitSubject:     commitSubject,
            LocalSummaryTitle: readLocalSummaryTitle(ctx, store, info.CheckpointID, summary),
        })
        seen[info.CheckpointID.String()] = struct{}{}
    }

// Second pass: checkpoints referenced by branch commit trailers in the
    // window that store.List did not surface. store.List only enumerates
    // checkpoints present in the local checkout, but on a checkout that has not
    // fetched recent checkpoints (the common case — checkpoints are pushed to
    // the remote from other worktrees) the recent work is missing locally even
    // though it is reachable from HEAD. The commit subject is always available
    // from git log, so we can summarize that work from the trailer + subject
    // without a (slow) per-checkpoint network fetch; when the checkpoint does
    // happen to be local we still prefer its richer session summary. Windowed
    // by commit ("landed on branch") time, since a CheckpointSummary carries no
    // CreatedAt of its own.
    for idStr, commitTime := range reachableCheckpointIDs {
        if _, ok := seen[idStr]; ok {
            continue
        }
        if commitTime.Before(since) || !commitTime.Before(until) {
            continue
        }
        commitSubject := commitSubjectsByCheckpoint[idStr]

// Opportunistic local read for a richer title/branch — no fetcher is
        // wired, so this never touches the network; a checkpoint absent locally
        // resolves to nil and we fall back to the commit subject.
        branch := ""
        localSummary := ""
        if cid, cidErr := checkpointid.NewCheckpointID(idStr); cidErr == nil {
            if summary, readErr := store.Read(ctx, cid); readErr == nil && summary != nil {
                branch = summary.Branch
                localSummary = readLocalSummaryTitle(ctx, store, cid, summary)
            }
        }

if strings.TrimSpace(localSummary) == "" && strings.TrimSpace(commitSubject) == "" {
            continue
        }
        candidates = append(candidates, candidate{
            CheckpointID:      idStr,
            RepoFullName:      repoFullName,
            Branch:            branch,
            CreatedAt:         commitTime,
            CommitSubject:     commitSubject,
            LocalSummaryTitle: localSummary,
        })
        seen[idStr] = struct{}{}
    }

// The second pass ranges over a map (randomized iteration order), so sort
    // before returning to keep bullet order — and therefore the LLM-authored
    // summary — stable across runs. Newest first, with the checkpoint ID as a
    // deterministic tiebreak for equal timestamps.
    sortCandidatesByRecency(candidates)
    return candidates, nil
}

func reachableCheckpointIDsInRange(ctx context.Context, repoRoot, revRange string, since time.Time) (map[string]struct{}, error) {
// sortCandidatesByRecency orders candidates most-recent-first by CreatedAt,
// breaking ties by checkpoint ID so the order is fully deterministic.
func sortCandidatesByRecency(candidates []candidate) {
    sort.SliceStable(candidates, func(i, j int) bool {
        if !candidates[i].CreatedAt.Equal(candidates[j].CreatedAt) {
            return candidates[i].CreatedAt.After(candidates[j].CreatedAt)
        }
        return candidates[i].CheckpointID < candidates[j].CheckpointID
    })
}

// readLocalSummaryTitle returns the latest session's outcome (falling back to
// its intent) for use as a bullet title, or "" when no session summary is
// available.
func readLocalSummaryTitle(ctx context.Context, store checkpoint.PersistentStore, cid checkpointid.CheckpointID, summary *checkpoint.CheckpointSummary) string {
    if summary == nil || len(summary.Sessions) == 0 {
        return ""
    }
    latestIndex := len(summary.Sessions) - 1
    metadata, err := store.ReadSessionMetadata(ctx, cid, latestIndex)
    if err != nil || metadata == nil || metadata.Summary == nil {
        return ""
    }
    if outcome := strings.TrimSpace(metadata.Summary.Outcome); outcome != "" {
        return outcome
    }
    return strings.TrimSpace(metadata.Summary.Intent)
}

// reachableCheckpointIDsInRange maps each checkpoint ID referenced by a commit
// trailer in revRange, within the window [since, until), to the most recent\
// referencing commit time *that falls inside the window*. The commit time is\
// the "landed on this branch" timestamp, used both for membership checks and to\
// window checkpoints that are fetched on demand by ID (whose CheckpointSummary\
// carries no CreatedAt of its own).\
//\
// Commits outside the window are ignored entirely, so a checkpoint referenced\
// by both an in-window commit and a later out-of-window commit still records\
// its in-window time (and is therefore not dropped by the caller's window\
// check). git's --since/--until only bound the scan; the explicit in-loop check\
// is authoritative for the half-open [since, until) boundary.\
func reachableCheckpointIDsInRange(ctx context.Context, repoRoot, revRange string, since, until time.Time) (map[string]time.Time, error) {\
    cmd := exec.CommandContext(\
        ctx,\
        "git",\
2 unmodified lines\
\
        "log",\
        revRange,\
        "--since="+since.UTC().Format(time.RFC3339),\
        "--until="+until.UTC().Format(time.RFC3339),\
        "--grep",\
        "Entire-Checkpoint:",\
        "--format=%B%x00",\
        "--format=%cI%x00%B%x00%x00",\
    )\
    output, err := cmd.Output()\
    if err != nil {\
        return nil, fmt.Errorf("list HEAD checkpoint trailers: %w", err)\
    }\
\
    reachable := make(map[string]struct{})\
    for _, message := range strings.Split(string(output), "\x00") {\
        for _, checkpointID := range trailers.ParseAllCheckpoints(message) {\
            reachable[checkpointID.String()] = struct{}{}\
    reachable := make(map[string]time.Time)\
    for _, record := range strings.Split(string(output), "\x00\x00") {\
        record = strings.TrimLeft(record, "\n")\
        parts := strings.SplitN(record, "\x00", 2)\
        if len(parts) != 2 {\
            continue\
        }\
        commitTime, parseErr := time.Parse(time.RFC3339, strings.TrimSpace(parts[0]))\
        if parseErr != nil {\
            continue\
        }\
        if commitTime.Before(since) || !commitTime.Before(until) {\
            continue\
        }\
        for _, checkpointID := range trailers.ParseAllCheckpoints(parts[1]) {\
            idStr := checkpointID.String()\
            if existing, ok := reachable[idStr]; !ok || commitTime.After(existing) {\
                reachable[idStr] = commitTime\
            }\
        }\
    }\
    return reachable, nil\
3 unmodified lines\
\
// those unique to the current branch — reachable from HEAD but not from the\
// repository's default branch. Falls back to "HEAD" when no default branch\
// can be resolved (e.g. a fresh repo with no main/master ref).\
func branchLocalRevRange(ctx context.Context, repoRoot string) string {\
//\
// When the current branch IS the default branch there is no parent history to\
// exclude: base..HEAD is empty on an up-to-date default branch, which would\
// drop every checkpoint whose summary.Branch is a (now-merged) feature branch\
// and leave the dispatch effectively empty. In that case we summarize\
// everything reachable from HEAD in the window, matching the server-side\
// dispatch. The base..HEAD exclusion only applies to feature branches.\
func branchLocalRevRange(ctx context.Context, repoRoot, currentBranch string) string {\
    base := defaultBranchRef(ctx, repoRoot)\
    if base == "" {\
        return "HEAD"\
    }\
    if currentBranch != "" && strings.TrimPrefix(base, "origin/") == currentBranch {\
        return "HEAD"\
    }\
    return base + "..HEAD"\
}\
```\
\
Mcmd/entire/cli/dispatch/mode\_local.go+134/-20\
\
```\
537 unmodified lines\
\
538\
539\
540\
541\
542\
543\
544\
545\
546\
547\
548\
549\
550\
551\
552\
553\
554\
555\
556\
557\
558\
559\
560\
561\
562\
563\
564\
565\
566\
567\
568\
569\
570\
571\
572\
573\
574\
575\
576\
577\
578\
579\
580\
581\
582\
583\
584\
585\
586\
587\
588\
589\
590\
591\
592\
593\
594\
595\
596\
597\
598\
599\
600\
601\
602\
603\
604\
605\
606\
607\
608\
609\
610\
611\
612\
613\
614\
615\
616\
617\
618\
619\
620\
621\
622\
623\
624\
625\
626\
627\
628\
629\
630\
631\
632\
633\
634\
635\
636\
637\
638\
639\
640\
641\
642\
643\
644\
645\
646\
647\
648\
649\
650\
651\
652\
653\
654\
655\
656\
657\
658\
659\
141 unmodified lines\
\
801\
802\
803\
688\
804\
805\
806\
807\
5 unmodified lines\
\
813\
814\
815\
700\
816\
817\
818\
819\
820\
12 unmodified lines\
\
833\
834\
835\
836\
837\
838\
839\
840\
841\
842\
843\
844\
845\
846\
847\
848\
849\
850\
851\
852\
853\
854\
855\
856\
857\
858\
859\
860\
861\
862\
863\
864\
865\
866\
867\
868\
869\
870\
871\
872\
873\
874\
875\
876\
877\
878\
879\
880\
881\
882\
883\
884\
885\
886\
887\
888\
889\
890\
891\
892\
893\
894\
895\
896\
897\
898\
899\
900\
901\
902\
903\
904\
905\
906\
907\
908\
909\
910\
911\
912\
\
537 unmodified lines\
\
    }\
}\
\
// TestLocalMode_ImplicitCurrentBranchOnDefaultBranchIncludesMergedWork is the\
// regression test for ENT-1188: on the default branch there is no parent\
// history to exclude, so work done on feature branches and merged into it\
// (summary.Branch is the feature branch, but the checkpoint trailer is\
// reachable from the default branch's HEAD) must appear in the dispatch.\
// Before the fix, branchLocalRevRange returned <default>..HEAD, which is empty\
// on an up-to-date default branch, so every such checkpoint was dropped and\
// the dispatch came back empty.\
func TestLocalMode_ImplicitCurrentBranchOnDefaultBranchIncludesMergedWork(t *testing.T) {\
    dir := t.TempDir()\
    stubGeneratedLocalDispatch(t)\
    testutil.InitRepo(t, dir)\
    addOriginRemote(t, dir)\
\
    // A single commit on the default branch carrying a checkpoint trailer,\
    // simulating merged feature-branch work now reachable from HEAD.\
    testutil.WriteFile(t, dir, "feature.md", "ship it")\
    testutil.GitAdd(t, dir, "feature.md")\
    commitWithMessage(t, dir, trailers.FormatCheckpoint("feature work", mustCheckpointID(t, testCheckpointID)))\
\
    createdAt := time.Now().UTC()\
    seedCommittedCheckpoint(t, dir, seededCheckpoint{\
        id:           testCheckpointID,\
        branch:       "my-feature",\
        createdAt:    createdAt,\
        filesTouched: []string{"feature.md"},\
        outcome:      testLocalFallbackText,\
    })\
\
    // Resolve the actual default branch name (go-git's PlainInit default) so\
    // the test does not hard-code master vs main.\
    repo, err := git.PlainOpenWithOptions(dir, &git.PlainOpenOptions{DetectDotGit: true})\
    if err != nil {\
        t.Fatal(err)\
    }\
    head, err := repo.Head()\
    if err != nil {\
        t.Fatal(err)\
    }\
    defaultBranch := head.Name().Short()\
\
    oldNow := nowUTC\
    nowUTC = func() time.Time { return createdAt.Add(time.Hour) }\
    t.Cleanup(func() { nowUTC = oldNow })\
\
    t.Chdir(dir)\
\
    got, err := Run(context.Background(), Options{\
        Mode:                  ModeLocal,\
        Since:                 "7d",\
        Branches:              []string{defaultBranch},\
        ImplicitCurrentBranch: true,\
    })\
    if err != nil {\
        t.Fatal(err)\
    }\
    if len(got.Repos) != 1 || len(got.Repos[0].Sections) == 0 || len(got.Repos[0].Sections[0].Bullets) == 0 ||\
        got.Repos[0].Sections[0].Bullets[0].Text != testLocalFallbackText {\
        t.Fatalf("merged feature-branch work missing from default-branch dispatch: %+v", got)\
    }\
}\
\
// TestLocalMode_IncludesReachableCheckpointMissingFromLocalStore is the other\
// half of the ENT-1188 fix: dispatch --local must summarize work reachable from\
// HEAD by commit trailer even when the checkpoint itself is absent from the\
// local checkout (the common case — checkpoints are pushed to the remote from\
// other worktrees and never fetched into this checkout). The commit subject is\
// always available from git log, so the bullet falls back to it without any\
// (slow) per-checkpoint network fetch. Before the fix, enumeration relied on\
// store.List, which only sees local checkpoints, so this work was invisible.\
func TestLocalMode_IncludesReachableCheckpointMissingFromLocalStore(t *testing.T) {\
    dir := t.TempDir()\
    stubGeneratedLocalDispatch(t)\
    testutil.InitRepo(t, dir)\
    addOriginRemote(t, dir)\
\
    // A commit on the default branch carrying a checkpoint trailer, but the\
    // checkpoint is intentionally NOT seeded into the local store.\
    const subject = "landed remote work"\
    testutil.WriteFile(t, dir, "feature.md", "ship it")\
    testutil.GitAdd(t, dir, "feature.md")\
    commitWithMessage(t, dir, trailers.FormatCheckpoint(subject, mustCheckpointID(t, testCheckpointID)))\
\
    repo, err := git.PlainOpenWithOptions(dir, &git.PlainOpenOptions{DetectDotGit: true})\
    if err != nil {\
        t.Fatal(err)\
    }\
    head, err := repo.Head()\
    if err != nil {\
        t.Fatal(err)\
    }\
    defaultBranch := head.Name().Short()\
\
    oldNow := nowUTC\
    nowUTC = func() time.Time { return time.Now().UTC().Add(time.Hour) }\
    t.Cleanup(func() { nowUTC = oldNow })\
\
    t.Chdir(dir)\
\
    got, err := Run(context.Background(), Options{\
        Mode:                  ModeLocal,\
        Since:                 "7d",\
        Branches:              []string{defaultBranch},\
        ImplicitCurrentBranch: true,\
    })\
    if err != nil {\
        t.Fatal(err)\
    }\
    if len(got.Repos) != 1 || len(got.Repos[0].Sections) == 0 || len(got.Repos[0].Sections[0].Bullets) == 0 {\
        t.Fatalf("reachable-but-unfetched checkpoint missing from dispatch: %+v", got)\
    }\
    if got.Repos[0].Sections[0].Bullets[0].Text != subject {\
        t.Fatalf("expected bullet from commit subject %q, got %q", subject, got.Repos[0].Sections[0].Bullets[0].Text)\
    }\
}\
\
func TestLocalMode_AllBranchesRestrictsToLocalBranches(t *testing.T) {\
    dir := t.TempDir()\
    stubGeneratedLocalDispatch(t)\
141 unmodified lines\
\
    script := "#!/bin/sh\n" +\
        "if [ \"$3\" = \"log\" ]; then\n" +\
        "  printf '%s\\n' \"$@\" > \"$TEST_GIT_ARGS_FILE\"\n" +\
        "  printf 'subject\\n\\nEntire-Checkpoint: " + testCheckpointID + "\\000'\n" +\
        "  printf '2026-04-02T10:00:00Z\\000subject\\n\\nEntire-Checkpoint: " + testCheckpointID + "\\000\\000'\n" +\
        "  exit 0\n" +\
        "fi\n" +\
        "exit 1\n"\
5 unmodified lines\
\
    t.Setenv("TEST_GIT_ARGS_FILE", argsFile)\
\
    since := time.Date(2026, 4, 1, 12, 30, 0, 0, time.UTC)\
    reachable, err := reachableCheckpointIDsInRange(context.Background(), "/tmp/repo", "origin/main..HEAD", since)\
    until := time.Date(2026, 5, 1, 12, 30, 0, 0, time.UTC)\
    reachable, err := reachableCheckpointIDsInRange(context.Background(), "/tmp/repo", "origin/main..HEAD", since, until)\
    if err != nil {\
        t.Fatal(err)\
    }\
12 unmodified lines\
\
    if !strings.Contains(args, "--since=2026-04-01T12:30:00Z") {\
        t.Fatalf("expected git log to bound history by since window, got args %q", args)\
    }\
    if !strings.Contains(args, "--until=2026-05-01T12:30:00Z") {\
        t.Fatalf("expected git log to bound history by until window, got args %q", args)\
    }\
    if !strings.Contains(args, "origin/main..HEAD") {\
        t.Fatalf("expected git log to use the supplied rev range, got args %q", args)\
    }\
}\
\
// TestReachableCheckpointIDsInRange_KeepsInWindowTimeDespiteLaterCommit is the\
// regression test for the bugbot finding: a checkpoint referenced by both an\
// in-window commit and a later out-of-window commit must record its in-window\
// time so the caller's [since, until) check does not drop it.\
func TestReachableCheckpointIDsInRange_KeepsInWindowTimeDespiteLaterCommit(t *testing.T) {\
    tmpDir := t.TempDir()\
    gitPath := filepath.Join(tmpDir, "git")\
\
    // git log emits newest-first: the out-of-window commit (after until) comes\
    // before the in-window commit, both referencing the same checkpoint. Only\
    // the in-window one should be recorded.\
    script := "#!/bin/sh\n" +\
        "if [ \"$3\" = \"log\" ]; then\n" +\
        "  printf '2026-06-15T10:00:00Z\\000later subject\\n\\nEntire-Checkpoint: " + testCheckpointID + "\\000\\000'\n" +\
        "  printf '2026-04-10T10:00:00Z\\000in-window subject\\n\\nEntire-Checkpoint: " + testCheckpointID + "\\000\\000'\n" +\
        "  exit 0\n" +\
        "fi\n" +\
        "exit 1\n"\
    if err := os.WriteFile(gitPath, []byte(script), 0o755); err != nil {\
        t.Fatal(err)\
    }\
    t.Setenv("PATH", tmpDir+string(os.PathListSeparator)+os.Getenv("PATH"))\
\
    since := time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)\
    until := time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC)\
    reachable, err := reachableCheckpointIDsInRange(context.Background(), "/tmp/repo", "HEAD", since, until)\
    if err != nil {\
        t.Fatal(err)\
    }\
    got, ok := reachable[testCheckpointID]\
    if !ok {\
        t.Fatalf("expected checkpoint %s to be reachable via its in-window commit, got %v", testCheckpointID, reachable)\
    }\
    want := time.Date(2026, 4, 10, 10, 0, 0, 0, time.UTC)\
    if !got.Equal(want) {\
        t.Fatalf("expected in-window commit time %s, got %s (later out-of-window commit leaked)", want, got)\
    }\
}\
\
// TestSortCandidatesByRecency covers the trail-review finding: because the\
// trailer fallback pass ranges over a map, candidates must be sorted before\
// returning so dispatch output is stable across runs. Newest first, ties broken\
// by checkpoint ID.\
func TestSortCandidatesByRecency(t *testing.T) {\
    t.Parallel()\
    base := time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)\
    candidates := []candidate{\
        {CheckpointID: "ccc", CreatedAt: base},\
        {CheckpointID: "aaa", CreatedAt: base.Add(2 * time.Hour)},\
        {CheckpointID: "bbb", CreatedAt: base}, // same time as ccc → tiebreak by ID\
        {CheckpointID: "zzz", CreatedAt: base.Add(time.Hour)},\
    }\
    sortCandidatesByRecency(candidates)\
\
    got := make([]string, len(candidates))\
    for i, c := range candidates {\
        got[i] = c.CheckpointID\
    }\
    want := []string{"aaa", "zzz", "bbb", "ccc"} // newest first; bbb < ccc for the tie\
    for i := range want {\
        if got[i] != want[i] {\
            t.Fatalf("sortCandidatesByRecency order = %v, want %v", got, want)\
        }\
    }\
}\
\
func TestLoadCommitSubjectsByCheckpoint_UsesSingleWindowedLogScan(t *testing.T) {\
    tmpDir := t.TempDir()\
    argsFile := filepath.Join(tmpDir, "git-args.txt")\
```\
\
Mcmd/entire/cli/dispatch/mode\_local\_test.go+188/-2\
\
```\
106 unmodified lines\
\
107\
108\
109\
110\
110\
111\
112\
113\
114\
115\
116\
115\
117\
118\
117\
119\
120\
121\
122\
121\
123\
124\
125\
126\
17 unmodified lines\
\
144\
145\
146\
145\
147\
148\
149\
150\
149\
151\
152\
153\
154\
51 unmodified lines\
\
206\
207\
208\
209\
210\
211\
212\
213\
214\
215\
216\
217\
218\
219\
220\
221\
222\
223\
224\
225\
226\
227\
228\
229\
230\
231\
232\
233\
234\
235\
236\
211\
237\
238\
239\
240\
241\
242\
217\
218\
219\
220\
243\
244\
245\
246\
247\
248\
249\
224\
250\
251\
252\
253\
254\
255\
230\
256\
257\
232\
258\
259\
234\
260\
261\
262\
263\
1 unmodified line\
\
265\
266\
267\
242\
268\
269\
270\
271\
15 unmodified lines\
\
287\
288\
289\
264\
290\
291\
266\
292\
293\
268\
294\
295\
270\
296\
297\
272\
298\
299\
300\
301\
302\
303\
278\
279\
280\
304\
305\
306\
307\
308\
283\
309\
310\
285\
311\
312\
287\
313\
314\
315\
316\
\
106 unmodified lines\
\
                return runGitClone(cmd.Context(), cmd, ref, targetDir)\
            }\
\
            provider, owner, repo, err := parseMirrorCloneRef(ref)\
            // provider is always "github" for the /gh/ shorthand; the pull-gated\
            // resolver pins the provider itself, so it's not threaded through.\
            _, owner, repo, err := parseMirrorCloneRef(ref)\
            if err != nil {\
                return fmt.Errorf("invalid <repo>: %w", err)\
            }\
\
            var mirrors []coreapi.Mirror\
            var placements []coreapi.ResolvedPlacement\
            lister := func(ctx context.Context, c *coreapi.Client) error {\
                ms, err := listMirrorsForRepo(ctx, c, provider, owner, repo)\
                ps, err := resolvePullablePlacements(ctx, c, owner, repo)\
                if err != nil {\
                    return err\
                }\
                mirrors = ms\
                placements = ps\
                return nil\
            }\
            // An explicit --cluster may name a cluster in a different federation\
17 unmodified lines\
\
                return err\
            }\
\
            if len(mirrors) == 0 {\
            if len(placements) == 0 {\
                return fmt.Errorf("no mirror found for /gh/%s/%s; run 'entire repo mirror create github.com/%s/%s' to onboard it", owner, repo, owner, repo)\
            }\
\
            chosen, err := selectCloneTarget(cmd, mirrors, cluster)\
            chosen, err := selectCloneTarget(cmd, placements, cluster)\
            if err != nil {\
                return err\
            }\
51 unmodified lines\
\
    return matched, nil\
}\
\
// resolvePullablePlacements returns every cluster placement of one GitHub\
// upstream the caller may pull (clone). It backs `repo clone /gh/<owner>/<repo>`\
// and deliberately differs from listMirrorsForRepo: that reads the\
// affiliation-scoped mirror list (repo#list), which omits public mirrors the\
// caller holds no grant on, so the shorthand used to fail on a public repo that\
// clones fine by full entire:// URL. This hits the pull-gated /mirrors/placements\
// endpoint instead — the same authority the clone's STS exchange enforces — so\
// anything clonable resolves, public or private-with-grant.\
//\
// owner/repo arrive already lowercased from parseMirrorCloneRef; the server\
// matches case-insensitively regardless. An empty result means not mirrored or\
// not pullable, and the caller surfaces that.\
func resolvePullablePlacements(ctx context.Context, c *coreapi.Client, owner, repo string) ([]coreapi.ResolvedPlacement, error) {\
    out, err := c.ResolveMirrorPlacements(ctx, coreapi.ResolveMirrorPlacementsParams{\
        Provider: coreapi.ResolveMirrorPlacementsProviderGithub,\
        Owner:    owner,\
        Repo:     repo,\
    })\
    if err != nil {\
        return nil, fmt.Errorf("resolve mirror placements: %w", err)\
    }\
    return out.Placements, nil\
}\
\
// selectCloneTarget resolves which mirror placement to clone from. With one\
// placement it returns it directly. With --cluster it picks the matching one (or\
// errors listing the available hosts). With more than one and no flag it prompts\
// interactively, failing fast with a --cluster pointer when there's no terminal.\
func selectCloneTarget(cmd *cobra.Command, mirrors []coreapi.Mirror, clusterFlag string) (coreapi.Mirror, error) {\
func selectCloneTarget(cmd *cobra.Command, placements []coreapi.ResolvedPlacement, clusterFlag string) (coreapi.ResolvedPlacement, error) {\
    // Dedupe by cluster host: one placement per cluster is what a clone targets,\
    // and the same host appearing twice would only confuse the picker. Key on the\
    // case-folded host — DNS is case-insensitive, so a --cluster value differing\
    // only in case from the API's ClusterHost must still match (the alternative is\
    // a misleading "not mirrored on ..." after a successful lookup + dial).\
    byHost := make(map[string]coreapi.Mirror, len(mirrors))\
    hosts := make([]string, 0, len(mirrors))\
    for _, m := range mirrors {\
        key := strings.ToLower(m.ClusterHost)\
    byHost := make(map[string]coreapi.ResolvedPlacement, len(placements))\
    hosts := make([]string, 0, len(placements))\
    for _, p := range placements {\
        key := strings.ToLower(p.ClusterHost)\
        if _, seen := byHost[key]; seen {\
            continue\
        }\
        byHost[key] = m\
        byHost[key] = p\
        hosts = append(hosts, key)\
    }\
    sort.Strings(hosts)\
\
    if clusterFlag != "" {\
        m, ok := byHost[strings.ToLower(strings.TrimSpace(clusterFlag))]\
        p, ok := byHost[strings.ToLower(strings.TrimSpace(clusterFlag))]\
        if !ok {\
            return coreapi.Mirror{}, fmt.Errorf("repo is not mirrored on %q; available: %s", clusterFlag, strings.Join(hosts, ", "))\
            return coreapi.ResolvedPlacement{}, fmt.Errorf("repo is not mirrored on %q; available: %s", clusterFlag, strings.Join(hosts, ", "))\
        }\
        return m, nil\
        return p, nil\
    }\
\
    if len(hosts) == 1 {\
1 unmodified line\
\
    }\
\
    if !interactive.CanPromptInteractively() {\
        return coreapi.Mirror{}, fmt.Errorf("repo is mirrored on %d clusters; pass --cluster to choose one of: %s", len(hosts), strings.Join(hosts, ", "))\
        return coreapi.ResolvedPlacement{}, fmt.Errorf("repo is mirrored on %d clusters; pass --cluster to choose one of: %s", len(hosts), strings.Join(hosts, ", "))\
    }\
\
    options := make([]huh.Option[string], len(hosts))\
15 unmodified lines\
\
        // caller stops instead of falling through to clone a zero-value target\
        // (the `entire:///gh/...` empty-host bug); a real form error propagates.\
        if cerr := handleFormCancellation(cmd.ErrOrStderr(), "Clone", err); cerr != nil {\
            return coreapi.Mirror{}, cerr\
            return coreapi.ResolvedPlacement{}, cerr\
        }\
        return coreapi.Mirror{}, NewSilentError(errors.New("clone cancelled"))\
        return coreapi.ResolvedPlacement{}, NewSilentError(errors.New("clone cancelled"))\
    }\
    m, ok := byHost[selected]\
    p, ok := byHost[selected]\
    if !ok {\
        return coreapi.Mirror{}, NewSilentError(errors.New("clone cancelled"))\
        return coreapi.ResolvedPlacement{}, NewSilentError(errors.New("clone cancelled"))\
    }\
    return m, nil\
    return p, nil\
}\
\
// mirrorCellLabel is the human label for a mirror placement in the clone picker:\
// the physical cell and jurisdiction when known, always anchored by the cluster\
// host that goes into the clone URL.\
func mirrorCellLabel(m coreapi.Mirror) string {\
    cell := strings.TrimSpace(m.Cell.Or(""))\
    jur := strings.TrimSpace(m.Jurisdiction.Or(""))\
func mirrorCellLabel(p coreapi.ResolvedPlacement) string {\
    cell := strings.TrimSpace(p.Cell.Or(""))\
    jur := strings.TrimSpace(p.Jurisdiction.Or(""))\
    switch {\
    case cell != "" && jur != "":\
        return fmt.Sprintf("%s (%s) — %s", cell, jur, m.ClusterHost)\
        return fmt.Sprintf("%s (%s) — %s", cell, jur, p.ClusterHost)\
    case cell != "":\
        return fmt.Sprintf("%s — %s", cell, m.ClusterHost)\
        return fmt.Sprintf("%s — %s", cell, p.ClusterHost)\
    default:\
        return m.ClusterHost\
        return p.ClusterHost\
    }\
}\
```\
\
Mcmd/entire/cli/repo\_clone.go+53/-27\
\
```\
76 unmodified lines\
\
77\
78\
79\
80\
80\
81\
82\
83\
84\
85\
85\
86\
87\
88\
89\
90\
90\
91\
92\
93\
2 unmodified lines\
\
96\
97\
98\
99\
99\
100\
101\
102\
36 unmodified lines\
\
139\
140\
141\
142\
143\
142\
143\
144\
145\
146\
147\
147\
148\
149\
150\
151\
152\
153\
154\
154\
155\
156\
157\
158\
159\
160\
161\
161\
162\
163\
164\
2 unmodified lines\
\
167\
168\
169\
170\
170\
171\
172\
173\
174\
175\
176\
177\
177\
178\
179\
180\
2 unmodified lines\
\
183\
184\
185\
186\
186\
187\
188\
189\
190\
191\
192\
193\
194\
195\
196\
197\
198\
199\
200\
201\
202\
203\
204\
205\
206\
207\
208\
209\
210\
211\
212\
213\
214\
215\
216\
217\
218\
219\
220\
221\
222\
223\
224\
225\
226\
227\
228\
229\
230\
231\
232\
233\
234\
235\
236\
\
76 unmodified lines\
\
    t.Parallel()\
    tests := []struct {\
        name   string\
        mirror coreapi.Mirror\
        mirror coreapi.ResolvedPlacement\
        want   string\
    }{\
        {\
            name:   "host only",\
            mirror: coreapi.Mirror{ClusterHost: "aws-us-east-2.entire.io"},\
            mirror: coreapi.ResolvedPlacement{ClusterHost: "aws-us-east-2.entire.io"},\
            want:   "aws-us-east-2.entire.io",\
        },\
        {\
            name: "cell and jurisdiction",\
            mirror: coreapi.Mirror{\
            mirror: coreapi.ResolvedPlacement{\
                ClusterHost:  "aws-us-east-2.entire.io",\
                Cell:         coreapi.NewOptString("aws-us-east-2"),\
                Jurisdiction: coreapi.NewOptString("us"),\
2 unmodified lines\
\
        },\
        {\
            name: "cell without jurisdiction",\
            mirror: coreapi.Mirror{\
            mirror: coreapi.ResolvedPlacement{\
                ClusterHost: "aws-us-east-2.entire.io",\
                Cell:        coreapi.NewOptString("aws-us-east-2"),\
            },\
36 unmodified lines\
\
func TestSelectCloneTarget(t *testing.T) {\
    t.Parallel()\
\
    usEast := coreapi.Mirror{Repo: "entire-api", ClusterHost: "aws-us-east-2.entire.io"}\
    euWest := coreapi.Mirror{Repo: "entire-api", ClusterHost: "aws-eu-west-1.entire.io"}\
    usEast := coreapi.ResolvedPlacement{ClusterHost: "aws-us-east-2.entire.io"}\
    euWest := coreapi.ResolvedPlacement{ClusterHost: "aws-eu-west-1.entire.io"}\
\
    t.Run("single placement returns directly", func(t *testing.T) {\
        t.Parallel()\
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast}, "")\
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.ResolvedPlacement{usEast}, "")\
        require.NoError(t, err)\
        require.Equal(t, "aws-us-east-2.entire.io", got.ClusterHost)\
    })\
\
    t.Run("dedupes repeated host to a single placement", func(t *testing.T) {\
        t.Parallel()\
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, usEast}, "")\
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.ResolvedPlacement{usEast, usEast}, "")\
        require.NoError(t, err)\
        require.Equal(t, "aws-us-east-2.entire.io", got.ClusterHost)\
    })\
\
    t.Run("--cluster picks the matching placement", func(t *testing.T) {\
        t.Parallel()\
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, euWest}, "aws-eu-west-1.entire.io")\
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.ResolvedPlacement{usEast, euWest}, "aws-eu-west-1.entire.io")\
        require.NoError(t, err)\
        require.Equal(t, "aws-eu-west-1.entire.io", got.ClusterHost)\
    })\
2 unmodified lines\
\
        t.Parallel()\
        // DNS hosts are case-insensitive: a mixed-case --cluster must still match\
        // the API's lowercase ClusterHost rather than falsely "not mirrored".\
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, euWest}, "AWS-EU-West-1.Entire.IO")\
        got, err := selectCloneTarget(newCloneTestCmd(), []coreapi.ResolvedPlacement{usEast, euWest}, "AWS-EU-West-1.Entire.IO")\
        require.NoError(t, err)\
        require.Equal(t, "aws-eu-west-1.entire.io", got.ClusterHost)\
    })\
\
    t.Run("--cluster with no match errors and lists hosts", func(t *testing.T) {\
        t.Parallel()\
        _, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, euWest}, "aws-ap-south-1.entire.io")\
        _, err := selectCloneTarget(newCloneTestCmd(), []coreapi.ResolvedPlacement{usEast, euWest}, "aws-ap-south-1.entire.io")\
        require.Error(t, err)\
        require.Contains(t, err.Error(), "aws-us-east-2.entire.io")\
        require.Contains(t, err.Error(), "aws-eu-west-1.entire.io")\
2 unmodified lines\
\
    t.Run("multiple placements with no terminal errors with a --cluster pointer", func(t *testing.T) {\
        t.Parallel()\
        // go test is non-interactive, so the picker path is unreachable here.\
        _, err := selectCloneTarget(newCloneTestCmd(), []coreapi.Mirror{usEast, euWest}, "")\
        _, err := selectCloneTarget(newCloneTestCmd(), []coreapi.ResolvedPlacement{usEast, euWest}, "")\
        require.Error(t, err)\
        require.Contains(t, err.Error(), "--cluster")\
    })\
}\
\
// TestResolvePullablePlacements_ReturnsPlacements verifies the clone-discovery\
// resolver hits the pull-gated /mirrors/placements endpoint with the upstream\
// coords and returns every placement (host + cell + jurisdiction) for the\
// picker. A public mirror the caller holds no grant on resolves here even\
// though it never would via the affiliation-scoped list — the whole point of\
// the endpoint.\
func TestResolvePullablePlacements_ReturnsPlacements(t *testing.T) {\
    t.Parallel()\
    var gotPath, gotQuery string\
    srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\
        gotPath = r.URL.Path\
        gotQuery = r.URL.RawQuery\
        w.Header().Set("Content-Type", "application/json")\
        body := &coreapi.ResolvePlacementsOutputBody{Placements: []coreapi.ResolvedPlacement{\
            {MirrorId: "01AAA", ClusterHost: "aws-us-east-2.entire.io", Cell: coreapi.NewOptString("aws-us-east-2"), Jurisdiction: coreapi.NewOptString("us")},\
            {MirrorId: "01BBB", ClusterHost: "aws-eu-west-1.entire.io", Cell: coreapi.NewOptString("aws-eu-west-1"), Jurisdiction: coreapi.NewOptString("eu")},\
        }}\
        if err := printJSON(w, body); err != nil {\
            t.Errorf("encode response: %v", err)\
        }\
    }))\
    t.Cleanup(srv.Close)\
\
    c, err := coreapi.NewWithBearer(srv.URL, "tok")\
    require.NoError(t, err)\
\
    got, err := resolvePullablePlacements(t.Context(), c, "karthik-rameshkumar", "my-entire")\
    require.NoError(t, err)\
\
    require.Equal(t, "/api/v1/mirrors/placements", gotPath)\
    require.Contains(t, gotQuery, "provider=github")\
    require.Contains(t, gotQuery, "owner=karthik-rameshkumar")\
    require.Contains(t, gotQuery, "repo=my-entire")\
\
    require.Len(t, got, 2)\
    require.Equal(t, "aws-us-east-2.entire.io", got[0].ClusterHost)\
    require.Equal(t, "aws-us-east-2", got[0].Cell.Or(""))\
    require.Equal(t, "us", got[0].Jurisdiction.Or(""))\
    require.Equal(t, "01AAA", got[0].MirrorId)\
    require.Equal(t, "aws-eu-west-1.entire.io", got[1].ClusterHost)\
}\
\
// TestListMirrorsForRepo_FiltersByRepo verifies the client-side repo filter:\
// the list API filters provider+owner server-side, but the repo match (which\
// the API has no param for) is applied locally.\
```\
\
Mcmd/entire/cli/repo\_clone\_test.go+54/-12\
\
```\
362 unmodified lines\
\
363\
364\
365\
366\
367\
368\
369\
370\
371\
372\
373\
374\
6352 unmodified lines\
\
6727\
6728\
6729\
6730\
6731\
6732\
6733\
6734\
6735\
6736\
6737\
6738\
6739\
6740\
6741\
6742\
6743\
6744\
6745\
6746\
6747\
6748\
6749\
6750\
6751\
6752\
6753\
6754\
6755\
6756\
6757\
6758\
6759\
6760\
6761\
6762\
6763\
6764\
6765\
6766\
6767\
6768\
6769\
6770\
6771\
6772\
6773\
6774\
6775\
6776\
6777\
6778\
6779\
6780\
6781\
6782\
6783\
6784\
6785\
6786\
6787\
6788\
6789\
6790\
6791\
6792\
6793\
6794\
6795\
6796\
6797\
6798\
6799\
6800\
6801\
6802\
6803\
6804\
6805\
6806\
6807\
6808\
6809\
6810\
6811\
6812\
6813\
6814\
6815\
6816\
6817\
6818\
6819\
6820\
6821\
6822\
6823\
6824\
6825\
6826\
6827\
6828\
6829\
6830\
6831\
6832\
6833\
6834\
6835\
6836\
6837\
6838\
6839\
6840\
6841\
6842\
6843\
6844\
6845\
6846\
6847\
6848\
6849\
6850\
6851\
6852\
6853\
6854\
6855\
6856\
6857\
6858\
6859\
\
362 unmodified lines\
\
    //\
    // GET /identity/handles/{provider}/{handle}\
    ResolveHandle(ctx context.Context, params ResolveHandleParams) (*ResolvedIdentity, error)\
    // ResolveMirrorPlacements invokes resolveMirrorPlacements operation.\
    //\
    // Resolve the pullable cluster placements of a mirrored upstream (clone discovery).\
    //\
    // GET /mirrors/placements\
    ResolveMirrorPlacements(ctx context.Context, params ResolveMirrorPlacementsParams) (*ResolvePlacementsOutputBody, error)\
    // RevokeProjectAccess invokes revokeProjectAccess operation.\
    //\
    // Revoke project access by grantee id.\
6352 unmodified lines\
\
    return result, nil\
}\
\
// ResolveMirrorPlacements invokes resolveMirrorPlacements operation.\
//\
// Resolve the pullable cluster placements of a mirrored upstream (clone discovery).\
//\
// GET /mirrors/placements\
func (c *Client) ResolveMirrorPlacements(ctx context.Context, params ResolveMirrorPlacementsParams) (*ResolvePlacementsOutputBody, error) {\
    res, err := c.sendResolveMirrorPlacements(ctx, params)\
    return res, err\
}\
\
func (c *Client) sendResolveMirrorPlacements(ctx context.Context, params ResolveMirrorPlacementsParams) (res *ResolvePlacementsOutputBody, err error) {\
\
    u := uri.Clone(c.requestURL(ctx))\
    var pathParts [1]string\
    pathParts[0] = "/mirrors/placements"\
    uri.AddPathParts(u, pathParts[:]...)\
\
    q := uri.NewQueryEncoder()\
    {\
        // Encode "provider" parameter.\
        cfg := uri.QueryParameterEncodingConfig{\
            Name:    "provider",\
            Style:   uri.QueryStyleForm,\
            Explode: false,\
        }\
\
        if err := q.EncodeParam(cfg, func(e uri.Encoder) error {\
            return e.EncodeValue(conv.StringToString(string(params.Provider)))\
        }); err != nil {\
            return res, errors.Wrap(err, "encode query")\
        }\
    }\
    {\
        // Encode "owner" parameter.\
        cfg := uri.QueryParameterEncodingConfig{\
            Name:    "owner",\
            Style:   uri.QueryStyleForm,\
            Explode: false,\
        }\
\
        if err := q.EncodeParam(cfg, func(e uri.Encoder) error {\
            return e.EncodeValue(conv.StringToString(params.Owner))\
        }); err != nil {\
            return res, errors.Wrap(err, "encode query")\
        }\
    }\
    {\
        // Encode "repo" parameter.\
        cfg := uri.QueryParameterEncodingConfig{\
            Name:    "repo",\
            Style:   uri.QueryStyleForm,\
            Explode: false,\
        }\
\
        if err := q.EncodeParam(cfg, func(e uri.Encoder) error {\
            return e.EncodeValue(conv.StringToString(params.Repo))\
        }); err != nil {\
            return res, errors.Wrap(err, "encode query")\
        }\
    }\
    u.RawQuery = q.Values().Encode()\
\
    r, err := ht.NewRequest(ctx, "GET", u)\
    if err != nil {\
        return res, errors.Wrap(err, "create request")\
    }\
\
    {\
        type bitset = [1]uint8\
        var satisfied bitset\
        {\
\
            switch err := c.securityBearerAuth(ctx, ResolveMirrorPlacementsOperation, r); {\
            case err == nil: // if NO error\
                satisfied[0] |= 1 << 0\
            case errors.Is(err, ogenerrors.ErrSkipClientSecurity):\
                // Skip this security.\
            default:\
                return res, errors.Wrap(err, "security \"BearerAuth\"")\
            }\
        }\
        {\
\
            switch err := c.securitySessionAuth(ctx, ResolveMirrorPlacementsOperation, r); {\
            case err == nil: // if NO error\
                satisfied[0] |= 1 << 1\
            case errors.Is(err, ogenerrors.ErrSkipClientSecurity):\
                // Skip this security.\
            default:\
                return res, errors.Wrap(err, "security \"SessionAuth\"")\
            }\
        }\
\
        if ok := func() bool {\
        nextRequirement:\
            for _, requirement := range []bitset{\
                {0b00000001},\
                {0b00000010},\
            } {\
                for i, mask := range requirement {\
                    if satisfied[i]&mask != mask {\
                        continue nextRequirement\
                    }\
                }\
                return true\
            }\
            return false\
        }(); !ok {\
            return res, ogenerrors.ErrSecurityRequirementIsNotSatisfied\
        }\
    }\
\
    resp, err := c.cfg.Client.Do(r)\
    if err != nil {\
        return res, errors.Wrap(err, "do request")\
    }\
    body := resp.Body\
    defer body.Close()\
\
    result, err := decodeResolveMirrorPlacementsResponse(resp)\
    if err != nil {\
        return res, errors.Wrap(err, "decode response")\
    }\
\
    return result, nil\
}\
\
// RevokeProjectAccess invokes revokeProjectAccess operation.\
//\
// Revoke project access by grantee id.\
```\
\
Minternal/coreapi/oas\_client\_gen.go+133\
\
```\
20014 unmodified lines\
\
20015\
20016\
20017\
20018\
20019\
20020\
20021\
20022\
20023\
20024\
20025\
20026\
20027\
20028\
20029\
20030\
20031\
20032\
20033\
20034\
20035\
20036\
20037\
20038\
20039\
20040\
20041\
20042\
20043\
20044\
20045\
20046\
20047\
20048\
20049\
20050\
20051\
20052\
20053\
20054\
20055\
20056\
20057\
20058\
20059\
20060\
20061\
20062\
20063\
20064\
20065\
20066\
20067\
20068\
20069\
20070\
20071\
20072\
20073\
20074\
20075\
20076\
20077\
20078\
20079\
20080\
20081\
20082\
20083\
20084\
20085\
20086\
20087\
20088\
20089\
20090\
20091\
20092\
20093\
20094\
20095\
20096\
20097\
20098\
20099\
20100\
20101\
20102\
20103\
20104\
20105\
20106\
20107\
20108\
20109\
20110\
20111\
20112\
20113\
20114\
20115\
20116\
20117\
20118\
20119\
20120\
20121\
20122\
20123\
20124\
20125\
20126\
20127\
20128\
20129\
20130\
20131\
20132\
20133\
20134\
20135\
20136\
20137\
20138\
20139\
20140\
20141\
20142\
20143\
20144\
20145\
20146\
20147\
20148\
20149\
20150\
20151\
20152\
20153\
20154\
20155\
20156\
20157\
20158\
20159\
20160\
20161\
20162\
20163\
20164\
20165\
20166\
20167\
20168\
20169\
20170\
20171\
20172\
20173\
20174\
20175\
20176\
20177\
20178\
20179\
20180\
20181\
20182\
20183\
20184\
20185\
20186\
20187\
20188\
20189\
20190\
20191\
20192\
20193\
20194\
20195\
20196\
20197\
20198\
20199\
20200\
20201\
20202\
20203\
20204\
20205\
20206\
20207\
20208\
20209\
20210\
20211\
20212\
20213\
20214\
20215\
20216\
20217\
20218\
20219\
20220\
235 unmodified lines\
\
20456\
20457\
20458\
20459\
20460\
20461\
20462\
20463\
20464\
20465\
20466\
20467\
20468\
20469\
20470\
20471\
20472\
20473\
20474\
20475\
20476\
20477\
20478\
20479\
20480\
20481\
20482\
20483\
20484\
20485\
20486\
20487\
20488\
20489\
20490\
20491\
20492\
20493\
20494\
20495\
20496\
20497\
20498\
20499\
20500\
20501\
20502\
20503\
20504\
20505\
20506\
20507\
20508\
20509\
20510\
20511\
20512\
20513\
20514\
20515\
20516\
20517\
20518\
20519\
20520\
20521\
20522\
20523\
20524\
20525\
20526\
20527\
20528\
20529\
20530\
20531\
20532\
20533\
20534\
20535\
20536\
20537\
20538\
20539\
20540\
20541\
20542\
20543\
20544\
20545\
20546\
20547\
20548\
20549\
20550\
20551\
20552\
20553\
20554\
20555\
20556\
20557\
20558\
20559\
20560\
20561\
20562\
20563\
20564\
20565\
20566\
20567\
20568\
20569\
20570\
20571\
20572\
20573\
20574\
20575\
20576\
20577\
20578\
20579\
20580\
20581\
20582\
20583\
20584\
20585\
20586\
20587\
20588\
20589\
20590\
20591\
20592\
20593\
20594\
20595\
20596\
20597\
20598\
20599\
20600\
20601\
20602\
20603\
20604\
20605\
20606\
20607\
20608\
20609\
20610\
20611\
20612\
20613\
20614\
20615\
20616\
20617\
20618\
20619\
20620\
20621\
20622\
20623\
20624\
20625\
20626\
20627\
20628\
20629\
20630\
20631\
20632\
20633\
20634\
20635\
20636\
20637\
20638\
20639\
20640\
20641\
20642\
20643\
20644\
20645\
20646\
20647\
20648\
20649\
20650\
20651\
20652\
20653\
20654\
20655\
20656\
20657\
20658\
20659\
20660\
20661\
20662\
20663\
20664\
20665\
20666\
20667\
20668\
20669\
20670\
20671\
20672\
20673\
20674\
20675\
20676\
20677\
20678\
20679\
20680\
20681\
20682\
20683\
20684\
20685\
\
20014 unmodified lines\
\
    return s.Decode(d)\
}\
\
// Encode implements json.Marshaler.\
func (s *ResolvePlacementsOutputBody) Encode(e *jx.Encoder) {\
    e.ObjStart()\
    s.encodeFields(e)\
    e.ObjEnd()\
}\
\
// encodeFields encodes fields.\
func (s *ResolvePlacementsOutputBody) encodeFields(e *jx.Encoder) {\
    {\
        if s.Schema.Set {\
            e.FieldStart("$schema")\
            s.Schema.Encode(e)\
        }\
    }\
    {\
        e.FieldStart("placements")\
        e.ArrStart()\
        for _, elem := range s.Placements {\
            elem.Encode(e)\
        }\
        e.ArrEnd()\
    }\
    for k, elem := range s.AdditionalProps {\
        e.FieldStart(k)\
\
        if len(elem) != 0 {\
            e.Raw(elem)\
        }\
    }\
}\
\
var jsonFieldsNameOfResolvePlacementsOutputBody = [2]string{\
    0: "$schema",\
    1: "placements",\
}\
\
// Decode decodes ResolvePlacementsOutputBody from json.\
func (s *ResolvePlacementsOutputBody) Decode(d *jx.Decoder) error {\
    if s == nil {\
        return errors.New("invalid: unable to decode ResolvePlacementsOutputBody to nil")\
    }\
    var requiredBitSet [1]uint8\
    s.AdditionalProps = map[string]jx.Raw{}\
\
    if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {\
        switch string(k) {\
        case "$schema":\
            if err := func() error {\
                s.Schema.Reset()\
                if err := s.Schema.Decode(d); err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"$schema\"")\
            }\
        case "placements":\
            requiredBitSet[0] |= 1 << 1\
            if err := func() error {\
                s.Placements = make([]ResolvedPlacement, 0)\
                if err := d.Arr(func(d *jx.Decoder) error {\
                    var elem ResolvedPlacement\
                    if err := elem.Decode(d); err != nil {\
                        return err\
                    }\
                    s.Placements = append(s.Placements, elem)\
                    return nil\
                }); err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"placements\"")\
            }\
        default:\
            var elem jx.Raw\
            if err := func() error {\
                v, err := d.RawAppend(nil)\
                elem = jx.Raw(v)\
                if err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrapf(err, "decode field %q", k)\
            }\
            s.AdditionalProps[string(k)] = elem\
        }\
        return nil\
    }); err != nil {\
        return errors.Wrap(err, "decode ResolvePlacementsOutputBody")\
    }\
    // Validate required fields.\
    var failures []validate.FieldError\
    for i, mask := range [1]uint8{\
        0b00000010,\
    } {\
        if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {\
            // Mask only required fields and check equality to mask using XOR.\
            //\
            // If XOR result is not zero, result is not equal to expected, so some fields are missed.\
            // Bits of fields which would be set are actually bits of missed fields.\
            missed := bits.OnesCount8(result)\
            for bitN := 0; bitN < missed; bitN++ {\
                bitIdx := bits.TrailingZeros8(result)\
                fieldIdx := i*8 + bitIdx\
                var name string\
                if fieldIdx < len(jsonFieldsNameOfResolvePlacementsOutputBody) {\
                    name = jsonFieldsNameOfResolvePlacementsOutputBody[fieldIdx]\
                } else {\
                    name = strconv.Itoa(fieldIdx)\
                }\
                failures = append(failures, validate.FieldError{\
                    Name:  name,\
                    Error: validate.ErrFieldRequired,\
                })\
                // Reset bit.\
                result &^= 1 << bitIdx\
            }\
        }\
    }\
    if len(failures) > 0 {\
        return &validate.Error{Fields: failures}\
    }\
\
    return nil\
}\
\
// MarshalJSON implements stdjson.Marshaler.\
func (s *ResolvePlacementsOutputBody) MarshalJSON() ([]byte, error) {\
    e := jx.Encoder{}\
    s.Encode(&e)\
    return e.Bytes(), nil\
}\
\
// UnmarshalJSON implements stdjson.Unmarshaler.\
func (s *ResolvePlacementsOutputBody) UnmarshalJSON(data []byte) error {\
    d := jx.DecodeBytes(data)\
    return s.Decode(d)\
}\
\
// Encode implements json.Marshaler.\
func (s ResolvePlacementsOutputBodyAdditional) Encode(e *jx.Encoder) {\
    e.ObjStart()\
    s.encodeFields(e)\
    e.ObjEnd()\
}\
\
// encodeFields implements json.Marshaler.\
func (s ResolvePlacementsOutputBodyAdditional) encodeFields(e *jx.Encoder) {\
    for k, elem := range s {\
        e.FieldStart(k)\
\
        if len(elem) != 0 {\
            e.Raw(elem)\
        }\
    }\
}\
\
// Decode decodes ResolvePlacementsOutputBodyAdditional from json.\
func (s *ResolvePlacementsOutputBodyAdditional) Decode(d *jx.Decoder) error {\
    if s == nil {\
        return errors.New("invalid: unable to decode ResolvePlacementsOutputBodyAdditional to nil")\
    }\
    m := s.init()\
    if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {\
        var elem jx.Raw\
        if err := func() error {\
            v, err := d.RawAppend(nil)\
            elem = jx.Raw(v)\
            if err != nil {\
                return err\
            }\
            return nil\
        }(); err != nil {\
            return errors.Wrapf(err, "decode field %q", k)\
        }\
        m[string(k)] = elem\
        return nil\
    }); err != nil {\
        return errors.Wrap(err, "decode ResolvePlacementsOutputBodyAdditional")\
    }\
\
    return nil\
}\
\
// MarshalJSON implements stdjson.Marshaler.\
func (s ResolvePlacementsOutputBodyAdditional) MarshalJSON() ([]byte, error) {\
    e := jx.Encoder{}\
    s.Encode(&e)\
    return e.Bytes(), nil\
}\
\
// UnmarshalJSON implements stdjson.Unmarshaler.\
func (s *ResolvePlacementsOutputBodyAdditional) UnmarshalJSON(data []byte) error {\
    d := jx.DecodeBytes(data)\
    return s.Decode(d)\
}\
\
// Encode implements json.Marshaler.\
func (s *ResolvedIdentity) Encode(e *jx.Encoder) {\
    e.ObjStart()\
235 unmodified lines\
\
    return s.Decode(d)\
}\
\
// Encode implements json.Marshaler.\
func (s *ResolvedPlacement) Encode(e *jx.Encoder) {\
    e.ObjStart()\
    s.encodeFields(e)\
    e.ObjEnd()\
}\
\
// encodeFields encodes fields.\
func (s *ResolvedPlacement) encodeFields(e *jx.Encoder) {\
    {\
        if s.Cell.Set {\
            e.FieldStart("cell")\
            s.Cell.Encode(e)\
        }\
    }\
    {\
        e.FieldStart("clusterHost")\
        e.Str(s.ClusterHost)\
    }\
    {\
        if s.Jurisdiction.Set {\
            e.FieldStart("jurisdiction")\
            s.Jurisdiction.Encode(e)\
        }\
    }\
    {\
        e.FieldStart("mirrorId")\
        e.Str(s.MirrorId)\
    }\
    for k, elem := range s.AdditionalProps {\
        e.FieldStart(k)\
\
        if len(elem) != 0 {\
            e.Raw(elem)\
        }\
    }\
}\
\
var jsonFieldsNameOfResolvedPlacement = [4]string{\
    0: "cell",\
    1: "clusterHost",\
    2: "jurisdiction",\
    3: "mirrorId",\
}\
\
// Decode decodes ResolvedPlacement from json.\
func (s *ResolvedPlacement) Decode(d *jx.Decoder) error {\
    if s == nil {\
        return errors.New("invalid: unable to decode ResolvedPlacement to nil")\
    }\
    var requiredBitSet [1]uint8\
    s.AdditionalProps = map[string]jx.Raw{}\
\
    if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {\
        switch string(k) {\
        case "cell":\
            if err := func() error {\
                s.Cell.Reset()\
                if err := s.Cell.Decode(d); err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"cell\"")\
            }\
        case "clusterHost":\
            requiredBitSet[0] |= 1 << 1\
            if err := func() error {\
                v, err := d.Str()\
                s.ClusterHost = string(v)\
                if err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"clusterHost\"")\
            }\
        case "jurisdiction":\
            if err := func() error {\
                s.Jurisdiction.Reset()\
                if err := s.Jurisdiction.Decode(d); err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"jurisdiction\"")\
            }\
        case "mirrorId":\
            requiredBitSet[0] |= 1 << 3\
            if err := func() error {\
                v, err := d.Str()\
                s.MirrorId = string(v)\
                if err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"mirrorId\"")\
            }\
        default:\
            var elem jx.Raw\
            if err := func() error {\
                v, err := d.RawAppend(nil)\
                elem = jx.Raw(v)\
                if err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrapf(err, "decode field %q", k)\
            }\
            s.AdditionalProps[string(k)] = elem\
        }\
        return nil\
    }); err != nil {\
        return errors.Wrap(err, "decode ResolvedPlacement")\
    }\
    // Validate required fields.\
    var failures []validate.FieldError\
    for i, mask := range [1]uint8{\
        0b00001010,\
    } {\
        if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {\
            // Mask only required fields and check equality to mask using XOR.\
            //\
            // If XOR result is not zero, result is not equal to expected, so some fields are missed.\
            // Bits of fields which would be set are actually bits of missed fields.\
            missed := bits.OnesCount8(result)\
            for bitN := 0; bitN < missed; bitN++ {\
                bitIdx := bits.TrailingZeros8(result)\
                fieldIdx := i*8 + bitIdx\
                var name string\
                if fieldIdx < len(jsonFieldsNameOfResolvedPlacement) {\
                    name = jsonFieldsNameOfResolvedPlacement[fieldIdx]\
                } else {\
                    name = strconv.Itoa(fieldIdx)\
                }\
                failures = append(failures, validate.FieldError{\
                    Name:  name,\
                    Error: validate.ErrFieldRequired,\
                })\
                // Reset bit.\
                result &^= 1 << bitIdx\
            }\
        }\
    }\
    if len(failures) > 0 {\
        return &validate.Error{Fields: failures}\
    }\
\
    return nil\
}\
\
// MarshalJSON implements stdjson.Marshaler.\
func (s *ResolvedPlacement) MarshalJSON() ([]byte, error) {\
    e := jx.Encoder{}\
    s.Encode(&e)\
    return e.Bytes(), nil\
}\
\
// UnmarshalJSON implements stdjson.Unmarshaler.\
func (s *ResolvedPlacement) UnmarshalJSON(data []byte) error {\
    d := jx.DecodeBytes(data)\
    return s.Decode(d)\
}\
\
// Encode implements json.Marshaler.\
func (s ResolvedPlacementAdditional) Encode(e *jx.Encoder) {\
    e.ObjStart()\
    s.encodeFields(e)\
    e.ObjEnd()\
}\
\
// encodeFields implements json.Marshaler.\
func (s ResolvedPlacementAdditional) encodeFields(e *jx.Encoder) {\
    for k, elem := range s {\
        e.FieldStart(k)\
\
        if len(elem) != 0 {\
            e.Raw(elem)\
        }\
    }\
}\
\
// Decode decodes ResolvedPlacementAdditional from json.\
func (s *ResolvedPlacementAdditional) Decode(d *jx.Decoder) error {\
    if s == nil {\
        return errors.New("invalid: unable to decode ResolvedPlacementAdditional to nil")\
    }\
    m := s.init()\
    if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error {\
        var elem jx.Raw\
        if err := func() error {\
            v, err := d.RawAppend(nil)\
            elem = jx.Raw(v)\
            if err != nil {\
                return err\
            }\
            return nil\
        }(); err != nil {\
            return errors.Wrapf(err, "decode field %q", k)\
        }\
        m[string(k)] = elem\
        return nil\
    }); err != nil {\
        return errors.Wrap(err, "decode ResolvedPlacementAdditional")\
    }\
\
    return nil\
}\
\
// MarshalJSON implements stdjson.Marshaler.\
func (s ResolvedPlacementAdditional) MarshalJSON() ([]byte, error) {\
    e := jx.Encoder{}\
    s.Encode(&e)\
    return e.Bytes(), nil\
}\
\
// UnmarshalJSON implements stdjson.Unmarshaler.\
func (s *ResolvedPlacementAdditional) UnmarshalJSON(data []byte) error {\
    d := jx.DecodeBytes(data)\
    return s.Decode(d)\
}\
\
// Encode implements json.Marshaler.\
func (s *ResourceAccess) Encode(e *jx.Encoder) {\
    e.ObjStart()\
```\
\
Minternal/coreapi/oas\_json\_gen.go+424\
\
```\
62 unmodified lines\
\
63\
64\
65\
66\
67\
68\
69\
\
62 unmodified lines\
\
    PatchRepoCIWebhookOperation            OperationName = "PatchRepoCIWebhook"\
    RemoveOrgMemberOperation               OperationName = "RemoveOrgMember"\
    ResolveHandleOperation                 OperationName = "ResolveHandle"\
    ResolveMirrorPlacementsOperation       OperationName = "ResolveMirrorPlacements"\
    RevokeProjectAccessOperation           OperationName = "RevokeProjectAccess"\
    RevokeProjectAccessByProviderOperation OperationName = "RevokeProjectAccessByProvider"\
    RevokeRepoAccessOperation              OperationName = "RevokeRepoAccess"\
```\
\
Minternal/coreapi/oas\_operations\_gen.go+1\
\
```\
312 unmodified lines\
\
313\
314\
315\
316\
317\
318\
319\
320\
321\
322\
323\
324\
325\
326\
327\
\
312 unmodified lines\
\
    Handle string\
}\
\
// ResolveMirrorPlacementsParams is parameters of resolveMirrorPlacements operation.\
type ResolveMirrorPlacementsParams struct {\
    Provider ResolveMirrorPlacementsProvider\
    // Upstream owner login (case-insensitive).\
    Owner string\
    // Upstream repo name (case-insensitive).\
    Repo string\
}\
\
// RevokeProjectAccessParams is parameters of revokeProjectAccess operation.\
type RevokeProjectAccessParams struct {\
    ProjectId   string\
```\
\
Minternal/coreapi/oas\_parameters\_gen.go+9\
\
```\
4791 unmodified lines\
\
4792\
4793\
4794\
4795\
4796\
4797\
4798\
4799\
4800\
4801\
4802\
4803\
4804\
4805\
4806\
4807\
4808\
4809\
4810\
4811\
4812\
4813\
4814\
4815\
4816\
4817\
4818\
4819\
4820\
4821\
4822\
4823\
4824\
4825\
4826\
4827\
4828\
4829\
4830\
4831\
4832\
4833\
4834\
4835\
4836\
4837\
4838\
4839\
4840\
4841\
4842\
4843\
4844\
4845\
4846\
4847\
4848\
4849\
4850\
4851\
4852\
4853\
4854\
4855\
4856\
4857\
4858\
4859\
4860\
4861\
4862\
4863\
4864\
4865\
4866\
4867\
4868\
4869\
4870\
4871\
4872\
4873\
4874\
4875\
4876\
4877\
4878\
4879\
4880\
4881\
4882\
4883\
4884\
4885\
4886\
4887\
4888\
4889\
\
4791 unmodified lines\
\
    return res, errors.Wrap(defRes, "error")\
}\
\
func decodeResolveMirrorPlacementsResponse(resp *http.Response) (res *ResolvePlacementsOutputBody, _ error) {\
    switch resp.StatusCode {\
    case 200:\
        // Code 200.\
        ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))\
        if err != nil {\
            return res, errors.Wrap(err, "parse media type")\
        }\
        switch {\
        case ct == "application/json":\
            buf, err := io.ReadAll(resp.Body)\
            if err != nil {\
                return res, err\
            }\
            d := jx.DecodeBytes(buf)\
\
            var response ResolvePlacementsOutputBody\
            if err := func() error {\
                if err := response.Decode(d); err != nil {\
                    return err\
                }\
                if err := d.Skip(); err != io.EOF {\
                    return errors.New("unexpected trailing data")\
                }\
                return nil\
            }(); err != nil {\
                err = &ogenerrors.DecodeBodyError{\
                    ContentType: ct,\
                    Body:        buf,\
                    Err:         err,\
                }\
                return res, err\
            }\
            // Validate response.\
            if err := func() error {\
                if err := response.Validate(); err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return res, errors.Wrap(err, "validate")\
            }\
            return &response, nil\
        default:\
            return res, validate.InvalidContentType(ct)\
        }\
    }\
    // Convenient error response.\
    defRes, err := func() (res *ErrorModelStatusCode, err error) {\
        ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))\
        if err != nil {\
            return res, errors.Wrap(err, "parse media type")\
        }\
        switch {\
        case ct == "application/problem+json":\
            buf, err := io.ReadAll(resp.Body)\
            if err != nil {\
                return res, err\
            }\
            d := jx.DecodeBytes(buf)\
\
            var response ErrorModel\
            if err := func() error {\
                if err := response.Decode(d); err != nil {\
                    return err\
                }\
                if err := d.Skip(); err != io.EOF {\
                    return errors.New("unexpected trailing data")\
                }\
                return nil\
            }(); err != nil {\
                err = &ogenerrors.DecodeBodyError{\
                    ContentType: ct,\
                    Body:        buf,\
                    Err:         err,\
                }\
                return res, err\
            }\
            return &ErrorModelStatusCode{\
                StatusCode: resp.StatusCode,\
                Response:   response,\
            }, nil\
        default:\
            return res, validate.InvalidContentType(ct)\
        }\
    }()\
    if err != nil {\
        return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode)\
    }\
    return res, errors.Wrap(defRes, "error")\
}\
\
func decodeRevokeProjectAccessResponse(resp *http.Response) (res *RevokeProjectAccessNoContent, _ error) {\
    switch resp.StatusCode {\
    case 204:\
```\
\
Minternal/coreapi/oas\_response\_decoders\_gen.go+92\
\
```\
8481 unmodified lines\
\
8482\
8483\
8484\
8485\
8486\
8487\
8488\
8489\
8490\
8491\
8492\
8493\
8494\
8495\
8496\
8497\
8498\
8499\
8500\
8501\
8502\
8503\
8504\
8505\
8506\
8507\
8508\
8509\
8510\
8511\
8512\
8513\
8514\
8515\
8516\
8517\
8518\
8519\
8520\
8521\
8522\
8523\
8524\
8525\
8526\
8527\
8528\
8529\
8530\
8531\
8532\
8533\
8534\
8535\
8536\
8537\
8538\
8539\
8540\
8541\
8542\
8543\
8544\
8545\
8546\
8547\
8548\
8549\
8550\
8551\
8552\
8553\
8554\
8555\
8556\
8557\
8558\
8559\
8560\
8561\
8562\
8563\
8564\
8565\
8566\
8567\
8568\
8569\
8570\
76 unmodified lines\
\
8647\
8648\
8649\
8650\
8651\
8652\
8653\
8654\
8655\
8656\
8657\
8658\
8659\
8660\
8661\
8662\
8663\
8664\
8665\
8666\
8667\
8668\
8669\
8670\
8671\
8672\
8673\
8674\
8675\
8676\
8677\
8678\
8679\
8680\
8681\
8682\
8683\
8684\
8685\
8686\
8687\
8688\
8689\
8690\
8691\
8692\
8693\
8694\
8695\
8696\
8697\
8698\
8699\
8700\
8701\
8702\
8703\
8704\
8705\
8706\
8707\
8708\
8709\
8710\
8711\
8712\
8713\
8714\
8715\
8716\
8717\
8718\
8719\
8720\
8721\
8722\
8723\
8724\
\
8481 unmodified lines\
\
    return m\
}\
\
type ResolveMirrorPlacementsProvider string\
\
const (\
    ResolveMirrorPlacementsProviderGithub ResolveMirrorPlacementsProvider = "github"\
)\
\
// AllValues returns all ResolveMirrorPlacementsProvider values.\
func (ResolveMirrorPlacementsProvider) AllValues() []ResolveMirrorPlacementsProvider {\
    return []ResolveMirrorPlacementsProvider{\
        ResolveMirrorPlacementsProviderGithub,\
    }\
}\
\
// MarshalText implements encoding.TextMarshaler.\
func (s ResolveMirrorPlacementsProvider) MarshalText() ([]byte, error) {\
    switch s {\
    case ResolveMirrorPlacementsProviderGithub:\
        return []byte(s), nil\
    default:\
        return nil, errors.Errorf("invalid value: %q", s)\
    }\
}\
\
// UnmarshalText implements encoding.TextUnmarshaler.\
func (s *ResolveMirrorPlacementsProvider) UnmarshalText(data []byte) error {\
    switch ResolveMirrorPlacementsProvider(data) {\
    case ResolveMirrorPlacementsProviderGithub:\
        *s = ResolveMirrorPlacementsProviderGithub\
        return nil\
    default:\
        return errors.Errorf("invalid value: %q", data)\
    }\
}\
\
// Ref: #/components/schemas/ResolvePlacementsOutputBody\
type ResolvePlacementsOutputBody struct {\
    // A URL to the JSON Schema for this object.\
    Schema          OptURI              `json:"$schema"`\
    Placements      []ResolvedPlacement `json:"placements"`\
    AdditionalProps ResolvePlacementsOutputBodyAdditional\
}\
\
// GetSchema returns the value of Schema.\
func (s *ResolvePlacementsOutputBody) GetSchema() OptURI {\
    return s.Schema\
}\
\
// GetPlacements returns the value of Placements.\
func (s *ResolvePlacementsOutputBody) GetPlacements() []ResolvedPlacement {\
    return s.Placements\
}\
\
// GetAdditionalProps returns the value of AdditionalProps.\
func (s *ResolvePlacementsOutputBody) GetAdditionalProps() ResolvePlacementsOutputBodyAdditional {\
    return s.AdditionalProps\
}\
\
// SetSchema sets the value of Schema.\
func (s *ResolvePlacementsOutputBody) SetSchema(val OptURI) {\
    s.Schema = val\
}\
\
// SetPlacements sets the value of Placements.\
func (s *ResolvePlacementsOutputBody) SetPlacements(val []ResolvedPlacement) {\
    s.Placements = val\
}\
\
// SetAdditionalProps sets the value of AdditionalProps.\
func (s *ResolvePlacementsOutputBody) SetAdditionalProps(val ResolvePlacementsOutputBodyAdditional) {\
    s.AdditionalProps = val\
}\
\
type ResolvePlacementsOutputBodyAdditional map[string]jx.Raw\
\
func (s *ResolvePlacementsOutputBodyAdditional) init() ResolvePlacementsOutputBodyAdditional {\
    m := *s\
    if m == nil {\
        m = map[string]jx.Raw{}\
        *s = m\
    }\
    return m\
}\
\
// Ref: #/components/schemas/ResolvedIdentity\
type ResolvedIdentity struct {\
    // A URL to the JSON Schema for this object.\
76 unmodified lines\
\
    return m\
}\
\
// Ref: #/components/schemas/ResolvedPlacement\
type ResolvedPlacement struct {\
    // Physical cell the mirror's cluster runs in, e.g. aws-us-east-2.\
    Cell OptString `json:"cell"`\
    // Public host of the cluster serving this mirror.\
    ClusterHost     string    `json:"clusterHost"`\
    Jurisdiction    OptString `json:"jurisdiction"`\
    MirrorId        string    `json:"mirrorId"`\
    AdditionalProps ResolvedPlacementAdditional\
}\
\
// GetCell returns the value of Cell.\
func (s *ResolvedPlacement) GetCell() OptString {\
    return s.Cell\
}\
\
// GetClusterHost returns the value of ClusterHost.\
func (s *ResolvedPlacement) GetClusterHost() string {\
    return s.ClusterHost\
}\
\
// GetJurisdiction returns the value of Jurisdiction.\
func (s *ResolvedPlacement) GetJurisdiction() OptString {\
    return s.Jurisdiction\
}\
\
// GetMirrorId returns the value of MirrorId.\
func (s *ResolvedPlacement) GetMirrorId() string {\
    return s.MirrorId\
}\
\
// GetAdditionalProps returns the value of AdditionalProps.\
func (s *ResolvedPlacement) GetAdditionalProps() ResolvedPlacementAdditional {\
    return s.AdditionalProps\
}\
\
// SetCell sets the value of Cell.\
func (s *ResolvedPlacement) SetCell(val OptString) {\
    s.Cell = val\
}\
\
// SetClusterHost sets the value of ClusterHost.\
func (s *ResolvedPlacement) SetClusterHost(val string) {\
    s.ClusterHost = val\
}\
\
// SetJurisdiction sets the value of Jurisdiction.\
func (s *ResolvedPlacement) SetJurisdiction(val OptString) {\
    s.Jurisdiction = val\
}\
\
// SetMirrorId sets the value of MirrorId.\
func (s *ResolvedPlacement) SetMirrorId(val string) {\
    s.MirrorId = val\
}\
\
// SetAdditionalProps sets the value of AdditionalProps.\
func (s *ResolvedPlacement) SetAdditionalProps(val ResolvedPlacementAdditional) {\
    s.AdditionalProps = val\
}\
\
type ResolvedPlacementAdditional map[string]jx.Raw\
\
func (s *ResolvedPlacementAdditional) init() ResolvedPlacementAdditional {\
    m := *s\
    if m == nil {\
        m = map[string]jx.Raw{}\
        *s = m\
    }\
    return m\
}\
\
// Ref: #/components/schemas/ResourceAccess\
type ResourceAccess struct {\
    Permissions     []string `json:"permissions"`\
```\
\
Minternal/coreapi/oas\_schemas\_gen.go+155\
\
```\
75 unmodified lines\
\
76\
77\
78\
79\
80\
81\
82\
80 unmodified lines\
\
163\
164\
165\
166\
167\
168\
169\
\
75 unmodified lines\
\
    PatchRepoCIWebhookOperation:            {},\
    RemoveOrgMemberOperation:               {},\
    ResolveHandleOperation:                 {},\
    ResolveMirrorPlacementsOperation:       {},\
    RevokeProjectAccessOperation:           {},\
    RevokeProjectAccessByProviderOperation: {},\
    RevokeRepoAccessOperation:              {},\
80 unmodified lines\
\
    PatchRepoCIWebhookOperation:            {},\
    RemoveOrgMemberOperation:               {},\
    ResolveHandleOperation:                 {},\
    ResolveMirrorPlacementsOperation:       {},\
    RevokeProjectAccessOperation:           {},\
    RevokeProjectAccessByProviderOperation: {},\
    RevokeRepoAccessOperation:              {},\
```\
\
Minternal/coreapi/oas\_security\_gen.go+2\
\
```\
2308 unmodified lines\
\
2309\
2310\
2311\
2312\
2313\
2314\
2315\
2316\
2317\
2318\
2319\
2320\
2321\
2322\
2323\
2324\
2325\
2326\
2327\
2328\
2329\
2330\
2331\
2332\
2333\
2334\
2335\
2336\
2337\
2338\
2339\
2340\
2341\
2342\
2343\
2344\
2345\
2346\
\
2308 unmodified lines\
\
    }\
}\
\
func (s ResolveMirrorPlacementsProvider) Validate() error {\
    switch s {\
    case "github":\
        return nil\
    default:\
        return errors.Errorf("invalid value: %v", s)\
    }\
}\
\
func (s *ResolvePlacementsOutputBody) Validate() error {\
    if s == nil {\
        return validate.ErrNilPointer\
    }\
\
    var failures []validate.FieldError\
    if err := func() error {\
        if s.Placements == nil {\
            return errors.New("nil is invalid value")\
        }\
        return nil\
    }(); err != nil {\
        failures = append(failures, validate.FieldError{\
            Name:  "placements",\
            Error: err,\
        })\
    }\
    if len(failures) > 0 {\
        return &validate.Error{Fields: failures}\
    }\
    return nil\
}\
\
func (s *ResourceAccess) Validate() error {\
    if s == nil {\
        return validate.ErrNilPointer\
```\
\
Minternal/coreapi/oas\_validators\_gen.go+32\
\
```\
2499 unmodified lines\
\
2500\
2501\
2502\
2503\
2504\
2505\
2506\
2507\
2508\
2509\
2510\
2511\
2512\
2513\
2514\
2515\
2516\
2517\
2518\
2519\
2520\
2521\
2522\
2523\
2524\
2525\
2526\
2527\
2528\
2529\
27 unmodified lines\
\
2557\
2558\
2559\
2560\
2561\
2562\
2563\
2564\
2565\
2566\
2567\
2568\
2569\
2570\
2571\
2572\
2573\
2574\
2575\
2576\
2577\
2578\
2579\
2580\
2581\
2582\
2583\
2584\
2585\
2586\
1254 unmodified lines\
\
3841\
3842\
3843\
3844\
3845\
3846\
3847\
3848\
3849\
3850\
3851\
3852\
3853\
3854\
3855\
3856\
3857\
3858\
3859\
3860\
3861\
3862\
3863\
3864\
3865\
3866\
3867\
3868\
3869\
3870\
3871\
3872\
3873\
3874\
3875\
3876\
3877\
3878\
3879\
3880\
3881\
3882\
3883\
3884\
3885\
3886\
3887\
3888\
3889\
3890\
3891\
3892\
3893\
3894\
3895\
3896\
3897\
3898\
3899\
3900\
3901\
3902\
3903\
3904\
3905\
3906\
3907\
3908\
3909\
3910\
3911\
3912\
3913\
3914\
3915\
3916\
3917\
3918\
3919\
3920\
3921\
3922\
3923\
\
2499 unmodified lines\
\
        ],\
        "type": "object"\
      },\
      "ResolvePlacementsOutputBody": {\
        "additionalProperties": true,\
        "properties": {\
          "$schema": {\
            "description": "A URL to the JSON Schema for this object.",\
            "examples": [\
              "/api/v1/schemas/ResolvePlacementsOutputBody.json"\
            ],\
            "format": "uri",\
            "readOnly": true,\
            "type": "string"\
          },\
          "placements": {\
            "items": {\
              "$ref": "#/components/schemas/ResolvedPlacement"\
            },\
            "type": "array"\
          }\
        },\
        "required": [\
          "placements"\
        ],\
        "type": "object"\
      },\
      "ResolvedIdentity": {\
        "additionalProperties": true,\
        "properties": {\
27 unmodified lines\
\
        ],\
        "type": "object"\
      },\
      "ResolvedPlacement": {\
        "additionalProperties": true,\
        "properties": {\
          "cell": {\
            "description": "Physical cell the mirror's cluster runs in, e.g. aws-us-east-2.",\
            "type": "string"\
          },\
          "clusterHost": {\
            "description": "Public host of the cluster serving this mirror.",\
            "type": "string"\
          },\
          "jurisdiction": {\
            "type": "string"\
          },\
          "mirrorId": {\
            "type": "string"\
          }\
        },\
        "required": [\
          "mirrorId",\
          "clusterHost"\
        ],\
        "type": "object"\
      },\
      "ResourceAccess": {\
        "additionalProperties": true,\
        "properties": {\
1254 unmodified lines\
\
        ]
      }
    },
    "/mirrors/placements": {
      "get": {
        "operationId": "resolveMirrorPlacements",
        "parameters": [\
          {\
            "explode": false,\
            "in": "query",\
            "name": "provider",\
            "required": true,\
            "schema": {\
              "enum": [\
                "github"\
              ],\
              "type": "string"\
            }\
          },\
          {\
            "description": "Upstream owner login (case-insensitive).",\
            "explode": false,\
            "in": "query",\
            "name": "owner",\
            "required": true,\
            "schema": {\
              "description": "Upstream owner login (case-insensitive).",\
              "minLength": 1,\
              "type": "string"\
            }\
          },\
          {\
            "description": "Upstream repo name (case-insensitive).",\
            "explode": false,\
            "in": "query",\
            "name": "repo",\
            "required": true,\
            "schema": {\
              "description": "Upstream repo name (case-insensitive).",\
              "minLength": 1,\
              "type": "string"\
            }\
          }\
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResolvePlacementsOutputBody"
                }
              }
            },
            "description": "OK"
          },
          "default": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Error"
          }
        },
        "security": [\
          {\
            "bearerAuth": []\
          },\
          {\
            "sessionAuth": []\
          }\
        ],
        "summary": "Resolve the pullable cluster placements of a mirrored upstream (clone discovery)",
        "tags": [\
          "mirrors"\
        ]
      }
    },
    "/mirrors/{mirrorId}": {
      "get": {
        "operationId": "getMirror",

Minternal/coreapi/spec/core.gen.json+125

2841 unmodified lines

2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
7097 unmodified lines

9993
9994
9995
9996
9997
9998
9999
10000
10001
10002
10003
10004
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014
10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047
10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061
10062
10063
10064
10065
10066
10067
10068
10069
10070
10071
10072
10073
10074
10075
10076
10077
10078
10079
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097
10098
10099
10100
10101
10102
10103
10104
10105
10106
10107
10108
10109
10110
10111
10112
10113
10114
10115

2841 unmodified lines

"memberships"
        ],
        "type": "object"
      },
      "ResolvePlacementsOutputBody": {
        "additionalProperties": true,
        "properties": {
          "$schema": {
            "description": "A URL to the JSON Schema for this object.",
            "examples": [\
              "/api/v1/schemas/ResolvePlacementsOutputBody.json"\
            ],
            "format": "uri",
            "readOnly": true,
            "type": "string"
          },
          "placements": {
            "items": {
              "$ref": "#/components/schemas/ResolvedPlacement"
            },
            "type": "array"
          }
        },
        "required": [\
          "placements"\
        ],
        "type": "object"
      },
      "ResolvedPlacement": {
        "additionalProperties": true,
        "properties": {
          "cell": {
            "description": "Physical cell the mirror's cluster runs in, e.g. aws-us-east-2.",
            "type": "string"
          },
          "clusterHost": {
            "description": "Public host of the cluster serving this mirror.",
            "type": "string"
          },
          "jurisdiction": {
            "type": "string"
          },
          "mirrorId": {
            "type": "string"
          }
        },
        "required": [\
          "mirrorId",\
          "clusterHost"\
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
7097 unmodified lines

"meta"
        ]
      }
    },
    "/mirrors/placements": {
      "get": {
        "operationId": "resolveMirrorPlacements",
        "parameters": [\
          {\
            "explode": false,\
            "in": "query",\
            "name": "provider",\
            "required": true,\
            "schema": {\
              "enum": [\
                "github"\
              ],\
              "type": "string"\
            }\
          },\
          {\
            "description": "Upstream owner login (case-insensitive).",\
            "explode": false,\
            "in": "query",\
            "name": "owner",\
            "required": true,\
            "schema": {\
              "description": "Upstream owner login (case-insensitive).",\
              "minLength": 1,\
              "type": "string"\
            }\
          },\
          {\
            "description": "Upstream repo name (case-insensitive).",\
            "explode": false,\
            "in": "query",\
            "name": "repo",\
            "required": true,\
            "schema": {\
              "description": "Upstream repo name (case-insensitive).",\
              "minLength": 1,\
              "type": "string"\
            }\
          }\
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResolvePlacementsOutputBody"
                }
              }
            },
            "description": "OK"
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Bad Request"
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Unauthorized"
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Forbidden"
          },
          "422": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Unprocessable Entity"
          },
          "500": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorModel"
                }
              }
            },
            "description": "Internal Server Error"
          }
        },
        "security": [\
          {\
            "bearerAuth": []\
          },\
          {\
            "sessionAuth": []\
          }\
        ],
        "summary": "Resolve the pullable cluster placements of a mirrored upstream (clone discovery)",
        "tags": [\
          "mirrors"\
        ]
      }
    }
  },
  "servers": [\
```\
\
Minternal/coreapi/spec/core.openapi.json+165