Skip to content

Commit d5e40eb

Browse files
committed
chore: re-vendor lib/
1 parent 2a185a5 commit d5e40eb

24 files changed

Lines changed: 578 additions & 1297 deletions

lib/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ This directory contains vendored packages from `github.com/sourcegraph/sourcegra
55
## Source
66

77
- **Repository**: https://github.com/sourcegraph/sourcegraph
8-
- **Commit**: bdc2f4bb8b59f78f4fa8868b2690b673b41948d4
9-
- **Date**: 2026-06-01 07:34:50 +0100
8+
- **Commit**: de6bd07264df18d8dc66e2aebeaad18ac504390f
9+
- **Date**: 2026-06-11 13:59:53 +0000
1010

1111
## Updating
1212

lib/batches/batch_spec.go

Lines changed: 184 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,39 @@ type BatchSpec struct {
3535
Description string `json:"description,omitempty" yaml:"description"`
3636
On []OnQueryOrRepository `json:"on,omitempty" yaml:"on"`
3737
Workspaces []WorkspaceConfiguration `json:"workspaces,omitempty" yaml:"workspaces"`
38+
Checkout *CheckoutOptions `json:"checkout,omitempty" yaml:"checkout,omitempty"`
3839
Steps []Step `json:"steps,omitempty" yaml:"steps"`
3940
TransformChanges *TransformChanges `json:"transformChanges,omitempty" yaml:"transformChanges,omitempty"`
4041
ImportChangesets []ImportChangeset `json:"importChangesets,omitempty" yaml:"importChangesets"`
4142
ChangesetTemplate *ChangesetTemplate `json:"changesetTemplate,omitempty" yaml:"changesetTemplate"`
42-
ChangesetHooks *ChangesetHooks `json:"changesetHooks,omitempty" yaml:"hooks,omitempty"`
43+
ChangesetHooks *ChangesetHooks `json:"changesetHooks,omitempty" yaml:"changesetHooks,omitempty"`
44+
}
45+
46+
// DefaultCheckoutFetchDepth is the git fetch depth used for workspace checkouts
47+
// when the spec does not configure checkout.fetchDepth. It fetches only the
48+
// target commit (a shallow clone), which is sufficient for most batch changes.
49+
const DefaultCheckoutFetchDepth = 1
50+
51+
// CheckoutOptions controls how repositories are checked out for workspace
52+
// execution. Only allowed when Version is 3.
53+
type CheckoutOptions struct {
54+
// FetchDepth controls how many commits of git history are fetched into the
55+
// workspace checkout. A value of 0 fetches the full history; any positive
56+
// value fetches that many commits. When nil, DefaultCheckoutFetchDepth is
57+
// used. History is required by tasks that inspect git history (e.g.
58+
// "update codeowners based on git history") or changeset hooks such as
59+
// onMergeConflict.
60+
FetchDepth *int `json:"fetchDepth,omitempty" yaml:"fetchDepth,omitempty"`
61+
}
62+
63+
// CheckoutFetchDepth returns the configured git fetch depth for workspace
64+
// checkouts, applying DefaultCheckoutFetchDepth when unset. A return value of 0
65+
// means full history.
66+
func (s *BatchSpec) CheckoutFetchDepth() int {
67+
if s.Checkout == nil || s.Checkout.FetchDepth == nil {
68+
return DefaultCheckoutFetchDepth
69+
}
70+
return *s.Checkout.FetchDepth
4371
}
4472

4573
// Hooks declares side-effect actions to run at well-defined changeset
@@ -54,16 +82,76 @@ type ChangesetHooks struct {
5482
// Hook actions reuse the Step shape from the top-level steps block.
5583
type ChangesetHookAction struct {
5684
Steps []Step `json:"steps,omitempty" yaml:"steps,omitempty"`
85+
// Commit configures the follow-up commits this hook produces. Its message
86+
// and author are resolved independently: when the message is empty a
87+
// per-event default is used, and when the author is nil the
88+
// changesetTemplate's author is inherited (which itself falls back to the
89+
// changeset's author). It may be nil if the hook does not configure a
90+
// commit at all.
91+
Commit *ExpandedGitCommitDescription `json:"commit,omitempty" yaml:"commit,omitempty"`
92+
}
93+
94+
// HasCommit reports whether the hook action declares its own commit
95+
// information (a message and/or an author).
96+
func (a ChangesetHookAction) HasCommit() bool {
97+
return a.Commit != nil && (a.Commit.Message != "" || a.Commit.Author != nil)
98+
}
99+
100+
// DefaultCommitMessage returns the commit message used for follow-up commits
101+
// produced by this hook event when the hook action does not provide its own
102+
// message. The returned value may contain changeset template variables (e.g.
103+
// ${{ repository.branch }}) that are rendered when the follow-up commit is
104+
// built.
105+
func (e ChangesetHookEvent) DefaultCommitMessage() string {
106+
switch e {
107+
case ChangesetHookEventOnMergeConflict:
108+
return "Fix for merge conflict on ${{ repository.branch }}"
109+
case ChangesetHookEventOnCIFailure:
110+
return "Fix for CI failure on ${{ repository.branch }}"
111+
default:
112+
return "Changeset hook fix on ${{ repository.branch }}"
113+
}
114+
}
115+
116+
// ActionForEvent returns the hook action configured for the given event, and
117+
// whether the event is known.
118+
func (h *ChangesetHooks) ActionForEvent(event ChangesetHookEvent) (ChangesetHookAction, bool) {
119+
switch event {
120+
case ChangesetHookEventOnCIFailure:
121+
return h.OnCIFailure, true
122+
case ChangesetHookEventOnMergeConflict:
123+
return h.OnMergeConflict, true
124+
default:
125+
return ChangesetHookAction{}, false
126+
}
57127
}
58128

59-
type changesetHookEvent string
129+
type ChangesetHookEvent string
60130

61131
// Hook event names. Kept here so callers don't pass typoed strings.
62132
const (
63-
ChangesetHookEventOnCIFailure changesetHookEvent = "onCIFailure"
64-
ChangesetHookEventOnMergeConflict changesetHookEvent = "onMergeConflict"
133+
ChangesetHookEventOnCIFailure ChangesetHookEvent = "onCIFailure"
134+
ChangesetHookEventOnMergeConflict ChangesetHookEvent = "onMergeConflict"
65135
)
66136

137+
// AllChangesetHookEvents is the canonical list of supported changeset hook
138+
// events. Add new events here so callers (validation, filtering, etc.) stay in
139+
// sync from a single source of truth.
140+
var AllChangesetHookEvents = []ChangesetHookEvent{
141+
ChangesetHookEventOnCIFailure,
142+
ChangesetHookEventOnMergeConflict,
143+
}
144+
145+
// Valid reports whether e is a known changeset hook event.
146+
func (e ChangesetHookEvent) Valid() bool {
147+
for _, known := range AllChangesetHookEvents {
148+
if e == known {
149+
return true
150+
}
151+
}
152+
return false
153+
}
154+
67155
type ChangesetTemplate struct {
68156
Title string `json:"title,omitempty" yaml:"title"`
69157
Body string `json:"body,omitempty" yaml:"body"`
@@ -127,16 +215,47 @@ type Step struct {
127215
If any `json:"if,omitempty" yaml:"if,omitempty"`
128216
}
129217

218+
type CodingAgentType string
219+
220+
const (
221+
CodingAgentTypeCodex CodingAgentType = "codex"
222+
CodingAgentTypeClaudeCode CodingAgentType = "claude-code"
223+
)
224+
130225
type CodingAgentStep struct {
131-
Type string `json:"type,omitempty" yaml:"type"`
132-
Prompt string `json:"prompt,omitempty" yaml:"prompt"`
226+
Type CodingAgentType `json:"type,omitempty" yaml:"type"`
227+
Prompt string `json:"prompt,omitempty" yaml:"prompt"`
133228
}
134229

135230
type BuildImageStep struct {
136231
Run string `json:"run" yaml:"run"`
137232
BaseImage string `json:"baseImage" yaml:"baseImage"`
138233
}
139234

235+
// KanikoImage is the container image used to build new OCI images from a base
236+
// image and a run script (see buildImage steps). It is referenced both when
237+
// desugaring buildImage steps into run steps and by the executor when deciding
238+
// whether a step is a buildImage-derived build container. Can be exchanged for
239+
// other image build tooling, as long as the build script is updated as well.
240+
//
241+
// Kaniko builds run unprivileged under docker's default seccomp/apparmor
242+
// profiles, so no extra docker flags are required for this container.
243+
//
244+
// The -alpine variant is required: the desugared buildImage step runs as an
245+
// ordinary `run` step, so the runner (batch-exec/src-cli) probes the image by
246+
// running `<shell> -c mktemp` and overrides the entrypoint with /bin/sh. The
247+
// plain kaniko image has no shell at all, and the -debug variant is built FROM
248+
// scratch without a /tmp directory, so its mktemp fails and the probe rejects
249+
// the image. -alpine has /bin/sh, /tmp, and the busybox wget/base64 tools used
250+
// by the generated build script. It also presets KANIKO_PRE_CLEANUP=1 and
251+
// KANIKO_CLEANUP=1, which wipe the container filesystem around the build (the
252+
// generated script must not rely on external binaries after invoking kaniko).
253+
//
254+
// Pinned to a digest so the build is reproducible and not subject to the tag
255+
// being re-pushed; the tag is retained for readability. When bumping, update
256+
// both the tag and the @sha256 digest (the multi-arch index digest).
257+
const KanikoImage = "ghcr.io/osscontainertools/kaniko:v1.27.6-alpine@sha256:795a358f6c22a9fcd66bb7e14bd97728155e1c171ca951f3c3ba6501054234ce"
258+
140259
// MarshalJSON canonicalizes the v3 `image:` field into `container:` on the
141260
// wire. Both fields exist on Step for ergonomic reasons (v3 specs use
142261
// `image:`, v1/v2 specs use `container:`), but src-cli's Step has only
@@ -240,6 +359,12 @@ func parseBatchSpec(schema string, data []byte) (*BatchSpec, error) {
240359
errs = errors.Append(errs, NewValidationError(errors.New("changesetTemplate.published is not supported in batch spec version 3; drive publication via the publish_changesets tool instead")))
241360
}
242361

362+
// v3 specs do not support importChangesets — batch change agents only
363+
// manage changesets they own.
364+
if spec.Version == 3 && len(spec.ImportChangesets) != 0 {
365+
errs = errors.Append(errs, NewValidationError(errors.New("importChangesets is not supported in batch spec version 3")))
366+
}
367+
243368
for i, step := range spec.Steps {
244369
for _, mount := range step.Mount {
245370
if strings.Contains(mount.Path, invalidMountCharacters) {
@@ -255,20 +380,39 @@ func parseBatchSpec(schema string, data []byte) (*BatchSpec, error) {
255380
if step.BuildImage != nil && step.Run != "" {
256381
errs = errors.Append(errs, NewValidationError(errors.Newf("step %d: buildImage and run cannot be combined in the same step", i+1)))
257382
}
258-
for name := range step.Files {
259-
if strings.Contains(name, invalidMountCharacters) {
260-
errs = errors.Append(errs, NewValidationError(errors.Newf("step %d files target path contains invalid characters", i+1)))
261-
}
262-
}
263383
}
264384

265385
if hookErr := validateHooks(&spec); hookErr != nil {
266386
errs = errors.Append(errs, hookErr)
267387
}
268388

389+
if checkoutErr := validateCheckout(&spec); checkoutErr != nil {
390+
errs = errors.Append(errs, checkoutErr)
391+
}
392+
269393
return &spec, errs
270394
}
271395

396+
// validateCheckout performs Go-level validation of spec.Checkout beyond what
397+
// the JSON schema enforces. The schema already gates `checkout:` on `version: 3`
398+
// and constrains `fetchDepth` to a non-negative integer. We re-check the version
399+
// invariant here so non-schema callers (and any future schema drift) still fail
400+
// safely.
401+
func validateCheckout(spec *BatchSpec) error {
402+
if spec.Checkout == nil {
403+
return nil
404+
}
405+
406+
var errs error
407+
if spec.Version != 3 {
408+
errs = errors.Append(errs, NewValidationError(errors.New("batch spec checkout requires version: 3")))
409+
}
410+
if spec.Checkout.FetchDepth != nil && *spec.Checkout.FetchDepth < 0 {
411+
errs = errors.Append(errs, NewValidationError(errors.New("checkout.fetchDepth must be greater than or equal to 0")))
412+
}
413+
return errs
414+
}
415+
272416
// validateHooks performs Go-level validation of spec.Hooks beyond what the
273417
// JSON schema enforces. The schema already gates `hooks:` on `version: 3` and
274418
// rejects unknown event names. We re-check the version invariant here so
@@ -285,8 +429,30 @@ func validateHooks(spec *BatchSpec) error {
285429
errs = errors.Append(errs, NewValidationError(errors.New("batch spec hooks require version: 3")))
286430
}
287431

288-
validate := func(event changesetHookEvent, action ChangesetHookAction) {
432+
validate := func(event ChangesetHookEvent, action ChangesetHookAction) {
289433
for i, step := range action.Steps {
434+
// Hook steps use the v3 step shape, which requires an image and
435+
// exactly one of a run command or a codingAgent. The JSON schema
436+
// also enforces this (see the version 3 conditional in
437+
// batch_spec.schema.json), so for spec strings parsed through the
438+
// schema these checks are a defense-in-depth backstop; we keep them
439+
// here so non-schema callers (and any future schema drift) still
440+
// fail safely.
441+
if step.Image == "" {
442+
errs = errors.Append(errs, NewValidationError(errors.Newf(
443+
"hooks.%s step %d must specify an image", event, i+1,
444+
)))
445+
}
446+
if step.Run == "" && step.CodingAgent == nil {
447+
errs = errors.Append(errs, NewValidationError(errors.Newf(
448+
"hooks.%s step %d must specify either run or codingAgent", event, i+1,
449+
)))
450+
}
451+
if step.CodingAgent != nil && step.Run != "" {
452+
errs = errors.Append(errs, NewValidationError(errors.Newf(
453+
"hooks.%s step %d: codingAgent and run cannot be combined in the same step", event, i+1,
454+
)))
455+
}
290456
for _, mount := range step.Mount {
291457
if strings.Contains(mount.Path, invalidMountCharacters) {
292458
errs = errors.Append(errs, NewValidationError(errors.Newf(
@@ -370,12 +536,14 @@ func SkippedStepsForRepo(spec *BatchSpec, repoName string, fileMatches []string)
370536
return skipped, nil
371537
}
372538

373-
// RequiredEnvVars inspects all steps for outer environment variables used and
374-
// compiles a deduplicated list from those.
375-
func (s *BatchSpec) RequiredEnvVars() []string {
539+
// RequiredEnvVarsForSteps inspects the given steps for outer environment
540+
// variables used and compiles a deduplicated list from those. Callers pass the
541+
// specific steps they care about (e.g. a spec's top-level steps or its
542+
// changeset hook steps).
543+
func RequiredEnvVarsForSteps(steps []Step) []string {
376544
requiredMap := map[string]struct{}{}
377545
required := []string{}
378-
for _, step := range s.Steps {
546+
for _, step := range steps {
379547
for _, v := range step.Env.OuterVars() {
380548
if _, ok := requiredMap[v]; !ok {
381549
requiredMap[v] = struct{}{}

0 commit comments

Comments
 (0)