Skip to content

Commit 8c826bc

Browse files
committed
internal/e2e: first version of the end-to-end module tests
GITHUB_TOKEN is required with the documented permissions for GITHUB_ORG. The test should otherwise work without any other manual steps. A cleanup go-build-ignored program is also included which removes repositories left behind by GITHUB_KEEP=true or failures, which also follows the GITHUB_TOKEN and GITHUB_ORG env vars. We use a separate Go module since these tests use google/go-github, and we don't want the root module to gain extra module dependencies. The test module requires the root module via a replace directive; we avoid using a Go workspace because it's unnecessary for two modules, and because the test module should always use the git repo's CUE module. We place the module in internal/e2e, and not somewhere under cmd/cue, since in the near term we might also want to end-to-end test cue/load. The module doesn't need to be inside an internal directory, since the test/... module path alone already makes it non-importable, but it's a helpful signal to developers that it's not a public library. Note that this commit doesn't hook up the tests in CI yet. A future commit will do that, adding a token secret and an Actions step. Signed-off-by: Daniel Martí <[email protected]> Change-Id: Id65235a058acb78b544265e508c9462856b06b1e Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1171435 Reviewed-by: Roger Peppe <[email protected]> Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
1 parent 3b4e03d commit 8c826bc

File tree

6 files changed

+448
-0
lines changed

6 files changed

+448
-0
lines changed

internal/e2e/cleanup.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright 2023 The CUE Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//go:build ignore
16+
17+
package main
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"log"
23+
"os"
24+
"time"
25+
26+
"github.com/google/go-github/v56/github"
27+
)
28+
29+
var (
30+
githubOrg = envOr("GITHUB_ORG", "cue-labs-modules-testing")
31+
githubToken = envMust("GITHUB_TOKEN")
32+
)
33+
34+
func main() {
35+
client := github.NewClient(nil).WithAuthToken(githubToken)
36+
ctx := context.TODO()
37+
38+
// Starting from the oldest, delete any repositories until a cutoff point.
39+
// For now, during active development, we delete all repos regardless of creation time.
40+
moreRepos:
41+
for {
42+
repos, resp, err := client.Repositories.ListByOrg(ctx, githubOrg, &github.RepositoryListByOrgOptions{
43+
Sort: "created",
44+
Direction: "asc",
45+
ListOptions: github.ListOptions{
46+
Page: 1,
47+
PerPage: 100,
48+
},
49+
})
50+
if err != nil {
51+
panic(err)
52+
}
53+
cutoff := time.Now()
54+
anyDeleted := false
55+
// cutoff := time.Now().Sub(24 * time.Hour)
56+
for _, repo := range repos {
57+
if repo.CreatedAt.After(cutoff) {
58+
break moreRepos // We're past the cutoff point; no more repos to delete.
59+
}
60+
repoName := repo.GetName()
61+
switch repoName {
62+
case "manual-testing":
63+
// We want to keep these repos around.
64+
continue
65+
}
66+
log.Printf("deleting %s/%s", githubOrg, repoName)
67+
if _, err := client.Repositories.Delete(ctx, githubOrg, repoName); err != nil {
68+
panic(err)
69+
}
70+
anyDeleted = true
71+
}
72+
// If we didn't find any more repos to delete, or we're at the last page, stop.
73+
// Note that we always get the first page since we delete repositories from its start.
74+
if !anyDeleted || resp.NextPage == 0 {
75+
break
76+
}
77+
}
78+
}
79+
80+
func envOr(name, fallback string) string {
81+
if s := os.Getenv(name); s != "" {
82+
return s
83+
}
84+
return fallback
85+
}
86+
87+
func envMust(name string) string {
88+
if s := os.Getenv(name); s != "" {
89+
return s
90+
}
91+
panic(fmt.Sprintf("%s must be set", name))
92+
}

internal/e2e/go.mod

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module test/e2e
2+
3+
go 1.20
4+
5+
require (
6+
cuelang.org/go v0.0.0-00010101000000-000000000000
7+
github.com/google/go-github/v56 v56.0.0
8+
github.com/rogpeppe/go-internal v1.11.1-0.20230926105539-32ae33786ecc
9+
github.com/rogpeppe/retry v0.1.0
10+
)
11+
12+
require (
13+
cuelabs.dev/go/oci/ociregistry v0.0.0-20231004130125-2c3ad8a6ecd3 // indirect
14+
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
15+
github.com/emicklei/proto v1.10.0 // indirect
16+
github.com/google/go-querystring v1.1.0 // indirect
17+
github.com/google/uuid v1.2.0 // indirect
18+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
19+
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
20+
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect
21+
github.com/opencontainers/go-digest v1.0.0 // indirect
22+
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
23+
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 // indirect
24+
github.com/spf13/cobra v1.7.0 // indirect
25+
github.com/spf13/pflag v1.0.5 // indirect
26+
github.com/tetratelabs/wazero v1.0.2 // indirect
27+
golang.org/x/mod v0.12.0 // indirect
28+
golang.org/x/net v0.15.0 // indirect
29+
golang.org/x/sys v0.12.0 // indirect
30+
golang.org/x/text v0.13.0 // indirect
31+
golang.org/x/tools v0.13.0 // indirect
32+
gopkg.in/yaml.v3 v3.0.1 // indirect
33+
)
34+
35+
replace cuelang.org/go => ../../

internal/e2e/go.sum

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
cuelabs.dev/go/oci/ociregistry v0.0.0-20231004130125-2c3ad8a6ecd3 h1:jJJsm3hgxosEoI5wXrwt8mv21j23vInE3owbSYKhR2c=
2+
cuelabs.dev/go/oci/ociregistry v0.0.0-20231004130125-2c3ad8a6ecd3/go.mod h1:oqwWmDcccWVB2yC2eCHFNrQR44/AVB7gHOwtBsWMo0g=
3+
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
4+
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
5+
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
6+
github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
7+
github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
8+
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
9+
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
10+
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
11+
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
12+
github.com/google/go-github/v56 v56.0.0 h1:TysL7dMa/r7wsQi44BjqlwaHvwlFlqkK8CtBWCX3gb4=
13+
github.com/google/go-github/v56 v56.0.0/go.mod h1:D8cdcX98YWJvi7TLo7zM4/h8ZTx6u6fwGEkCdisopo0=
14+
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
15+
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
16+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
17+
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
18+
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
19+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
20+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
21+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
22+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
23+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
24+
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
25+
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
26+
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
27+
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
28+
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
29+
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
30+
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
31+
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
32+
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
33+
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 h1:sadMIsgmHpEOGbUs6VtHBXRR1OHevnj7hLx9ZcdNGW4=
34+
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
35+
github.com/rogpeppe/go-internal v1.11.1-0.20230926105539-32ae33786ecc h1:mutztH6OmvFf2MGH0cqacv/FCFLkJn/rz3i7E/VWfm0=
36+
github.com/rogpeppe/go-internal v1.11.1-0.20230926105539-32ae33786ecc/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
37+
github.com/rogpeppe/retry v0.1.0 h1:6km4oqeZcFrnhx+PCPg/YxV3fnTdROBNVlSl8Pe/ztU=
38+
github.com/rogpeppe/retry v0.1.0/go.mod h1:/PtRtl9qXn+Pv5S4wN+Y5nusihQeI1PJ9U7KDcKzuvI=
39+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
40+
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
41+
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
42+
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
43+
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
44+
github.com/tetratelabs/wazero v1.0.2 h1:lpwL5zczFHk2mxKur98035Gig+Z3vd9JURk6lUdZxXY=
45+
github.com/tetratelabs/wazero v1.0.2/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
46+
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
47+
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
48+
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
49+
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
50+
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
51+
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
52+
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
53+
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
54+
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
55+
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
56+
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
57+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
58+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
59+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
60+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
61+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/e2e/script_test.go

+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// Copyright 2023 The CUE Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package e2e_test
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"net/http"
21+
"os"
22+
"os/exec"
23+
"path/filepath"
24+
"testing"
25+
"time"
26+
27+
"github.com/google/go-github/v56/github"
28+
"github.com/rogpeppe/go-internal/testscript"
29+
"github.com/rogpeppe/retry"
30+
)
31+
32+
func TestMain(m *testing.M) {
33+
cachedGobin := os.Getenv("CUE_CACHED_GOBIN")
34+
if cachedGobin == "" {
35+
// Install the cmd/cue version into a cached GOBIN so we can reuse it.
36+
// TODO: use "go tool cue" once we can rely on Go 1.22's tool dependency tracking in go.mod.
37+
// See: https://go.dev/issue/48429
38+
cacheDir, err := os.UserCacheDir()
39+
if err != nil {
40+
panic(err)
41+
}
42+
cachedGobin = filepath.Join(cacheDir, "cue-e2e-gobin")
43+
cmd := exec.Command("go", "install", "cuelang.org/go/cmd/cue")
44+
cmd.Env = append(cmd.Environ(), "GOBIN="+cachedGobin)
45+
out, err := cmd.CombinedOutput()
46+
if err != nil {
47+
panic(fmt.Errorf("%v: %s", err, out))
48+
}
49+
os.Setenv("CUE_CACHED_GOBIN", cachedGobin)
50+
}
51+
52+
os.Exit(testscript.RunMain(m, map[string]func() int{
53+
"cue": func() int {
54+
// Note that we could avoid this wrapper entirely by setting PATH,
55+
// since TestMain sets up a single cue binary in a GOBIN directory,
56+
// but that may change at any point, or we might just switch to "go tool cue".
57+
cmd := exec.Command(filepath.Join(cachedGobin, "cue"), os.Args[1:]...)
58+
cmd.Stdin = os.Stdin
59+
cmd.Stdout = os.Stdout
60+
cmd.Stderr = os.Stderr
61+
if err := cmd.Run(); err != nil {
62+
if err, ok := err.(*exec.ExitError); ok {
63+
return err.ExitCode()
64+
}
65+
fmt.Fprintln(os.Stderr, err)
66+
return 1
67+
}
68+
return 0
69+
},
70+
}))
71+
}
72+
73+
var (
74+
// githubOrg is a GitHub organization where the "CUE Module Publisher"
75+
// GitHub App has been installed on all repositories.
76+
// This is necessary since we will create a new repository per test,
77+
// and there's no way to easily install the app on each repo via the API.
78+
githubOrg = envOr("GITHUB_ORG", "cue-labs-modules-testing")
79+
// githubToken should have read and write access to repository
80+
// administration and contents within githubOrg,
81+
// to be able to create repositories under the org and git push to them.
82+
githubToken = envMust("GITHUB_TOKEN")
83+
// githubKeep leaves the newly created repo around when set to true.
84+
githubKeep = envOr("GITHUB_KEEP", "false")
85+
)
86+
87+
func TestScript(t *testing.T) {
88+
p := testscript.Params{
89+
Dir: filepath.Join("testdata", "script"),
90+
RequireExplicitExec: true,
91+
Setup: func(env *testscript.Env) error {
92+
env.Setenv("CUE_EXPERIMENT", "modules")
93+
env.Setenv("CUE_REGISTRY", "registry.cue.works")
94+
env.Setenv("CUE_CACHED_GOBIN", os.Getenv("CUE_CACHED_GOBIN"))
95+
env.Setenv("GITHUB_TOKEN", githubToken) // needed for "git push"
96+
return nil
97+
},
98+
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
99+
// create-github-repo creates a unique repository under githubOrg
100+
// and sets $MODULE to its resulting module path.
101+
"create-github-repo": func(ts *testscript.TestScript, neg bool, args []string) {
102+
if neg || len(args) > 0 {
103+
ts.Fatalf("usage: create-github-repo")
104+
}
105+
// TODO: name the repo after ts.Name once the API lands
106+
// TODO: add a short random suffix to prevent time collisions
107+
repoName := time.Now().UTC().Format("2006-01-02.15-04-05")
108+
client := github.NewClient(nil).WithAuthToken(githubToken)
109+
ctx := context.TODO()
110+
111+
repo := &github.Repository{
112+
Name: github.String(repoName),
113+
}
114+
_, _, err := client.Repositories.Create(ctx, githubOrg, repo)
115+
ts.Check(err)
116+
117+
// Unless GITHUB_KEEP=true is set, delete the repo when we finish.
118+
//
119+
// TODO: It might be useful to leave the repo around when the test fails.
120+
// We would need testscript.TestScript to expose T.Failed for this.
121+
ts.Defer(func() {
122+
if githubKeep == "true" {
123+
return
124+
}
125+
_, err := client.Repositories.Delete(ctx, githubOrg, repoName)
126+
ts.Check(err)
127+
})
128+
129+
ts.Setenv("MODULE", fmt.Sprintf("github.com/%s/%s", githubOrg, repoName))
130+
},
131+
// env-fill rewrites its argument files to replace any environment variable
132+
// references with their values, using the same algorithm as cmpenv.
133+
"env-fill": func(ts *testscript.TestScript, neg bool, args []string) {
134+
if neg || len(args) == 0 {
135+
ts.Fatalf("usage: env-fill args...")
136+
}
137+
for _, arg := range args {
138+
path := ts.MkAbs(arg)
139+
data := ts.ReadFile(path)
140+
data = tsExpand(ts, data)
141+
ts.Check(os.WriteFile(path, []byte(data), 0o666))
142+
}
143+
},
144+
// cue-mod-wait waits for a CUE module to exist in a registry for up to 20s.
145+
// Since this is easily done via an HTTP HEAD request, an OCI client isn't necessary.
146+
"cue-mod-wait": func(ts *testscript.TestScript, neg bool, args []string) {
147+
if neg || len(args) > 0 {
148+
ts.Fatalf("usage: cue-mod-wait")
149+
}
150+
manifest := tsExpand(ts, "https://${CUE_REGISTRY}/v2/${MODULE}/manifests/${VERSION}")
151+
retries := retry.Strategy{
152+
Delay: 10 * time.Millisecond,
153+
MaxDelay: time.Second,
154+
MaxDuration: 20 * time.Second,
155+
}
156+
for it := retries.Start(); it.Next(nil); {
157+
resp, err := http.Head(manifest)
158+
ts.Check(err)
159+
if resp.StatusCode == http.StatusOK {
160+
return
161+
}
162+
}
163+
ts.Fatalf("timed out waiting for module")
164+
},
165+
},
166+
}
167+
testscript.Run(t, p)
168+
}
169+
170+
func envOr(name, fallback string) string {
171+
if s := os.Getenv(name); s != "" {
172+
return s
173+
}
174+
return fallback
175+
}
176+
177+
func envMust(name string) string {
178+
if s := os.Getenv(name); s != "" {
179+
return s
180+
}
181+
panic(fmt.Sprintf("%s must be set", name))
182+
}
183+
184+
func tsExpand(ts *testscript.TestScript, s string) string {
185+
return os.Expand(s, func(key string) string {
186+
return ts.Getenv(key)
187+
})
188+
}

0 commit comments

Comments
 (0)