Skip to content

Commit 503d98b

Browse files
authored
support URL in dependabot update repo argument (#490)
1 parent f52e3fe commit 503d98b

File tree

2 files changed

+119
-10
lines changed

2 files changed

+119
-10
lines changed

cmd/dependabot/internal/cmd/update.go

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ import (
66
"encoding/json"
77
"errors"
88
"fmt"
9-
"io"
10-
"log"
11-
"net"
12-
"net/url"
13-
"os"
14-
159
"github.com/MakeNowJust/heredoc"
1610
"github.com/dependabot/cli/internal/infra"
1711
"github.com/dependabot/cli/internal/model"
1812
"github.com/dependabot/cli/internal/server"
1913
"github.com/spf13/cobra"
2014
"gopkg.in/yaml.v3"
15+
"io"
16+
"log"
17+
"net"
18+
"net/url"
19+
"os"
20+
"regexp"
21+
"strings"
2122
)
2223

2324
var updateCmd = NewUpdateCommand()
@@ -55,6 +56,8 @@ func NewUpdateCommand() *cobra.Command {
5556
Short: "Perform an update job",
5657
Example: heredoc.Doc(`
5758
$ dependabot update go_modules dependabot/cli
59+
$ dependabot update go_modules git@github.com:dependabot/cli.git
60+
$ dependabot update go_modules https://github.com/dependabot/cli.git
5861
$ dependabot update -f input.yml
5962
`),
6063
RunE: func(cmd *cobra.Command, args []string) error {
@@ -208,6 +211,34 @@ func readArguments(cmd *cobra.Command, flags *UpdateFlags) (*model.Input, error)
208211
return nil, errors.New("requires a repo argument")
209212
}
210213

214+
var hostname, apiEndpoint string
215+
// if the repo is a git URL, extract the hostname
216+
// accepts org/repo, git@host:org/repo.git, and https://host/org/repo.git
217+
if u, err := url.Parse(repo); err == nil {
218+
hostname = u.Hostname()
219+
apiEndpoint = fmt.Sprintf("%s://%s/api/v3", u.Scheme, hostname)
220+
repo = u.Path
221+
} else {
222+
// at this point, it may be org/repo or username@host:org/repo.git
223+
re := regexp.MustCompile(`^(?:(?:[a-zA-Z0-9._%+-]+@|https?://)?([^:/]+):)?([^/]+)/([^/]+)(?:\.git)?$`)
224+
matches := re.FindStringSubmatch(repo)
225+
if len(matches) < 4 {
226+
return nil, fmt.Errorf("invalid repo format: %s", repo)
227+
}
228+
if matches[1] != "" {
229+
hostname = matches[1]
230+
apiEndpoint = fmt.Sprintf("https://%s/api/v3", hostname)
231+
}
232+
repo = fmt.Sprintf("%s/%s", matches[2], matches[3])
233+
}
234+
repo = strings.TrimPrefix(strings.TrimSuffix(repo, ".git"), "/")
235+
if hostname == "" {
236+
hostname = "github.com"
237+
apiEndpoint = "https://api.github.com"
238+
}
239+
240+
log.Println("Using hostname:", hostname, "api endpoint:", apiEndpoint)
241+
211242
allowed := []model.Allowed{{UpdateType: "all"}}
212243
if len(flags.dependencies) > 0 {
213244
allowed = allowed[:0]
@@ -238,8 +269,8 @@ func readArguments(cmd *cobra.Command, flags *UpdateFlags) (*model.Input, error)
238269
Directory: flags.directory,
239270
Commit: flags.commit,
240271
Branch: flags.branch,
241-
Hostname: nil,
242-
APIEndpoint: nil,
272+
Hostname: &hostname,
273+
APIEndpoint: &apiEndpoint,
243274
},
244275
UpdateSubdependencies: false,
245276
UpdatingAPullRequest: false,
@@ -315,17 +346,21 @@ func processInput(input *model.Input, flags *UpdateFlags) {
315346

316347
if hasLocalToken && !isGitSourceInCreds {
317348
log.Println("Inserting $LOCAL_GITHUB_ACCESS_TOKEN into credentials")
349+
host := "github.com"
350+
if input.Job.Source.Hostname != nil && *input.Job.Source.Hostname != "" {
351+
host = *input.Job.Source.Hostname
352+
}
318353
input.Credentials = append(input.Credentials, model.Credential{
319354
"type": "git_source",
320-
"host": "github.com",
355+
"host": host,
321356
"username": "x-access-token",
322357
"password": "$LOCAL_GITHUB_ACCESS_TOKEN",
323358
})
324359
if len(input.Job.CredentialsMetadata) > 0 {
325360
// Add the metadata since the next section will be skipped.
326361
input.Job.CredentialsMetadata = append(input.Job.CredentialsMetadata, map[string]any{
327362
"type": "git_source",
328-
"host": "github.com",
363+
"host": host,
329364
})
330365
}
331366
}

cmd/dependabot/internal/cmd/update_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,33 @@ func Test_processInput(t *testing.T) {
6363
}
6464
})
6565

66+
t.Run("adds git_source to credentials with specific hostname when local token is present", func(t *testing.T) {
67+
var input model.Input
68+
os.Setenv("LOCAL_GITHUB_ACCESS_TOKEN", "token")
69+
host := "github.example.com"
70+
input.Job.Source.Hostname = &host
71+
72+
processInput(&input, nil)
73+
74+
if len(input.Credentials) != 1 {
75+
t.Fatal("expected credentials to be added")
76+
}
77+
if !reflect.DeepEqual(input.Credentials[0], model.Credential{
78+
"type": "git_source",
79+
"host": host,
80+
"username": "x-access-token",
81+
"password": "$LOCAL_GITHUB_ACCESS_TOKEN",
82+
}) {
83+
t.Error("expected credentials to be added")
84+
}
85+
if !reflect.DeepEqual(input.Job.CredentialsMetadata[0], model.Credential{
86+
"type": "git_source",
87+
"host": host,
88+
}) {
89+
t.Error("expected credentials metadata to be added")
90+
}
91+
})
92+
6693
t.Run("adds metadata when credentials are provided", func(t *testing.T) {
6794
var input model.Input
6895
input.Credentials = []model.Credential{
@@ -200,6 +227,53 @@ func Test_extractInput(t *testing.T) {
200227
if input.Job.PackageManager != "go_modules" {
201228
t.Errorf("expected package manager to be go_modules, got %s", input.Job.PackageManager)
202229
}
230+
if input.Job.Source.Repo != "dependabot/cli" {
231+
t.Errorf("expected repo to be dependabot/cli, got %s", input.Job.Source.Repo)
232+
}
233+
})
234+
t.Run("test arguments with https URL", func(t *testing.T) {
235+
cmd := NewUpdateCommand()
236+
if err := cmd.ParseFlags([]string{"go_modules", "https://example.com/org/repo.git"}); err != nil {
237+
t.Fatal(err)
238+
}
239+
input, err := extractInput(cmd, &UpdateFlags{})
240+
if err != nil {
241+
t.Fatal(err)
242+
}
243+
if input.Job.PackageManager != "go_modules" {
244+
t.Errorf("expected package manager to be go_modules, got %s", input.Job.PackageManager)
245+
}
246+
if input.Job.Source.Repo != "org/repo" {
247+
t.Errorf("expected repo to be dependabot/cli, got %s", input.Job.Source.Repo)
248+
}
249+
if *input.Job.Source.Hostname != "example.com" {
250+
t.Errorf("expected hostname to be example.com, got %s", *input.Job.Source.Hostname)
251+
}
252+
if *input.Job.Source.APIEndpoint != "https://example.com/api/v3" {
253+
t.Errorf("unexpected API Endpoint %s", *input.Job.Source.APIEndpoint)
254+
}
255+
})
256+
t.Run("test arguments with git ssh", func(t *testing.T) {
257+
cmd := NewUpdateCommand()
258+
if err := cmd.ParseFlags([]string{"go_modules", "user@example.com:org/repo.git"}); err != nil {
259+
t.Fatal(err)
260+
}
261+
input, err := extractInput(cmd, &UpdateFlags{})
262+
if err != nil {
263+
t.Fatal(err)
264+
}
265+
if input.Job.PackageManager != "go_modules" {
266+
t.Errorf("expected package manager to be go_modules, got %s", input.Job.PackageManager)
267+
}
268+
if input.Job.Source.Repo != "org/repo" {
269+
t.Errorf("expected repo to be dependabot/cli, got %s", input.Job.Source.Repo)
270+
}
271+
if *input.Job.Source.Hostname != "example.com" {
272+
t.Errorf("expected hostname to be example.com, got %s", *input.Job.Source.Hostname)
273+
}
274+
if *input.Job.Source.APIEndpoint != "https://example.com/api/v3" {
275+
t.Errorf("unexpected API Endpoint %s", *input.Job.Source.APIEndpoint)
276+
}
203277
})
204278
t.Run("test file", func(t *testing.T) {
205279
cmd := NewUpdateCommand()

0 commit comments

Comments
 (0)