X Tutup
Skip to content

Commit b5d90e1

Browse files
author
Nate Smith
authored
Merge pull request cli#4588 from cli/bin-ext-migrate
binary extension migration
2 parents 0a0a358 + 54b82dd commit b5d90e1

File tree

4 files changed

+147
-9
lines changed

4 files changed

+147
-9
lines changed

git/git.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ func CurrentBranch() (string, error) {
8484
return "", fmt.Errorf("%sgit: %s", stderr.String(), err)
8585
}
8686

87+
func listRemotesForPath(path string) ([]string, error) {
88+
remoteCmd, err := GitCommand("-C", path, "remote", "-v")
89+
if err != nil {
90+
return nil, err
91+
}
92+
output, err := run.PrepareCmd(remoteCmd).Output()
93+
return outputLines(output), err
94+
}
95+
8796
func listRemotes() ([]string, error) {
8897
remoteCmd, err := GitCommand("remote", "-v")
8998
if err != nil {

git/remote.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,11 @@ func (r *Remote) String() string {
3535
return r.Name
3636
}
3737

38-
// Remotes gets the git remotes set for the current repo
39-
func Remotes() (RemoteSet, error) {
40-
list, err := listRemotes()
41-
if err != nil {
42-
return nil, err
43-
}
44-
remotes := parseRemotes(list)
38+
func remotes(path string, remoteList []string) (RemoteSet, error) {
39+
remotes := parseRemotes(remoteList)
4540

4641
// this is affected by SetRemoteResolution
47-
remoteCmd, err := GitCommand("config", "--get-regexp", `^remote\..*\.gh-resolved$`)
42+
remoteCmd, err := GitCommand("-C", path, "config", "--get-regexp", `^remote\..*\.gh-resolved$`)
4843
if err != nil {
4944
return nil, err
5045
}
@@ -70,6 +65,23 @@ func Remotes() (RemoteSet, error) {
7065
return remotes, nil
7166
}
7267

68+
func RemotesForPath(path string) (RemoteSet, error) {
69+
list, err := listRemotesForPath(path)
70+
if err != nil {
71+
return nil, err
72+
}
73+
return remotes(path, list)
74+
}
75+
76+
// Remotes gets the git remotes set for the current repo
77+
func Remotes() (RemoteSet, error) {
78+
list, err := listRemotes()
79+
if err != nil {
80+
return nil, err
81+
}
82+
return remotes(".", list)
83+
}
84+
7385
func parseRemotes(gitRemotes []string) (remotes RemoteSet) {
7486
for _, r := range gitRemotes {
7587
match := remoteRE.FindStringSubmatch(r)

pkg/cmd/extension/manager.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
"github.com/MakeNowJust/heredoc"
2020
"github.com/cli/cli/v2/api"
21+
"github.com/cli/cli/v2/git"
2122
"github.com/cli/cli/v2/internal/config"
2223
"github.com/cli/cli/v2/internal/ghrepo"
2324
"github.com/cli/cli/v2/pkg/extensions"
@@ -333,7 +334,6 @@ func (m *Manager) Install(repo ghrepo.Interface) error {
333334
return err
334335
}
335336
if !hs {
336-
// TODO open an issue hint, here?
337337
return errors.New("extension is uninstallable: missing executable")
338338
}
339339

@@ -487,6 +487,19 @@ func (m *Manager) upgradeExtension(ext Extension, force bool) error {
487487
if ext.IsBinary() {
488488
err = m.upgradeBinExtension(ext)
489489
} else {
490+
// Check if git extension has changed to a binary extension
491+
var isBin bool
492+
repo, repoErr := repoFromPath(filepath.Join(ext.Path(), ".."))
493+
if repoErr == nil {
494+
isBin, _ = isBinExtension(m.client, repo)
495+
}
496+
if isBin {
497+
err = m.Remove(ext.Name())
498+
if err != nil {
499+
return fmt.Errorf("failed to migrate to new precompiled extension format: %w", err)
500+
}
501+
return m.installBin(repo)
502+
}
490503
err = m.upgradeGitExtension(ext, force)
491504
}
492505
return err
@@ -654,6 +667,32 @@ func isBinExtension(client *http.Client, repo ghrepo.Interface) (isBin bool, err
654667
return
655668
}
656669

670+
func repoFromPath(path string) (ghrepo.Interface, error) {
671+
remotes, err := git.RemotesForPath(path)
672+
if err != nil {
673+
return nil, err
674+
}
675+
676+
if len(remotes) == 0 {
677+
return nil, fmt.Errorf("no remotes configured for %s", path)
678+
}
679+
680+
var remote *git.Remote
681+
682+
for _, r := range remotes {
683+
if r.Name == "origin" {
684+
remote = r
685+
break
686+
}
687+
}
688+
689+
if remote == nil {
690+
remote = remotes[0]
691+
}
692+
693+
return ghrepo.FromURL(remote.FetchURL)
694+
}
695+
657696
func possibleDists() []string {
658697
return []string{
659698
"aix-ppc64",

pkg/cmd/extension/manager_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/MakeNowJust/heredoc"
1515
"github.com/cli/cli/v2/internal/config"
1616
"github.com/cli/cli/v2/internal/ghrepo"
17+
"github.com/cli/cli/v2/internal/run"
1718
"github.com/cli/cli/v2/pkg/httpmock"
1819
"github.com/cli/cli/v2/pkg/iostreams"
1920
"github.com/stretchr/testify/assert"
@@ -297,6 +298,83 @@ func TestManager_UpgradeExtension_GitExtension_Force(t *testing.T) {
297298
assert.Equal(t, "", stderr.String())
298299
}
299300

301+
func TestManager_MigrateToBinaryExtension(t *testing.T) {
302+
tempDir := t.TempDir()
303+
assert.NoError(t, stubExtension(filepath.Join(tempDir, "extensions", "gh-remote", "gh-remote")))
304+
io, _, stdout, stderr := iostreams.Test()
305+
306+
reg := httpmock.Registry{}
307+
defer reg.Verify(t)
308+
client := http.Client{Transport: &reg}
309+
m := newTestManager(tempDir, &client, io)
310+
exts, err := m.list(false)
311+
assert.NoError(t, err)
312+
assert.Equal(t, 1, len(exts))
313+
ext := exts[0]
314+
ext.currentVersion = "old version"
315+
ext.latestVersion = "new version"
316+
317+
rs, restoreRun := run.Stub()
318+
defer restoreRun(t)
319+
320+
rs.Register(`git -C.*?gh-remote remote -v`, 0, "origin git@github.com:owner/gh-remote.git (fetch)\norigin git@github.com:owner/gh-remote.git (push)")
321+
rs.Register(`git -C.*?gh-remote config --get-regexp \^.*`, 0, "remote.origin.gh-resolve base")
322+
323+
reg.Register(
324+
httpmock.REST("GET", "repos/owner/gh-remote/releases/latest"),
325+
httpmock.JSONResponse(
326+
release{
327+
Tag: "v1.0.2",
328+
Assets: []releaseAsset{
329+
{
330+
Name: "gh-remote-windows-amd64.exe",
331+
APIURL: "/release/cool",
332+
},
333+
},
334+
}))
335+
reg.Register(
336+
httpmock.REST("GET", "repos/owner/gh-remote/releases/latest"),
337+
httpmock.JSONResponse(
338+
release{
339+
Tag: "v1.0.2",
340+
Assets: []releaseAsset{
341+
{
342+
Name: "gh-remote-windows-amd64.exe",
343+
APIURL: "/release/cool",
344+
},
345+
},
346+
}))
347+
reg.Register(
348+
httpmock.REST("GET", "release/cool"),
349+
httpmock.StringResponse("FAKE UPGRADED BINARY"))
350+
351+
err = m.upgradeExtension(ext, false)
352+
assert.NoError(t, err)
353+
354+
assert.Equal(t, "", stdout.String())
355+
assert.Equal(t, "", stderr.String())
356+
357+
manifest, err := os.ReadFile(filepath.Join(tempDir, "extensions/gh-remote", manifestName))
358+
assert.NoError(t, err)
359+
360+
var bm binManifest
361+
err = yaml.Unmarshal(manifest, &bm)
362+
assert.NoError(t, err)
363+
364+
assert.Equal(t, binManifest{
365+
Name: "gh-remote",
366+
Owner: "owner",
367+
Host: "github.com",
368+
Tag: "v1.0.2",
369+
Path: filepath.Join(tempDir, "extensions/gh-remote/gh-remote.exe"),
370+
}, bm)
371+
372+
fakeBin, err := os.ReadFile(filepath.Join(tempDir, "extensions/gh-remote/gh-remote.exe"))
373+
assert.NoError(t, err)
374+
375+
assert.Equal(t, "FAKE UPGRADED BINARY", string(fakeBin))
376+
}
377+
300378
func TestManager_UpgradeExtension_BinaryExtension(t *testing.T) {
301379
tempDir := t.TempDir()
302380
io, _, _, _ := iostreams.Test()

0 commit comments

Comments
 (0)
X Tutup