Skip to content

Commit 2f1081f

Browse files
committed
feat: initial github support
1 parent d71528c commit 2f1081f

Some content is hidden

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

42 files changed

+1619
-269
lines changed

.github/workflows/test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ jobs:
4646
- name: Ensure scm-engine binary work
4747
run: ./scm-engine -h
4848

49-
- name: Test scm-engine against test GitLab project
49+
- name: Test scm-engine against a GitLab project
5050
run: ./scm-engine evaluate all
5151
env:
5252
SCM_ENGINE_TOKEN: "${{ secrets.GITLAB_INTEGRATION_TEST_API_TOKEN }}"
53-
SCM_ENGINE_CONFIG_FILE: ".scm-engine.example.yml"
53+
SCM_ENGINE_CONFIG_FILE: ".scm-engine.gitlab.example.yml"
5454
GITLAB_PROJECT: "jippi/scm-engine-schema-test"
5555
GITLAB_BASEURL: https://gitlab.com/
5656

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
/scm-engine
1818
/scm-engine.exe
1919
/docs/gitlab/script-attributes.md
20+
/docs/github/script-attributes.md
File renamed without changes.

cmd/cmd_evaluate.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,26 @@ import (
55

66
"github.com/jippi/scm-engine/pkg/config"
77
"github.com/jippi/scm-engine/pkg/scm"
8-
"github.com/jippi/scm-engine/pkg/scm/gitlab"
98
"github.com/jippi/scm-engine/pkg/state"
109
"github.com/urfave/cli/v2"
1110
)
1211

1312
func Evaluate(cCtx *cli.Context) error {
1413
ctx := state.WithProjectID(cCtx.Context, cCtx.String(FlagSCMProject))
14+
ctx = state.WithBaseURL(ctx, cCtx.String(FlagSCMBaseURL))
1515
ctx = state.WithCommitSHA(ctx, cCtx.String(FlagCommitSHA))
1616
ctx = state.WithDryRun(ctx, cCtx.Bool(FlagDryRun))
17+
ctx = state.WithProvider(ctx, cCtx.String(FlagProvider))
18+
ctx = state.WithToken(ctx, cCtx.String(FlagAPIToken))
19+
ctx = state.WithToken(ctx, cCtx.String(FlagAPIToken))
1720
ctx = state.WithUpdatePipeline(ctx, cCtx.Bool(FlagUpdatePipeline))
1821

1922
cfg, err := config.LoadFile(cCtx.String(FlagConfigFile))
2023
if err != nil {
2124
return err
2225
}
2326

24-
client, err := gitlab.NewClient(cCtx.String(FlagAPIToken), cCtx.String(FlagSCMBaseURL))
27+
client, err := getClient(ctx)
2528
if err != nil {
2629
return err
2730
}

cmd/cmd_server.go

+11-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"time"
1515

1616
"github.com/jippi/scm-engine/pkg/config"
17-
"github.com/jippi/scm-engine/pkg/scm/gitlab"
1817
"github.com/jippi/scm-engine/pkg/state"
1918
"github.com/urfave/cli/v2"
2019
slogctx "github.com/veqryn/slog-context"
@@ -55,15 +54,22 @@ func errHandler(ctx context.Context, w http.ResponseWriter, code int, err error)
5554
return
5655
}
5756

58-
func Server(cCtx *cli.Context) error { //nolint:unparam
59-
slogctx.Info(cCtx.Context, "Starting HTTP server", slog.String("listen", cCtx.String(FlagServerListen)))
57+
func Server(cCtx *cli.Context) error {
58+
// Initialize context
59+
ctx := state.WithDryRun(cCtx.Context, cCtx.Bool(FlagDryRun))
60+
ctx = state.WithBaseURL(ctx, cCtx.String(FlagSCMBaseURL))
61+
ctx = state.WithToken(ctx, cCtx.String(FlagAPIToken))
62+
ctx = state.WithProvider(ctx, cCtx.String(FlagProvider))
63+
ctx = state.WithUpdatePipeline(ctx, cCtx.Bool(FlagUpdatePipeline))
64+
65+
slogctx.Info(ctx, "Starting HTTP server", slog.String("listen", cCtx.String(FlagServerListen)))
6066

6167
mux := http.NewServeMux()
6268

6369
ourSecret := cCtx.String(FlagWebhookSecret)
6470

65-
// Initialize GitLab client
66-
client, err := gitlab.NewClient(cCtx.String(FlagAPIToken), cCtx.String(FlagSCMBaseURL))
71+
// Initialize client
72+
client, err := getClient(cCtx.Context)
6773
if err != nil {
6874
return err
6975
}
@@ -179,9 +185,6 @@ func Server(cCtx *cli.Context) error { //nolint:unparam
179185
ReadTimeout: 5 * time.Second,
180186
WriteTimeout: 5 * time.Second,
181187
BaseContext: func(l net.Listener) context.Context {
182-
ctx := state.WithDryRun(cCtx.Context, cCtx.Bool(FlagDryRun))
183-
ctx = state.WithUpdatePipeline(ctx, cCtx.Bool(FlagUpdatePipeline))
184-
185188
return ctx
186189
},
187190
}

cmd/conventions.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ package cmd
22

33
const (
44
FlagAPIToken = "api-token"
5+
FlagCommitSHA = "commit"
56
FlagConfigFile = "config"
67
FlagDryRun = "dry-run"
78
FlagMergeRequestID = "id"
8-
FlagUpdatePipeline = "update-pipeline"
9-
FlagCommitSHA = "commit"
9+
FlagProvider = "provider"
1010
FlagSCMBaseURL = "base-url"
1111
FlagSCMProject = "project"
1212
FlagServerListen = "listen"
13+
FlagUpdatePipeline = "update-pipeline"
1314
FlagWebhookSecret = "webhook-secret"
1415
)

cmd/shared.go

+18-3
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,29 @@ import (
88

99
"github.com/jippi/scm-engine/pkg/config"
1010
"github.com/jippi/scm-engine/pkg/scm"
11+
"github.com/jippi/scm-engine/pkg/scm/github"
12+
"github.com/jippi/scm-engine/pkg/scm/gitlab"
1113
"github.com/jippi/scm-engine/pkg/state"
1214
slogctx "github.com/veqryn/slog-context"
1315
)
1416

17+
func getClient(ctx context.Context) (scm.Client, error) {
18+
switch state.Provider(ctx) {
19+
case "github":
20+
return github.NewClient(ctx), nil
21+
22+
case "gitlab":
23+
return gitlab.NewClient(ctx)
24+
25+
default:
26+
return nil, fmt.Errorf("unknown provider %q - we only support 'github' and 'gitlab'", state.Provider(ctx))
27+
}
28+
}
29+
1530
func ProcessMR(ctx context.Context, client scm.Client, cfg *config.Config, event any) (err error) {
1631
// Stop the pipeline when we leave this func
1732
defer func() {
18-
if stopErr := client.Stop(ctx, err); err != nil {
33+
if stopErr := client.Stop(ctx, err); stopErr != nil {
1934
slogctx.Error(ctx, "Failed to update pipeline", slog.Any("error", stopErr))
2035
}
2136
}()
@@ -157,12 +172,12 @@ func syncLabels(ctx context.Context, client scm.Client, remote []*scm.Label, req
157172

158173
// Update
159174
for _, label := range required {
160-
e, ok := remoteLabels[label.Name]
175+
remote, ok := remoteLabels[label.Name]
161176
if !ok {
162177
continue
163178
}
164179

165-
if label.IsEqual(e) {
180+
if label.IsEqual(ctx, remote) {
166181
continue
167182
}
168183

docs/commands/evaluate.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ USAGE:
1515
scm-engine evaluate [command options] [mr_id, mr_id, ...]
1616
1717
OPTIONS:
18-
--project value GitLab project (example: 'gitlab-org/gitlab') [$GITLAB_PROJECT, $CI_PROJECT_PATH]
19-
--id value The pull/merge ID to process, if not provided as a CLI flag [$CI_MERGE_REQUEST_IID]
20-
--commit value The git commit sha [$CI_COMMIT_SHA]
2118
--update-pipeline Update the CI pipeline status with progress (default: false) [$SCM_ENGINE_UPDATE_PIPELINE]
19+
--project value GitLab project (example: 'gitlab-org/gitlab') [$GITLAB_PROJECT, $CI_PROJECT_PATH, $GITHUB_REPOSITORY]
20+
--id value The pull/merge ID to process, if not provided as a CLI flag [$CI_MERGE_REQUEST_IID]
21+
--commit value The git commit sha [$CI_COMMIT_SHA, $GITHUB_SHA]
2222
--help, -h show help
2323
2424
GLOBAL OPTIONS:
2525
--config value Path to the scm-engine config file (default: ".scm-engine.yml") [$SCM_ENGINE_CONFIG_FILE]
26-
--api-token value GitHub/GitLab API token [$SCM_ENGINE_TOKEN]
27-
--base-url value Base URL for the SCM instance (default: "https://gitlab.com/") [$GITLAB_BASEURL, $CI_SERVER_URL]
26+
--provider value SCM provider to use. Must be either "github" or "gitlab". SCM Engine will automatically detect "github" if "GITHUB_ACTIONS" environment variable is set (e.g., inside GitHub Actions) and detect "gitlab" if "GITLAB_CI" environment variable is set (e.g., inside GitLab CI). [$SCM_ENGINE_PROVIDER]
27+
--api-token value GitHub/GitLab API token [$SCM_ENGINE_TOKEN, $GITHUB_TOKEN]
28+
--base-url value Base URL for the SCM instance (default: "https://gitlab.com/") [$GITLAB_BASEURL, $CI_SERVER_URL, $GITHUB_API_URL]
2829
--dry-run Dry run, don't actually _do_ actions, just print them (default: false)
2930
--help, -h show help
3031
--version, -v print the version

docs/commands/server.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ OPTIONS:
3131
3232
GLOBAL OPTIONS:
3333
--config value Path to the scm-engine config file (default: ".scm-engine.yml") [$SCM_ENGINE_CONFIG_FILE]
34-
--api-token value GitHub/GitLab API token [$SCM_ENGINE_TOKEN]
35-
--base-url value Base URL for the SCM instance (default: "https://gitlab.com/") [$GITLAB_BASEURL, $CI_SERVER_URL]
34+
--provider value SCM provider to use. Must be either "github" or "gitlab". SCM Engine will automatically detect "github" if "GITHUB_ACTIONS" environment variable is set (e.g., inside GitHub Actions) and detect "gitlab" if "GITLAB_CI" environment variable is set (e.g., inside GitLab CI). [$SCM_ENGINE_PROVIDER]
35+
--api-token value GitHub/GitLab API token [$SCM_ENGINE_TOKEN, $GITHUB_TOKEN]
36+
--base-url value Base URL for the SCM instance (default: "https://gitlab.com/") [$GITLAB_BASEURL, $CI_SERVER_URL, $GITHUB_API_URL]
3637
--dry-run Dry run, don't actually _do_ actions, just print them (default: false)
3738
--help, -h show help
3839
--version, -v print the version

docs/configuration/index.md docs/configuration.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Options
1+
# Configuration file
22

33
The default configuration filename is `.scm-engine.yml`, either in current working directory, or if you are in a Git repository, the root of the project.
44

@@ -105,15 +105,15 @@ SCM Engine supports two strategies for managing labels, each changes the behavio
105105

106106
Use the `#!yaml conditional` strategy when you want to add/remove a label on a Merge Request depending on *something*. It's the default strategy, and the most simple one to use.
107107

108-
!!! example "Please see the [*Add label if a file extension is modified*](examples.md#add-label-if-a-file-extension-is-modified) example for how to use this"
108+
!!! example "Please see the [*Add label if a file extension is modified*](./gitlab/examples.md#add-label-if-a-file-extension-is-modified) example for how to use this"
109109

110110
#### `label[].strategy = generate` {#label.strategy-generate data-toc-label="generate"}
111111

112112
Use the [`#!yaml generate`](#label.strategy) strategy if you want to create dynamic labels, for example, depending labels based on the file structure within your project.
113113

114114
Thanks to the dynamic nature of the `#!yaml generate` strategy, it has fantastic flexibility, at the cost of greater complexity.
115115

116-
!!! example "Please see the [*generate labels from directory layout*](examples.md#generate-labels-via-script) example for how to use this"
116+
!!! example "Please see the [*generate labels from directory layout*](./gitlab/examples.md#generate-labels-via-script) example for how to use this"
117117

118118
### `label[].name` {#label.name data-toc-label="name"}
119119

docs/github/.pages

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
title: >
2+
<svg style="vertical-align: sub" aria-hidden="true" viewBox="0 0 16 16" version="1.1" data-view-component="true" class="octicon octicon-mark-github v-align-middle">
3+
<path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path>
4+
</svg>
5+
6+
GitHub (WIP)
7+
8+
arrange:
9+
- setup.md
10+
- ...

docs/github/script-functions.md

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Script Functions
2+
3+
!!! tip "The [Expr Language Definition](https://expr-lang.org/docs/language-definition) is a great resource to learn more about the language"
4+
5+
## pull_request
6+
7+
### `pull_request.state_is(string...) -> boolean` {: #pull_request.state_is data-toc-label="state_is"}
8+
9+
Check if the `pull_request` state is any of the provided states
10+
11+
**Valid options**:
12+
13+
- `CLOSED` - In closed state
14+
- `MERGED` - Pull Request has been merged
15+
- `OPEN` - Opened Pull Request
16+
17+
```css
18+
pull_request.state_is("MERGED")
19+
pull_request.state_is("CLOSED", "MERGED")
20+
```
21+
22+
### `pull_request.modified_files(string...) -> boolean` {: #pull_request.modified_files data-toc-label="modified_files"}
23+
24+
Returns wether any of the provided files patterns have been modified in the Pull Request.
25+
26+
The file patterns use the [`.gitignore` format](https://git-scm.com/docs/gitignore#_pattern_format).
27+
28+
```css
29+
pull_request.modified_files("*.go", "docs/") == true
30+
```
31+
32+
### `pull_request.modified_files_list(string...) -> []string` {: #pull_request.modified_files_list data-toc-label="modified_files_list"}
33+
34+
Returns an array of files matching the provided (optional) pattern thas has been modified in the Pull Request.
35+
36+
The file patterns use the [`.gitignore` format](https://git-scm.com/docs/gitignore#_pattern_format).
37+
38+
```css
39+
pull_request.modified_files_list("*.go", "docs/") == ["example/file.go", "docs/index.md"]
40+
```
41+
42+
### `pull_request.has_label(string) -> boolean` {: #pull_request.has_label data-toc-label="has_label"}
43+
44+
Returns wether any of the provided label exist on the Pull Request.
45+
46+
```css
47+
pull_request.labels = ["hello"]
48+
pull_request.has_label("hello") == true
49+
pull_request.has_label("world") == false
50+
```
51+
52+
### `pull_request.has_no_label(string) -> boolean` {: #pull_request.has_no_label data-toc-label="has_no_label"}
53+
54+
Returns wether the Pull Request has the provided label or not.
55+
56+
```css
57+
pull_request.labels = ["hello"]
58+
pull_request.has_no_label("hello") == false
59+
pull_request.has_no_label("world") == true
60+
```
61+
62+
## Global
63+
64+
### `duration(string) -> duration` {: #duration data-toc-label="duration"}
65+
66+
Returns the [`time.Duration`](https://pkg.go.dev/time#Duration) value of the given string str.
67+
68+
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h", "d" and "w".
69+
70+
```css
71+
duration("1h").Seconds() == 3600
72+
```
73+
74+
### `uniq([]string) -> []string` {: #uniq data-toc-label="uniq"}
75+
76+
Returns a new array where all duplicate values has been removed.
77+
78+
```css
79+
(["hello", "world", "world"] | uniq) == ["hello", "world"]
80+
```
81+
82+
### `filepath_dir` {: #filepath_dir data-toc-label="filepath_dir"}
83+
84+
`filepath_dir` returns all but the last element of path, typically the path's directory. After dropping the final element,
85+
86+
Dir calls [Clean](https://pkg.go.dev/path/filepath#Clean) on the path and trailing slashes are removed.
87+
88+
If the path is empty, `filepath_dir` returns ".". If the path consists entirely of separators, `filepath_dir` returns a single separator.
89+
90+
The returned path does not end in a separator unless it is the root directory.
91+
92+
```css
93+
filepath_dir("example/directory/file.go") == "example/directory"
94+
```
95+
96+
### `limit_path_depth_to` {: #limit_path_depth_to data-toc-label="limit_path_depth_to"}
97+
98+
`limit_path_depth_to` takes a path structure, and limits it to the configured maximum depth. Particularly useful when using `generated` labels from a directory structure, and want to to have a label naming scheme that only uses path of the path.
99+
100+
```css
101+
limit_path_depth_to("path1/path2/path3/path4", 2), == "path1/path2"
102+
limit_path_depth_to("path1/path2", 3), == "path1/path2"
103+
```

docs/github/setup.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Setup
2+
3+
TODO

docs/gitlab/.pages

+24
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
title: >
2+
<svg style="enable-background:new 0 0 100 92;vertical-align: sub;" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 100 92" xml:space="preserve">
3+
<style type="text/css">
4+
.st0{fill:#E24329;}
5+
.st1{fill:#FC6D26;}
6+
.st2{fill:#FCA326;}
7+
</style>
8+
<desc>Created with Sketch.</desc>
9+
<g>
10+
<path class="st0" d="M95.9,36.5l-0.1-0.3L82.8,2.4c-0.3-0.7-0.7-1.2-1.3-1.6c-0.6-0.4-1.3-0.6-2-0.5c-0.7,0-1.4,0.3-2,0.7
11+
c-0.6,0.4-1,1.1-1.1,1.7l-8.7,26.7H32.3L23.6,2.7C23.4,2.1,23,1.4,22.5,1c-0.6-0.4-1.2-0.7-2-0.7c-0.7,0-1.4,0.1-2,0.5
12+
c-0.6,0.4-1.1,0.9-1.3,1.6L4.2,36.1l-0.1,0.3c-3.8,10-0.6,21.3,8,27.7c0,0,0,0,0,0l0.1,0.1l19.7,14.7l9.7,7.4l5.9,4.5
13+
c1.4,1.1,3.4,1.1,4.8,0l5.9-4.5l9.7-7.4l19.8-14.8c0,0,0,0,0.1,0C96.5,57.8,99.7,46.5,95.9,36.5z"/>
14+
<path class="st1" d="M95.9,36.5l-0.1-0.3c-6.4,1.3-12.3,4-17.4,7.8C78.3,44,63,55.6,50,65.4c9.7,7.3,18.1,13.7,18.1,13.7l19.8-14.8
15+
c0,0,0,0,0.1,0C96.5,57.8,99.7,46.5,95.9,36.5z"/>
16+
<path class="st2" d="M31.9,79.1l9.7,7.4l5.9,4.5c1.4,1.1,3.4,1.1,4.8,0l5.9-4.5l9.7-7.4c0,0-8.4-6.4-18.1-13.7
17+
C40.3,72.7,31.9,79.1,31.9,79.1z"/>
18+
<path class="st1" d="M21.6,43.9c-5.1-3.8-11-6.5-17.4-7.8l-0.1,0.3c-3.8,10-0.6,21.3,8,27.7c0,0,0,0,0,0l0.1,0.1l19.7,14.7
19+
c0,0,8.4-6.4,18.1-13.7C37,55.6,21.7,44,21.6,43.9z"/>
20+
</g>
21+
</svg>
22+
23+
GitLab
24+
125
arrange:
226
- setup.md
327
- ...
File renamed without changes.

docs/index.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ hide:
1717

1818
## What does it look like?
1919

20-
!!! tip "Please see the [Configuration Examples page](configuration/examples.md) for more use-cases"
20+
!!! tip "Please see the [Configuration Examples page](gitlab/examples.md) for more use-cases"
2121

22-
!!! info "Please see the [Configuration Options page](configuration/index.md) for all options and explanations"
22+
!!! info "Please see the [Configuration Options page](configuration.md) for all options and explanations"
2323

2424
```yaml
25-
--8<-- ".scm-engine.example.yml"
25+
--8<-- ".scm-engine.gitlab.example.yml"
2626
```

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/expr-lang/expr v1.16.7
1010
github.com/fatih/structtag v1.2.0
1111
github.com/golang-cz/devslog v0.0.8
12+
github.com/google/go-github/v62 v62.0.0
1213
github.com/guregu/null/v5 v5.0.0
1314
github.com/hasura/go-graphql-client v0.12.1
1415
github.com/iancoleman/strcase v0.3.0

go.sum

+4-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4
2626
github.com/golang-cz/devslog v0.0.8 h1:53ipA2rC5JzWBWr9qB8EfenvXppenNiF/8DwgtNT5Q4=
2727
github.com/golang-cz/devslog v0.0.8/go.mod h1:bSe5bm0A7Nyfqtijf1OMNgVJHlWEuVSXnkuASiE1vV8=
2828
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
29-
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
30-
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
29+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
30+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
31+
github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4=
32+
github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4=
3133
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
3234
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
3335
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=

0 commit comments

Comments
 (0)