Skip to content

Commit 48b4731

Browse files
authored
Add an option to output environment variables (#1)
Adds an `--output env` option to emit `SHARD_TESTS` and `SHARD_PATHS` environment variables. Example GitHub usage: ``` go run github.com/blampe/shard@latest --total $(TOTAL) --index $(INDEX) --output env >> "$GITHUB_ENV" ``` ``` go test -run "$(SHARD_TESTS)" $(SHARD_PATHS) ```
1 parent b251cf8 commit 48b4731

File tree

5 files changed

+99
-12
lines changed

5 files changed

+99
-12
lines changed

.github/workflows/ci.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- "*"
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: actions/setup-go@v5
18+
- run: go test -coverpkg ./... -coverprofile coverage.txt ./...
19+
- uses: codecov/codecov-action@v5

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ We can discover test cases significantly faster by essentially `grep`-ing for pa
2929

3030
* A test can potentially be executed more than once if another package shares a test with the same name.
3131
Renaming your tests to be globally unique is currently the best workaround if you want to guarantee a single execution per test function.
32-
You can discover test with name collisions by running `shard --total 1 --index 0`.
32+
You can discover tests with name collisions by running `shard --total 1 --index 0`.
3333
* Benchmarks aren't currently collected so running with `-bench` will not have any effect.
3434

3535

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/blampe/shard
22

3-
go 1.22.6
3+
go 1.21.0

main.go

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"errors"
45
"flag"
56
"fmt"
67
"log"
@@ -24,30 +25,56 @@ func main() {
2425
index := flag.Int("index", -1, "shard index to collect tests for")
2526
total := flag.Int("total", -1, "total number of shards")
2627
seed := flag.Int64("seed", 0, "randomly shuffle tests using this seed")
28+
output := flag.String("output", "", "output format (env)")
2729

2830
flag.Parse()
29-
if *index < 0 {
30-
log.Fatal("index is required")
31+
32+
p := prog{index: *index, total: *total, seed: *seed, root: *root, output: *output}
33+
out, err := p.run()
34+
if err != nil {
35+
log.Fatal(err.Error())
3136
}
32-
if *total < 0 {
33-
log.Fatal("total is required")
37+
fmt.Fprint(os.Stdout, out)
38+
}
39+
40+
type prog struct {
41+
index int
42+
total int
43+
seed int64
44+
root string
45+
output string
46+
}
47+
48+
func (p prog) run() (string, error) {
49+
if p.index < 0 {
50+
return "", errors.New("index is required")
51+
}
52+
if p.total < 0 {
53+
return "", errors.New("total is required")
3454
}
35-
if *index >= *total {
36-
log.Fatal("index must be less than total")
55+
if p.index >= p.total {
56+
return "", errors.New("index must be less than total")
3757
}
3858

39-
tests, err := internal.Collect(*root)
59+
tests, err := internal.Collect(p.root)
4060
if err != nil {
4161
log.Fatal(err)
4262
}
4363

44-
names, paths := internal.Assign(tests, *index, *total, *seed)
64+
names, paths := internal.Assign(tests, p.index, p.total, p.seed)
4565

4666
// No-op if we didn't find any tests or get any assigned.
4767
if len(paths) == 0 {
48-
paths = []string{*root}
68+
paths = []string{p.root}
4969
names = []string{"NoTestsFound"}
5070
}
5171

52-
fmt.Printf("-run ^(%s)$ %s\n", strings.Join(names, "|"), strings.Join(paths, " "))
72+
pattern := fmt.Sprintf(`^(?:%s)\$`, strings.Join(names, "|"))
73+
74+
switch p.output {
75+
case "env":
76+
return fmt.Sprintf("SHARD_TESTS=%s\nSHARD_PATHS=%s", pattern, strings.Join(paths, " ")), nil
77+
default:
78+
return fmt.Sprintf("-run %s %s", pattern, strings.Join(paths, " ")), nil
79+
}
5380
}

main_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"testing"
6+
)
7+
8+
func TestProg(t *testing.T) {
9+
tests := []struct {
10+
p prog
11+
name string
12+
want string
13+
wantErr error
14+
}{
15+
{
16+
name: "default output",
17+
p: prog{total: 1, root: "."},
18+
want: `-run ^(?:TestAssign|TestCollect|TestProg)\$ ./. ./internal`,
19+
},
20+
{
21+
name: "env output",
22+
p: prog{output: "env", total: 1, root: "."},
23+
want: `SHARD_TESTS=^(?:TestAssign|TestCollect|TestProg)\$
24+
SHARD_PATHS=./. ./internal`,
25+
},
26+
}
27+
28+
for _, tt := range tests {
29+
t.Run(tt.name, func(t *testing.T) {
30+
out, err := tt.p.run()
31+
if out != tt.want {
32+
t.Errorf("wanted %q but got %q", tt.want, out)
33+
34+
}
35+
if !errors.Is(err, tt.wantErr) {
36+
t.Errorf("wanted %q but got %q", tt.wantErr, err)
37+
}
38+
})
39+
}
40+
41+
}

0 commit comments

Comments
 (0)