Skip to content

Commit 60b9db6

Browse files
authored
Mock sourcetool package, add integration tests (#222)
* Move sourctool pkg options to own pkg Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * tool: Use new opts packag Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * Generate tool implementation fakes Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * Add mock verifier and makefile target Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * Add sourcetool integrationt ests Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> --------- Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]>
1 parent 8947bf7 commit 60b9db6

File tree

13 files changed

+1166
-132
lines changed

13 files changed

+1166
-132
lines changed

.github/workflows/go-test.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,9 @@ jobs:
2121
check-latest: true
2222

2323
- name: Run Go tests
24-
run: go test ./sourcetool/...
24+
run: |
25+
go test ./sourcetool/...
26+
27+
- name: Check generated fakes
28+
run: |
29+
hack/verify-fakes.sh

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.PHONY: fakes
2+
fakes: ## Rebuild the implementation fakes
3+
go generate ./sourcetool/...
4+

hack/common.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
3+
function exit_with_msg() {
4+
echo "${1}"
5+
exit 1
6+
}

hack/verify-fakes.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env bash
2+
3+
set -o errexit
4+
set -o nounset
5+
set -o pipefail
6+
7+
set -o xtrace
8+
9+
source hack/common.sh
10+
11+
make fakes
12+
git diff --exit-code || exit_with_msg "Fakes are not up to date. Please run 'make fakes' and commit the result"

sourcetool/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/go-git/go-git/v5 v5.16.2
1111
github.com/google/go-github/v69 v69.2.0
1212
github.com/in-toto/attestation v1.1.2
13+
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2
1314
github.com/migueleliasweb/go-github-mock v1.4.0
1415
github.com/sigstore/sigstore-go v1.0.0
1516
github.com/sirupsen/logrus v1.9.3
@@ -124,6 +125,7 @@ require (
124125
golang.org/x/term v0.32.0 // indirect
125126
golang.org/x/text v0.26.0 // indirect
126127
golang.org/x/time v0.11.0 // indirect
128+
golang.org/x/tools v0.33.0 // indirect
127129
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
128130
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
129131
google.golang.org/grpc v1.72.0 // indirect

sourcetool/go.sum

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
286286
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
287287
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
288288
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
289+
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 h1:yVCLo4+ACVroOEr4iFU1iH46Ldlzz2rTuu18Ra7M8sU=
290+
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ=
289291
github.com/migueleliasweb/go-github-mock v1.4.0 h1:pQ6K8r348m2q79A8Khb0PbEeNQV7t3h1xgECV+jNpXk=
290292
github.com/migueleliasweb/go-github-mock v1.4.0/go.mod h1:/DUmhXkxrgVlDOVBqGoUXkV4w0ms5n1jDQHotYm135o=
291293
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -298,8 +300,8 @@ github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5
298300
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw=
299301
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
300302
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
301-
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
302-
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
303+
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
304+
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
303305
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
304306
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
305307
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
@@ -334,6 +336,8 @@ github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGq
334336
github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk=
335337
github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4=
336338
github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k=
339+
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
340+
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
337341
github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc=
338342
github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw=
339343
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
@@ -475,6 +479,8 @@ golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
475479
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
476480
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
477481
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
482+
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
483+
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
478484
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
479485
google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
480486
google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=

sourcetool/internal/tools.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//go:build tools
2+
3+
// This file forces the import of modules used for build scripts and tests
4+
// not directly used by the codebase.
5+
6+
package internal
7+
8+
import (
9+
_ "github.com/maxbrunsfeld/counterfeiter/v6"
10+
)

sourcetool/pkg/sourcetool/implementation.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/ghcontrol"
2424
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/policy"
2525
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/slsa"
26+
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/sourcetool/options"
2627
)
2728

2829
const (
@@ -61,22 +62,24 @@ jobs:
6162
`
6263

6364
// toolImplementation defines the mockable implementation of source tool
65+
//
66+
//counterfeiter:generate . toolImplementation
6467
type toolImplementation interface {
65-
GetActiveControls(*Options) (slsa.Controls, error)
66-
EnsureDefaults(opts *Options) error
67-
VerifyOptionsForFullOnboard(*Options) error
68-
CreateRepoRuleset(*Options) error
69-
CheckWorkflowFork(*Options) error
70-
CreateWorkflowPR(*Options) error
71-
CheckPolicyFork(*Options) error
72-
CreatePolicyPR(*Options) error
73-
CheckForks(*Options) error
68+
GetActiveControls(*options.Options) (slsa.Controls, error)
69+
EnsureDefaults(opts *options.Options) error
70+
VerifyOptionsForFullOnboard(*options.Options) error
71+
CreateRepoRuleset(*options.Options) error
72+
CheckWorkflowFork(*options.Options) error
73+
CreateWorkflowPR(*options.Options) error
74+
CheckPolicyFork(*options.Options) error
75+
CreatePolicyPR(*options.Options) error
76+
CheckForks(*options.Options) error
7477
}
7578

7679
type defaultToolImplementation struct{}
7780

7881
// GetActiveControls returns a slsa.Controls with the active controls on a repo
79-
func (impl *defaultToolImplementation) GetActiveControls(opts *Options) (slsa.Controls, error) {
82+
func (impl *defaultToolImplementation) GetActiveControls(opts *options.Options) (slsa.Controls, error) {
8083
ctx := context.Background()
8184

8285
if err := opts.EnsureBranch(); err != nil {
@@ -122,7 +125,7 @@ func (impl *defaultToolImplementation) GetActiveControls(opts *Options) (slsa.Co
122125

123126
// EnsureBranch makes sure the manager has a defined branch, looking up the
124127
// default if it needs to
125-
func (impl *defaultToolImplementation) EnsureDefaults(opts *Options) error {
128+
func (impl *defaultToolImplementation) EnsureDefaults(opts *options.Options) error {
126129
if t := os.Getenv(tokenVar); t == "" {
127130
return fmt.Errorf("$%s environment variable not set", tokenVar)
128131
}
@@ -142,7 +145,7 @@ func (impl *defaultToolImplementation) EnsureDefaults(opts *Options) error {
142145
return nil
143146
}
144147

145-
func getUserData(opts *Options) error {
148+
func getUserData(opts *options.Options) error {
146149
if opts.UserForkOrg != "" {
147150
return nil
148151
}
@@ -184,7 +187,7 @@ func getUserData(opts *Options) error {
184187
}
185188

186189
// VerifyOptions checks options are in good shape to run
187-
func (impl *defaultToolImplementation) VerifyOptionsForFullOnboard(opts *Options) error {
190+
func (impl *defaultToolImplementation) VerifyOptionsForFullOnboard(opts *options.Options) error {
188191
errs := []error{}
189192
if opts.Repo == "" {
190193
errs = append(errs, errors.New("no repository name defined"))
@@ -217,7 +220,7 @@ func (impl *defaultToolImplementation) VerifyOptionsForFullOnboard(opts *Options
217220
return errors.Join(errs...)
218221
}
219222

220-
func (impl *defaultToolImplementation) CreateRepoRuleset(opts *Options) error {
223+
func (impl *defaultToolImplementation) CreateRepoRuleset(opts *options.Options) error {
221224
// Ensure we have branch and defaults
222225
if err := opts.EnsureBranch(); err != nil {
223226
return err
@@ -240,7 +243,7 @@ func (impl *defaultToolImplementation) CreateRepoRuleset(opts *Options) error {
240243

241244
// CheckWorkflowFork verifies that the user has a fork of the repository
242245
// we are configuring.
243-
func (impl *defaultToolImplementation) CheckWorkflowFork(opts *Options) error {
246+
func (impl *defaultToolImplementation) CheckWorkflowFork(opts *options.Options) error {
244247
userForkOrg := opts.UserForkOrg
245248
userForkRepo := opts.Repo // For now we only support forks with the same name
246249

@@ -257,7 +260,7 @@ func (impl *defaultToolImplementation) CheckWorkflowFork(opts *Options) error {
257260

258261
// CreateWorkflowPR creates the pull request to add the provenance workflow
259262
// to the repository
260-
func (impl *defaultToolImplementation) CreateWorkflowPR(opts *Options) error {
263+
func (impl *defaultToolImplementation) CreateWorkflowPR(opts *options.Options) error {
261264
// Branchname to be created on the user's fork
262265
branchname := fmt.Sprintf("slsa-source-workflow-%d", time.Now().Unix())
263266

@@ -328,7 +331,7 @@ func (impl *defaultToolImplementation) CreateWorkflowPR(opts *Options) error {
328331
return nil
329332
}
330333

331-
func (impl *defaultToolImplementation) CheckPolicyFork(opts *Options) error {
334+
func (impl *defaultToolImplementation) CheckPolicyFork(opts *options.Options) error {
332335
policyOrg, policyRepo, ok := strings.Cut(opts.PolicyRepo, "/")
333336
if !ok || policyRepo == "" {
334337
return fmt.Errorf("unable to parse policy repository slug")
@@ -349,7 +352,7 @@ func (impl *defaultToolImplementation) CheckPolicyFork(opts *Options) error {
349352
}
350353

351354
// CreatePolicyPR creates a pull request to push the policy
352-
func (impl *defaultToolImplementation) CreatePolicyPR(opts *Options) error {
355+
func (impl *defaultToolImplementation) CreatePolicyPR(opts *options.Options) error {
353356
// Branchname to be created on the user's fork
354357
branchname := fmt.Sprintf("slsa-source-policy-%d", time.Now().Unix())
355358

@@ -425,7 +428,7 @@ func (impl *defaultToolImplementation) CreatePolicyPR(opts *Options) error {
425428
}
426429

427430
// CheckForks checks that the user has forks of the required repositories
428-
func (impl *defaultToolImplementation) CheckForks(opts *Options) error {
431+
func (impl *defaultToolImplementation) CheckForks(opts *options.Options) error {
429432
errs := []error{}
430433
if err := impl.CheckPolicyFork(opts); err != nil {
431434
errs = append(errs, err)

sourcetool/pkg/sourcetool/options.go

Lines changed: 15 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,53 @@
11
package sourcetool
22

3-
import (
4-
"context"
5-
"errors"
6-
"fmt"
3+
import "github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/sourcetool/options"
74

8-
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/ghcontrol"
9-
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/policy"
10-
)
11-
12-
type Options struct {
13-
Repo string
14-
Owner string
15-
Branch string
16-
Commit string
17-
18-
// Organization to look for slsa and user forks
19-
UserForkOrg string
20-
Enforce bool
21-
UseSSH bool
22-
UpdateRepo bool
23-
24-
// PolicyRepo is the repository where the policies are stored
25-
PolicyRepo string
26-
}
27-
28-
// DefaultOptions holds the default options the tool initializes with
29-
var DefaultOptions = Options{
30-
PolicyRepo: fmt.Sprintf("%s/%s", policy.SourcePolicyRepoOwner, policy.SourcePolicyRepo),
31-
UseSSH: true,
32-
}
33-
34-
// GetGitHubConnection creates a new github connection to the repository
35-
// defined in the options set.
36-
func (o *Options) GetGitHubConnection() (*ghcontrol.GitHubConnection, error) {
37-
if o.Owner == "" {
38-
return nil, errors.New("owner not set")
39-
}
40-
if o.Repo == "" {
41-
return nil, errors.New("repository not set")
42-
}
43-
44-
return ghcontrol.NewGhConnection(o.Owner, o.Repo, ghcontrol.BranchToFullRef(o.Branch)), nil
45-
}
46-
47-
// EnsureCommit checks the options have a commit sha defined. If not, then
48-
// the latest commit from the loaded branch is read from the GitHub API.
49-
func (o *Options) EnsureCommit() error {
50-
if o.Commit != "" {
51-
return nil
52-
}
53-
54-
if o.Branch == "" {
55-
return errors.New("unable to fetch latest commit, no branch defined")
56-
}
57-
58-
ghc, err := o.GetGitHubConnection()
59-
if err != nil {
60-
return fmt.Errorf("getting GitHub connection: %w", err)
61-
}
62-
63-
commit, err := ghc.GetLatestCommit(context.Background(), o.Branch)
64-
if err != nil {
65-
return fmt.Errorf("fetching latest commit: %w", err)
66-
}
67-
o.Commit = commit
68-
return nil
69-
}
70-
71-
// EnsureBranch checks that the options set has a branch set and if not, it
72-
// reads the repository's default branch from the GitHub API.
73-
func (o *Options) EnsureBranch() error {
74-
if o.Branch != "" {
75-
return nil
76-
}
77-
78-
ghc, err := o.GetGitHubConnection()
79-
if err != nil {
80-
return fmt.Errorf("getting GitHub connection: %w", err)
81-
}
82-
83-
branch, err := ghc.GetDefaultBranch(context.Background())
84-
if err != nil {
85-
return fmt.Errorf("fetching default branch: %w", err)
86-
}
87-
o.Branch = branch
88-
return nil
89-
}
90-
91-
type ooFn func(*Options) error
92-
93-
func WithRepo(repo string) ooFn {
94-
return func(o *Options) error {
5+
func WithRepo(repo string) options.Fn {
6+
return func(o *options.Options) error {
957
// TODO(puerco): Validate repo string
968
o.Repo = repo
979
return nil
9810
}
9911
}
10012

101-
func WithOwner(repo string) ooFn {
102-
return func(o *Options) error {
13+
func WithOwner(repo string) options.Fn {
14+
return func(o *options.Options) error {
10315
// TODO(puerco): Validate org string
10416
o.Owner = repo
10517
return nil
10618
}
10719
}
10820

109-
func WithBranch(branch string) ooFn {
110-
return func(o *Options) error {
21+
func WithBranch(branch string) options.Fn {
22+
return func(o *options.Options) error {
11123
o.Branch = branch
11224
return nil
11325
}
11426
}
11527

116-
func WithCommit(commit string) ooFn {
117-
return func(o *Options) error {
28+
func WithCommit(commit string) options.Fn {
29+
return func(o *options.Options) error {
11830
o.Commit = commit
11931
return nil
12032
}
12133
}
12234

123-
func WithEnforce(enforce bool) ooFn {
124-
return func(o *Options) error {
35+
func WithEnforce(enforce bool) options.Fn {
36+
return func(o *options.Options) error {
12537
o.Enforce = enforce
12638
return nil
12739
}
12840
}
12941

130-
func WithUserForkOrg(org string) ooFn {
131-
return func(o *Options) error {
42+
func WithUserForkOrg(org string) options.Fn {
43+
return func(o *options.Options) error {
13244
o.UserForkOrg = org
13345
return nil
13446
}
13547
}
13648

137-
func WithPolicyRepo(slug string) ooFn {
138-
return func(o *Options) error {
49+
func WithPolicyRepo(slug string) options.Fn {
50+
return func(o *options.Options) error {
13951
o.PolicyRepo = slug
14052
return nil
14153
}

0 commit comments

Comments
 (0)