Skip to content

Commit b91ccff

Browse files
committed
vendor in lib from sourcegraph
1 parent ae33727 commit b91ccff

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+8183
-0
lines changed

lib/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Vendored lib packages
2+
3+
This directory contains vendored packages from `github.com/sourcegraph/sourcegraph/lib`.
4+
5+
## Source
6+
7+
- **Repository**: https://github.com/sourcegraph/sourcegraph
8+
- **Commit**: 2ee2b8e77de9663b08ce5f6e5a2c7d2217ce721a
9+
- **Date**: 2025-11-17 19:49:42 -0800
10+
11+
## Updating
12+
13+
To update these vendored packages, run:
14+
15+
```bash
16+
./dev/vendor-lib.sh
17+
```
18+
19+
The script will:
20+
1. Validate that `../sourcegraph` is on the `main` branch with no uncommitted changes
21+
2. Discover all direct and transitive dependencies on `lib` packages
22+
3. Copy only the needed packages
23+
4. Update this README with the new commit information
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Package accesstoken is exposed in lib/ for usage in src-cli
2+
package accesstoken
3+
4+
import (
5+
"sync"
6+
7+
"github.com/grafana/regexp" // avoid pulling in internal lazyregexp package
8+
9+
"github.com/sourcegraph/sourcegraph/lib/errors"
10+
)
11+
12+
var makePersonalAccessTokenRegex = sync.OnceValue[*regexp.Regexp](func() *regexp.Regexp {
13+
return regexp.MustCompile("^(?:(?:sgp_|sgph_)(?:[a-zA-Z0-9]+_)?)?([a-fA-F0-9]{40})$")
14+
})
15+
16+
// ParsePersonalAccessToken parses a personal access token to remove prefixes and extract the <token> that is stored in the database
17+
// Personal access tokens can take several forms:
18+
// - <token>
19+
// - sgp_<token>
20+
// - sgp_<instance-identifier>_<token>
21+
func ParsePersonalAccessToken(token string) (string, error) {
22+
tokenMatches := makePersonalAccessTokenRegex().FindStringSubmatch(token)
23+
if len(tokenMatches) <= 1 {
24+
return "", errors.New("invalid token format")
25+
}
26+
tokenValue := tokenMatches[1]
27+
28+
return tokenValue, nil
29+
}

lib/api/version_check.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package api
2+
3+
import (
4+
"github.com/grafana/regexp"
5+
6+
"github.com/Masterminds/semver"
7+
)
8+
9+
// BuildDateRegex matches the build date in a Sourcegraph version string.
10+
var BuildDateRegex = regexp.MustCompile(`\d+_(\d{4}-\d{2}-\d{2})_(\d+\.\d+)?-?[a-z0-9]{7,}(_patch)?$`)
11+
12+
// CheckSourcegraphVersion checks if the given version satisfies the given constraint.
13+
// NOTE: A version with a prerelease suffix (e.g. the "-rc.3" of "3.35.1-rc.3") is not
14+
// considered by semver to satisfy a constraint without a prerelease suffix, regardless of
15+
// whether or not the major/minor/patch version is greater than or equal to that of the
16+
// constraint.
17+
//
18+
// For example, the version "3.35.1-rc.3" is not considered to satisfy the constraint ">=
19+
// 3.23.0". This is likely not the expected outcome. However, the same version IS
20+
// considered to satisfy the constraint "3.23.0-0". Thus, it is recommended to pass a
21+
// constraint with a minimum prerelease version suffix attached if comparisons to
22+
// prerelease versions are ever expected. See
23+
// https://github.com/Masterminds/semver#working-with-prerelease-versions for more.
24+
func CheckSourcegraphVersion(version, constraint, minDate string) (bool, error) {
25+
if version == "dev" || version == "0.0.0+dev" {
26+
return true, nil
27+
}
28+
29+
// Since we don't actually care about the abbreviated commit hash at the end of the
30+
// version string, we match on 7 or more characters. Currently, the Sourcegraph version
31+
// is expected to return 12:
32+
// https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/blob/dev/ci/internal/ci/config.go?L96.
33+
matches := BuildDateRegex.FindStringSubmatch(version)
34+
if len(matches) > 1 {
35+
return matches[1] >= minDate, nil
36+
}
37+
38+
c, err := semver.NewConstraint(constraint)
39+
if err != nil {
40+
return false, nil
41+
}
42+
43+
v, err := semver.NewVersion(version)
44+
if err != nil {
45+
return false, err
46+
}
47+
48+
return c.Check(v), nil
49+
}

lib/batches/batch_spec.go

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
package batches
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/sourcegraph/sourcegraph/lib/batches/env"
8+
"github.com/sourcegraph/sourcegraph/lib/batches/overridable"
9+
"github.com/sourcegraph/sourcegraph/lib/batches/schema"
10+
"github.com/sourcegraph/sourcegraph/lib/batches/template"
11+
"github.com/sourcegraph/sourcegraph/lib/batches/yaml"
12+
"github.com/sourcegraph/sourcegraph/lib/errors"
13+
)
14+
15+
// Some general notes about the struct definitions below.
16+
//
17+
// 1. They map _very_ closely to the batch spec JSON schema. We don't
18+
// auto-generate the types because we need YAML support (more on that in a
19+
// moment) and because no generator can currently handle oneOf fields
20+
// gracefully in Go, but that's a potential future enhancement.
21+
//
22+
// 2. Fields are tagged with _both_ JSON and YAML tags. Internally, the JSON
23+
// schema library needs to be able to marshal the struct to JSON for
24+
// validation, so we need to ensure that we're generating the right JSON to
25+
// represent the YAML that we unmarshalled.
26+
//
27+
// 3. All JSON tags include omitempty so that the schema validation can pick up
28+
// omitted fields. The other option here was to have everything unmarshal to
29+
// pointers, which is ugly and inefficient.
30+
31+
type BatchSpec struct {
32+
Version int `json:"version,omitempty" yaml:"version"`
33+
Name string `json:"name,omitempty" yaml:"name"`
34+
Description string `json:"description,omitempty" yaml:"description"`
35+
On []OnQueryOrRepository `json:"on,omitempty" yaml:"on"`
36+
Workspaces []WorkspaceConfiguration `json:"workspaces,omitempty" yaml:"workspaces"`
37+
Steps []Step `json:"steps,omitempty" yaml:"steps"`
38+
TransformChanges *TransformChanges `json:"transformChanges,omitempty" yaml:"transformChanges,omitempty"`
39+
ImportChangesets []ImportChangeset `json:"importChangesets,omitempty" yaml:"importChangesets"`
40+
ChangesetTemplate *ChangesetTemplate `json:"changesetTemplate,omitempty" yaml:"changesetTemplate"`
41+
}
42+
43+
type ChangesetTemplate struct {
44+
Title string `json:"title,omitempty" yaml:"title"`
45+
Body string `json:"body,omitempty" yaml:"body"`
46+
Branch string `json:"branch,omitempty" yaml:"branch"`
47+
Fork *bool `json:"fork,omitempty" yaml:"fork"`
48+
Commit ExpandedGitCommitDescription `json:"commit" yaml:"commit"`
49+
Published *overridable.BoolOrString `json:"published" yaml:"published"`
50+
}
51+
52+
type GitCommitAuthor struct {
53+
Name string `json:"name" yaml:"name"`
54+
Email string `json:"email" yaml:"email"`
55+
}
56+
57+
type ExpandedGitCommitDescription struct {
58+
Message string `json:"message,omitempty" yaml:"message"`
59+
Author *GitCommitAuthor `json:"author,omitempty" yaml:"author"`
60+
}
61+
62+
type ImportChangeset struct {
63+
Repository string `json:"repository" yaml:"repository"`
64+
ExternalIDs []any `json:"externalIDs" yaml:"externalIDs"`
65+
}
66+
67+
type WorkspaceConfiguration struct {
68+
RootAtLocationOf string `json:"rootAtLocationOf,omitempty" yaml:"rootAtLocationOf"`
69+
In string `json:"in,omitempty" yaml:"in"`
70+
OnlyFetchWorkspace bool `json:"onlyFetchWorkspace,omitempty" yaml:"onlyFetchWorkspace"`
71+
}
72+
73+
type OnQueryOrRepository struct {
74+
RepositoriesMatchingQuery string `json:"repositoriesMatchingQuery,omitempty" yaml:"repositoriesMatchingQuery"`
75+
Repository string `json:"repository,omitempty" yaml:"repository"`
76+
Branch string `json:"branch,omitempty" yaml:"branch"`
77+
Branches []string `json:"branches,omitempty" yaml:"branches"`
78+
}
79+
80+
var ErrConflictingBranches = NewValidationError(errors.New("both branch and branches specified"))
81+
82+
func (oqor *OnQueryOrRepository) GetBranches() ([]string, error) {
83+
if oqor.Branch != "" {
84+
if len(oqor.Branches) > 0 {
85+
return nil, ErrConflictingBranches
86+
}
87+
return []string{oqor.Branch}, nil
88+
}
89+
return oqor.Branches, nil
90+
}
91+
92+
type Step struct {
93+
Run string `json:"run,omitempty" yaml:"run"`
94+
Container string `json:"container,omitempty" yaml:"container"`
95+
Env env.Environment `json:"env" yaml:"env"`
96+
Files map[string]string `json:"files,omitempty" yaml:"files,omitempty"`
97+
Outputs Outputs `json:"outputs,omitempty" yaml:"outputs,omitempty"`
98+
Mount []Mount `json:"mount,omitempty" yaml:"mount,omitempty"`
99+
If any `json:"if,omitempty" yaml:"if,omitempty"`
100+
}
101+
102+
func (s *Step) IfCondition() string {
103+
switch v := s.If.(type) {
104+
case bool:
105+
if v {
106+
return "true"
107+
}
108+
return "false"
109+
case string:
110+
return v
111+
default:
112+
return ""
113+
}
114+
}
115+
116+
type Outputs map[string]Output
117+
118+
type Output struct {
119+
Value string `json:"value,omitempty" yaml:"value,omitempty"`
120+
Format string `json:"format,omitempty" yaml:"format,omitempty"`
121+
}
122+
123+
type TransformChanges struct {
124+
Group []Group `json:"group,omitempty" yaml:"group"`
125+
}
126+
127+
type Group struct {
128+
Directory string `json:"directory,omitempty" yaml:"directory"`
129+
Branch string `json:"branch,omitempty" yaml:"branch"`
130+
Repository string `json:"repository,omitempty" yaml:"repository"`
131+
}
132+
133+
type Mount struct {
134+
Mountpoint string `json:"mountpoint" yaml:"mountpoint"`
135+
Path string `json:"path" yaml:"path"`
136+
}
137+
138+
func ParseBatchSpec(data []byte) (*BatchSpec, error) {
139+
return parseBatchSpec(schema.BatchSpecJSON, data)
140+
}
141+
142+
func parseBatchSpec(schema string, data []byte) (*BatchSpec, error) {
143+
var spec BatchSpec
144+
if err := yaml.UnmarshalValidate(schema, data, &spec); err != nil {
145+
var multiErr errors.MultiError
146+
if errors.As(err, &multiErr) {
147+
var newMultiError error
148+
149+
for _, e := range multiErr.Errors() {
150+
// In case of `name` we try to make the error message more user-friendly.
151+
if strings.Contains(e.Error(), "name: Does not match pattern") {
152+
newMultiError = errors.Append(newMultiError, NewValidationError(errors.Newf("The batch change name can only contain word characters, dots and dashes. No whitespace or newlines allowed.")))
153+
} else {
154+
newMultiError = errors.Append(newMultiError, NewValidationError(e))
155+
}
156+
}
157+
158+
return nil, newMultiError
159+
}
160+
161+
return nil, err
162+
}
163+
164+
var errs error
165+
166+
if len(spec.Steps) != 0 && spec.ChangesetTemplate == nil {
167+
errs = errors.Append(errs, NewValidationError(errors.New("batch spec includes steps but no changesetTemplate")))
168+
}
169+
170+
for i, step := range spec.Steps {
171+
for _, mount := range step.Mount {
172+
if strings.Contains(mount.Path, invalidMountCharacters) {
173+
errs = errors.Append(errs, NewValidationError(errors.Newf("step %d mount path contains invalid characters", i+1)))
174+
}
175+
if strings.Contains(mount.Mountpoint, invalidMountCharacters) {
176+
errs = errors.Append(errs, NewValidationError(errors.Newf("step %d mount mountpoint contains invalid characters", i+1)))
177+
}
178+
}
179+
}
180+
181+
return &spec, errs
182+
}
183+
184+
const invalidMountCharacters = ","
185+
186+
func (on *OnQueryOrRepository) String() string {
187+
if on.RepositoriesMatchingQuery != "" {
188+
return on.RepositoriesMatchingQuery
189+
} else if on.Repository != "" {
190+
return "repository:" + on.Repository
191+
}
192+
193+
return fmt.Sprintf("%v", *on)
194+
}
195+
196+
// BatchSpecValidationError is returned when parsing/using values from the batch spec failed.
197+
type BatchSpecValidationError struct {
198+
err error
199+
}
200+
201+
func NewValidationError(err error) BatchSpecValidationError {
202+
return BatchSpecValidationError{err}
203+
}
204+
205+
func (e BatchSpecValidationError) Error() string {
206+
return e.err.Error()
207+
}
208+
209+
func IsValidationError(err error) bool {
210+
return errors.HasType[*BatchSpecValidationError](err)
211+
}
212+
213+
// SkippedStepsForRepo calculates the steps required to run on the given repo.
214+
func SkippedStepsForRepo(spec *BatchSpec, repoName string, fileMatches []string) (skipped map[int]struct{}, err error) {
215+
skipped = map[int]struct{}{}
216+
217+
for idx, step := range spec.Steps {
218+
// If no if condition is set the step is always run.
219+
if step.IfCondition() == "" {
220+
continue
221+
}
222+
223+
batchChange := template.BatchChangeAttributes{
224+
Name: spec.Name,
225+
Description: spec.Description,
226+
}
227+
// TODO: This step ctx is incomplete, is this allowed?
228+
// We can at least optimize further here and do more static evaluation
229+
// when we have a cached result for the previous step.
230+
stepCtx := &template.StepContext{
231+
Repository: template.Repository{
232+
Name: repoName,
233+
FileMatches: fileMatches,
234+
},
235+
BatchChange: batchChange,
236+
}
237+
static, boolVal, err := template.IsStaticBool(step.IfCondition(), stepCtx)
238+
if err != nil {
239+
return nil, err
240+
}
241+
242+
if static && !boolVal {
243+
skipped[idx] = struct{}{}
244+
}
245+
}
246+
247+
return skipped, nil
248+
}
249+
250+
// RequiredEnvVars inspects all steps for outer environment variables used and
251+
// compiles a deduplicated list from those.
252+
func (s *BatchSpec) RequiredEnvVars() []string {
253+
requiredMap := map[string]struct{}{}
254+
required := []string{}
255+
for _, step := range s.Steps {
256+
for _, v := range step.Env.OuterVars() {
257+
if _, ok := requiredMap[v]; !ok {
258+
requiredMap[v] = struct{}{}
259+
required = append(required, v)
260+
}
261+
}
262+
}
263+
return required
264+
}

0 commit comments

Comments
 (0)