Skip to content

Commit a27cf5d

Browse files
committed
Support for database/sql in migrations + framework for multi-driver River
Here, add a new minimal driver called `riverdriver/riversql` that supports Go's built-in `database/sql` package, but only for purposes of migrations. The idea here is to fully complete #57 by providing a way of making `rivermigrate` interoperable with Go migration frameworks that support Go-based migrations like Goose, which provides hooks for `*sql.Tx` [1] rather than pgx. `riverdriver/riversql` is not a full driver and is only meant to be used with `rivermigrate`. We document this clearly in a number of places. To make a multi-driver world possible with River, we have to start the work of building a platform that does more than `riverpgxv5`'s "cheat" workaround. This works by having each driver implement specific database operations like `MigrationGetAll`, which target their wrapped database package of choice. This is accomplished by having each driver bundle in its own sqlc that targets its package. So `riverpgxv5` has an `sqlc.yaml` that targets `pgx/v5`, while `riversql` has one that targets `database/sql`. There's some `sqlc.yaml` duplication involved here, but luckily both drivers can share a `river_migration.sql` file that contains all queries involved, so you only need to change one place. `river_migration.sql` also migrates entirely out of the main `./internal/dbsqlc`. The idea here is that eventually `./internal/dbsqlc` will disappear completely, usurped entirely by driver-specific versions. As this is done, all references to `pgx` will disappear from the top-level package. There are some complications here to figure out like `LISTEN`/`NOTIFY` though, and I'm not clear whether `database/sql` could ever become a fully functional driver as it might be missing some needed functionality (e.g. subtransactions are still not supported after talking about them for ten f*ing years [2]. However, even if it's not, the system would let us support other fully functional packages or future major versions of pgx (or even past ones like `pgx/v4` if there's demand). `river/riverdriver` becomes a package as it now has types in it that need to be referenced by driver implementations, and this would otherwise not be possible without introducing a circular dependency. Notably, this development branch has to use some `go.mod` `replace` directives to demonstrate that it works correctly. If we go this direction, we'll need to break it into chunks to release it without them: 1. Break out changes to `river/riverdriver`. Tag and release it. 2. Break out changes to `riverdriver/river*` drivers. Have them target the release in (1), comment out `replace`s, then tag and release them. 3. Target the remaining River changes to the releases in (1) and (2), comment out `replace`s, then tag and release the top-level driver. Unfortunately future deep incisions to drivers will require similar gymnastics, but I don't think there's a way around it (we already have this process except it's currently two steps instead of three). The hope is that these will change relatively rarely, so it won't be too painful. [1] https://github.com/pressly/goose#go-migrations [2] golang/go#7898
1 parent 13ecbd4 commit a27cf5d

36 files changed

+1165
-148
lines changed

.github/workflows/ci.yml

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,15 @@ jobs:
7272
env:
7373
TEST_DATABASE_URL: postgres://postgres:[email protected]:5432/river_testdb?sslmode=disable
7474

75-
- name: Test riverpgxv5
75+
- name: Test riverdriver
76+
working-directory: ./riverdriver
77+
run: go test -race ./...
78+
79+
- name: Test riverdriver/riverdatabasesql
80+
working-directory: ./riverdriver/riverdatabasesql
81+
run: go test -race ./...
82+
83+
- name: Test riverdriver/riverpgxv5
7684
working-directory: ./riverdriver/riverpgxv5
7785
run: go test -race ./...
7886

@@ -117,10 +125,13 @@ jobs:
117125
golangci:
118126
name: lint
119127
runs-on: ubuntu-latest
128+
env:
129+
GOLANGCI_LINT_VERSION: v1.55.2
120130
permissions:
121131
contents: read
122132
# allow read access to pull request. Use with `only-new-issues` option.
123133
pull-requests: read
134+
124135
steps:
125136
- uses: actions/setup-go@v4
126137
with:
@@ -130,13 +141,33 @@ jobs:
130141
- name: Checkout
131142
uses: actions/checkout@v3
132143

133-
- name: golangci-lint
144+
- name: Lint
145+
uses: golangci/golangci-lint-action@v3
146+
with:
147+
only-new-issues: true # Optional: show only new issues if it's a pull request. The default value is `false`.
148+
version: ${{ env.GOLANGCI_LINT_VERSION }}
149+
working-directory: .
150+
151+
- name: Lint riverdriver
152+
uses: golangci/golangci-lint-action@v3
153+
with:
154+
only-new-issues: true # Optional: show only new issues if it's a pull request. The default value is `false`.
155+
version: ${{ env.GOLANGCI_LINT_VERSION }}
156+
working-directory: ./riverdriver
157+
158+
- name: Lint riverdriver/riverdatabasesql
134159
uses: golangci/golangci-lint-action@v3
135160
with:
136-
# Optional: show only new issues if it's a pull request. The default value is `false`.
137-
only-new-issues: true
161+
only-new-issues: true # Optional: show only new issues if it's a pull request. The default value is `false`.
162+
version: ${{ env.GOLANGCI_LINT_VERSION }}
163+
working-directory: ./riverdriver/riverdatabasesql
138164

139-
version: v1.55.2
165+
- name: Lint riverdriver/riverpgxv5
166+
uses: golangci/golangci-lint-action@v3
167+
with:
168+
only-new-issues: true # Optional: show only new issues if it's a pull request. The default value is `false`.
169+
version: ${{ env.GOLANGCI_LINT_VERSION }}
170+
working-directory: ./riverdriver/riverpgxv5
140171

141172
producer_sample:
142173
runs-on: ubuntu-latest
@@ -204,7 +235,6 @@ jobs:
204235
sqlc-version: "1.22.0"
205236

206237
- name: Run sqlc diff
207-
working-directory: ./internal/dbsqlc
208238
run: |
209239
echo "Please make sure that all sqlc changes are checked in!"
210-
sqlc diff
240+
make verify

.golangci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ linters-settings:
6464
- Default
6565
- Prefix(github.com/riverqueue)
6666

67+
gomoddirectives:
68+
replace-local: true
69+
6770
gosec:
6871
excludes:
6972
- G404 # use of non-crypto random; overly broad for our use case

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added `riverdriver/riverdatabasesql` driver to enable River Go migrations through Go's built in `database/sql` package. [PR #98](https://github.com/riverqueue/river/pull/98).
13+
14+
### Changed
15+
16+
- `riverdriver` becomes its own submodule. It contains types that `riverdriver/riverdatabasesql` and `riverdriver/riverpgxv5` need to reference. [PR #98](https://github.com/riverqueue/river/pull/98).
17+
1018
## [0.0.12] - 2023-12-02
1119

1220
### Added

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,16 @@ generate: generate/sqlc
44

55
.PHONY: generate/sqlc
66
generate/sqlc:
7-
cd internal/dbsqlc && sqlc generate
7+
cd internal/dbsqlc && sqlc generate
8+
cd riverdriver/riverdatabasesql/internal/dbsqlc && sqlc generate
9+
cd riverdriver/riverpgxv5/internal/dbsqlc && sqlc generate
10+
11+
.PHONY: verify
12+
verify:
13+
verify: verify/sqlc
14+
15+
.PHONY: verify/sqlc
16+
verify/sqlc:
17+
cd internal/dbsqlc && sqlc diff
18+
cd riverdriver/riverdatabasesql/internal/dbsqlc && sqlc diff
19+
cd riverdriver/riverpgxv5/internal/dbsqlc && sqlc diff

docs/development.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ queries. After changing an sqlc `.sql` file, generate Go with:
3232
```shell
3333
git checkout master && git pull --rebase
3434
VERSION=v0.0.x
35+
git tag riverdriver/VERSION -m "release riverdriver/VERSION"
3536
git tag riverdriver/riverpgxv5/$VERSION -m "release riverdriver/riverpgxv5/$VERSION"
37+
git tag riverdriver/riverdatabasesql/$VERSION -m "release riverdriver/riverdatabasesql/$VERSION"
3638
git tag $VERSION
3739
git push --tags
3840
```

go.mod

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
module github.com/riverqueue/river
22

3-
go 1.21.0
3+
go 1.21.4
44

5-
// replace github.com/riverqueue/river/riverdriver/riverpgxv5 => ./riverdriver/riverpgxv5
5+
replace github.com/riverqueue/river/riverdriver => ./riverdriver
6+
7+
replace github.com/riverqueue/river/riverdriver/riverpgxv5 => ./riverdriver/riverpgxv5
8+
9+
replace github.com/riverqueue/river/riverdriver/riverdatabasesql => ./riverdriver/riverdatabasesql
610

711
require (
812
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
913
github.com/jackc/pgx/v5 v5.5.0
1014
github.com/jackc/puddle/v2 v2.2.1
1115
github.com/oklog/ulid/v2 v2.1.0
16+
github.com/riverqueue/river/riverdriver v0.0.0-00010101000000-000000000000
1217
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.0.12
18+
github.com/riverqueue/river/riverdriver/riverdatabasesql v0.0.0-00010101000000-000000000000
1319
github.com/robfig/cron/v3 v3.0.1
1420
github.com/spf13/cobra v1.8.0
1521
github.com/stretchr/testify v1.8.4
@@ -23,6 +29,7 @@ require (
2329
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2430
github.com/jackc/pgpassfile v1.0.0 // indirect
2531
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
32+
github.com/lib/pq v1.10.9 // indirect
2633
github.com/pmezard/go-difflib v1.0.0 // indirect
2734
github.com/spf13/pflag v1.0.5 // indirect
2835
golang.org/x/crypto v0.15.0 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
1818
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
1919
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
2020
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
21+
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
22+
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
2123
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
2224
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
2325
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
2426
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2527
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
26-
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.0.12 h1:mcDBnqwzEXY9WDOwbkd8xmFdSr/H6oHb1F3NCNCmLDY=
27-
github.com/riverqueue/river/riverdriver/riverpgxv5 v0.0.12/go.mod h1:k6hsPkW9Fl3qURzyLHbvxUCqWDpit0WrZ3oEaKezD3E=
2828
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
2929
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
3030
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=

internal/dbsqlc/models.go

Lines changed: 0 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/dbsqlc/sqlc.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ sql:
44
queries:
55
- river_job.sql
66
- river_leader.sql
7-
- river_migration.sql
87
schema:
98
- river_job.sql
109
- river_leader.sql
11-
- river_migration.sql
1210
gen:
1311
go:
1412
package: "dbsqlc"

internal/riverinternaltest/riverinternaltest.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"log"
1010
"log/slog"
11+
"net/url"
1112
"os"
1213
"runtime"
1314
"sync"
@@ -52,19 +53,39 @@ func BaseServiceArchetype(tb testing.TB) *baseservice.Archetype {
5253
}
5354

5455
func DatabaseConfig(databaseName string) *pgxpool.Config {
55-
databaseURL := valutil.ValOrDefault(os.Getenv("TEST_DATABASE_URL"), "postgres:///river_testdb?sslmode=disable")
56-
57-
config, err := pgxpool.ParseConfig(databaseURL)
56+
config, err := pgxpool.ParseConfig(DatabaseURL(databaseName))
5857
if err != nil {
5958
panic(fmt.Sprintf("error parsing database URL: %v", err))
6059
}
6160
config.MaxConns = dbPoolMaxConns
6261
config.ConnConfig.ConnectTimeout = 10 * time.Second
63-
config.ConnConfig.Database = databaseName
6462
config.ConnConfig.RuntimeParams["timezone"] = "UTC"
6563
return config
6664
}
6765

66+
// DatabaseURL gets a test database URL from TEST_DATABASE_URL or falls back on
67+
// a default pointing to `river_testdb`. If databaseName is set, it replaces the
68+
// database in the URL, although the host and other parameters are preserved.
69+
//
70+
// Most of the time DatabaseConfig should be used instead of this function, but
71+
// it may be useful in non-pgx situations like for examples showing the use of
72+
// `database/sql`.
73+
func DatabaseURL(databaseName string) string {
74+
u, err := url.Parse(valutil.ValOrDefault(
75+
os.Getenv("TEST_DATABASE_URL"),
76+
"postgres://localhost/river_testdb?sslmode=disable"),
77+
)
78+
if err != nil {
79+
panic(err)
80+
}
81+
82+
if databaseName != "" {
83+
u.Path = databaseName
84+
}
85+
86+
return u.String()
87+
}
88+
6889
// DiscardContinuously drains continuously out of the given channel and discards
6990
// anything that comes out of it. Returns a stop function that should be invoked
7091
// to stop draining. Stop must be invoked before tests finish to stop an

internal/util/dbutil/db_util.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/jackc/pgx/v5"
88

99
"github.com/riverqueue/river/internal/dbsqlc"
10+
"github.com/riverqueue/river/riverdriver"
1011
)
1112

1213
// Executor is an interface for a type that can begin a transaction and also
@@ -56,3 +57,35 @@ func WithTxV[T any](ctx context.Context, txBeginner TxBeginner, innerFunc func(c
5657

5758
return res, nil
5859
}
60+
61+
// WithExecutorTx starts and commits a transaction on a driver executor around
62+
// the given function, allowing the return of a generic value.
63+
func WithExecutorTx(ctx context.Context, exec riverdriver.Executor, innerFunc func(ctx context.Context, tx riverdriver.ExecutorTx) error) error {
64+
_, err := WithExecutorTxV(ctx, exec, func(ctx context.Context, tx riverdriver.ExecutorTx) (struct{}, error) {
65+
return struct{}{}, innerFunc(ctx, tx)
66+
})
67+
return err
68+
}
69+
70+
// WithExecutorTxV starts and commits a transaction on a driver executor around
71+
// the given function, allowing the return of a generic value.
72+
func WithExecutorTxV[T any](ctx context.Context, exec riverdriver.Executor, innerFunc func(ctx context.Context, tx riverdriver.ExecutorTx) (T, error)) (T, error) {
73+
var defaultRes T
74+
75+
tx, err := exec.Begin(ctx)
76+
if err != nil {
77+
return defaultRes, fmt.Errorf("error beginning transaction: %w", err)
78+
}
79+
defer tx.Rollback(ctx)
80+
81+
res, err := innerFunc(ctx, tx)
82+
if err != nil {
83+
return defaultRes, err
84+
}
85+
86+
if err := tx.Commit(ctx); err != nil {
87+
return defaultRes, fmt.Errorf("error committing transaction: %w", err)
88+
}
89+
90+
return res, nil
91+
}

internal/util/dbutil/db_util_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"github.com/stretchr/testify/require"
99

1010
"github.com/riverqueue/river/internal/riverinternaltest"
11+
"github.com/riverqueue/river/riverdriver"
12+
"github.com/riverqueue/river/riverdriver/riverpgxv5"
1113
)
1214

1315
func TestWithTx(t *testing.T) {
@@ -40,3 +42,36 @@ func TestWithTxV(t *testing.T) {
4042
require.NoError(t, err)
4143
require.Equal(t, 7, ret)
4244
}
45+
46+
func TestWithExecutorTx(t *testing.T) {
47+
t.Parallel()
48+
49+
ctx := context.Background()
50+
dbPool := riverinternaltest.TestDB(ctx, t)
51+
driver := riverpgxv5.New(dbPool)
52+
53+
err := WithExecutorTx(ctx, driver.GetExecutor(), func(ctx context.Context, tx riverdriver.ExecutorTx) error {
54+
_, err := tx.Exec(ctx, "SELECT 1")
55+
require.NoError(t, err)
56+
57+
return nil
58+
})
59+
require.NoError(t, err)
60+
}
61+
62+
func TestWithExecutorTxV(t *testing.T) {
63+
t.Parallel()
64+
65+
ctx := context.Background()
66+
dbPool := riverinternaltest.TestDB(ctx, t)
67+
driver := riverpgxv5.New(dbPool)
68+
69+
ret, err := WithExecutorTxV(ctx, driver.GetExecutor(), func(ctx context.Context, tx riverdriver.ExecutorTx) (int, error) {
70+
_, err := tx.Exec(ctx, "SELECT 1")
71+
require.NoError(t, err)
72+
73+
return 7, nil
74+
})
75+
require.NoError(t, err)
76+
require.Equal(t, 7, ret)
77+
}

riverdriver/go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module github.com/riverqueue/river/riverdriver
2+
3+
go 1.21.4
4+
5+
require github.com/jackc/pgx/v5 v5.5.0
6+
7+
require (
8+
github.com/jackc/pgpassfile v1.0.0 // indirect
9+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
10+
github.com/jackc/puddle/v2 v2.2.1 // indirect
11+
golang.org/x/crypto v0.15.0 // indirect
12+
golang.org/x/sync v0.5.0 // indirect
13+
golang.org/x/text v0.14.0 // indirect
14+
)

riverdriver/go.sum

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
5+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
6+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
7+
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
8+
github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw=
9+
github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
10+
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
11+
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
12+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
13+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
14+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
15+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
16+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
17+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
18+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
19+
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
20+
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
21+
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
22+
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
23+
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
24+
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
25+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
26+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
27+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
28+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)