From 6950c6ee32a77ed63f871b3d405e517873332e5b Mon Sep 17 00:00:00 2001 From: vilmibm Date: Tue, 9 Nov 2021 13:25:02 -0600 Subject: [PATCH] binary extension create - add a wizard to gh extension create - add --precompiled-go - add --precompiled-other - build out scaffolding for both types of binary extensions --- pkg/cmd/extension/command.go | 142 +++++++++++--- pkg/cmd/extension/command_test.go | 118 +++++++++++- pkg/cmd/extension/ext_tmpls/buildScript.sh | 4 + pkg/cmd/extension/ext_tmpls/goBinMain.go.txt | 26 +++ pkg/cmd/extension/ext_tmpls/goBinWorkflow.yml | 14 ++ .../extension/ext_tmpls/otherBinWorkflow.yml | 16 ++ pkg/cmd/extension/ext_tmpls/script.sh | 35 ++++ pkg/cmd/extension/manager.go | 176 ++++++++++++++---- pkg/cmd/extension/manager_test.go | 60 +++++- pkg/extensions/extension.go | 10 +- pkg/extensions/manager_mock.go | 22 ++- 11 files changed, 539 insertions(+), 84 deletions(-) create mode 100644 pkg/cmd/extension/ext_tmpls/buildScript.sh create mode 100644 pkg/cmd/extension/ext_tmpls/goBinMain.go.txt create mode 100644 pkg/cmd/extension/ext_tmpls/goBinWorkflow.yml create mode 100644 pkg/cmd/extension/ext_tmpls/otherBinWorkflow.yml create mode 100644 pkg/cmd/extension/ext_tmpls/script.sh diff --git a/pkg/cmd/extension/command.go b/pkg/cmd/extension/command.go index 510c2e032..54d604857 100644 --- a/pkg/cmd/extension/command.go +++ b/pkg/cmd/extension/command.go @@ -6,11 +6,13 @@ import ( "os" "strings" + "github.com/AlecAivazis/survey/v2" "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/git" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/extensions" + "github.com/cli/cli/v2/pkg/prompt" "github.com/cli/cli/v2/utils" "github.com/spf13/cobra" ) @@ -180,41 +182,127 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command { return nil }, }, - &cobra.Command{ - Use: "create ", - Short: "Create a new extension", - Args: cmdutil.ExactArgs(1, "must specify a name for the extension"), - RunE: func(cmd *cobra.Command, args []string) error { - extName := args[0] - if !strings.HasPrefix(extName, "gh-") { - extName = "gh-" + extName + func() *cobra.Command { + promptCreate := func() (string, extensions.ExtTemplateType, error) { + var extName string + var extTmplType int + err := prompt.SurveyAskOne(&survey.Input{ + Message: "Extension name:", + }, &extName) + if err != nil { + return extName, -1, err } - if err := m.Create(extName); err != nil { - return err - } - if !io.IsStdoutTTY() { - return nil - } - link := "https://docs.github.com/github-cli/github-cli/creating-github-cli-extensions" - cs := io.ColorScheme() - out := heredoc.Docf(` + err = prompt.SurveyAskOne(&survey.Select{ + Message: "What kind of extension?", + Options: []string{ + "Script (Bash, Ruby, Python, etc)", + "Go", + "Other Precompiled (C++, Rust, etc)", + }, + }, &extTmplType) + return extName, extensions.ExtTemplateType(extTmplType), err + } + var flagType string + cmd := &cobra.Command{ + Use: "create []", + Short: "Create a new extension", + Example: heredoc.Doc(` + # Use interactively + gh extension create + + # Create a script-based extension + gh extension create foobar + + # Create a Go extension + gh extension create --precompiled=go foobar + + # Create a non-Go precompiled extension + gh extension create --precompiled=other foobar + `), + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if cmd.Flags().Changed("precompiled") { + if flagType != "go" && flagType != "other" { + return cmdutil.FlagErrorf("value for --precompiled must be 'go' or 'other'. Got '%s'", flagType) + } + } + var extName string + var err error + tmplType := extensions.GitTemplateType + if len(args) == 0 { + if io.IsStdoutTTY() { + extName, tmplType, err = promptCreate() + if err != nil { + return fmt.Errorf("could not prompt: %w", err) + } + } + } else { + extName = args[0] + if flagType == "go" { + tmplType = extensions.GoBinTemplateType + } else if flagType == "other" { + tmplType = extensions.OtherBinTemplateType + } + } + + var fullName string + + if strings.HasPrefix(extName, "gh-") { + fullName = extName + extName = extName[3:] + } else { + fullName = "gh-" + extName + } + if err := m.Create(fullName, tmplType); err != nil { + return err + } + if !io.IsStdoutTTY() { + return nil + } + + var goBinChecks string + + steps := fmt.Sprintf( + "- run 'cd %[1]s; gh extension install .; gh %[2]s' to see your new extension in action", + fullName, extName) + + cs := io.ColorScheme() + if tmplType == extensions.GoBinTemplateType { + goBinChecks = heredoc.Docf(` + %[1]s Downloaded Go dependencies + %[1]s Built %[2]s binary + `, cs.SuccessIcon(), fullName) + steps = heredoc.Docf(` + - run 'cd %[1]s; gh extension install .; gh %[2]s' to see your new extension in action + - use 'go build && gh %[2]s' to see changes in your code as you develop`, fullName, extName) + } else if tmplType == extensions.OtherBinTemplateType { + steps = heredoc.Docf(` + - run 'cd %[1]s; gh extension install .' to install your extension locally + - fill in script/build.sh with your compilation script for automated builds + - compile a %[1]s binary locally and run 'gh %[2]s' to see changes`, fullName, extName) + } + link := "https://docs.github.com/github-cli/github-cli/creating-github-cli-extensions" + out := heredoc.Docf(` %[1]s Created directory %[2]s %[1]s Initialized git repository %[1]s Set up extension scaffolding + %[6]s + %[2]s is ready for development! - %[2]s is ready for development - - Install locally with: cd %[2]s && gh extension install . - - Publish to GitHub with: gh repo create %[2]s + %[4]s + %[5]s + - commit and use 'gh repo create' to share your extension with others For more information on writing extensions: %[3]s - `, cs.SuccessIcon(), extName, link) - fmt.Fprint(io.Out, out) - return nil - }, - }, + `, cs.SuccessIcon(), fullName, link, cs.Bold("Next Steps"), steps, goBinChecks) + fmt.Fprint(io.Out, out) + return nil + }, + } + cmd.Flags().StringVar(&flagType, "precompiled", "", "Create a precompiled extension. Possible values: go, other") + return cmd + }(), ) return &extCmd diff --git a/pkg/cmd/extension/command_test.go b/pkg/cmd/extension/command_test.go index 839484c3e..4232332de 100644 --- a/pkg/cmd/extension/command_test.go +++ b/pkg/cmd/extension/command_test.go @@ -14,6 +14,7 @@ import ( "github.com/cli/cli/v2/pkg/extensions" "github.com/cli/cli/v2/pkg/httpmock" "github.com/cli/cli/v2/pkg/iostreams" + "github.com/cli/cli/v2/pkg/prompt" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" ) @@ -28,6 +29,7 @@ func TestNewCmdExtension(t *testing.T) { name string args []string managerStubs func(em *extensions.ExtensionManagerMock) func(*testing.T) + askStubs func(as *prompt.AskStubber) isTTY bool wantErr bool errMsg string @@ -263,10 +265,75 @@ func TestNewCmdExtension(t *testing.T) { wantStdout: "gh test\tcli/gh-test\t\ngh test2\tcli/gh-test2\tUpgrade available\n", }, { - name: "create extension tty", - args: []string{"create", "test"}, + name: "create extension interactive", + args: []string{"create"}, managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) { - em.CreateFunc = func(name string) error { + em.CreateFunc = func(name string, tmplType extensions.ExtTemplateType) error { + return nil + } + return func(t *testing.T) { + calls := em.CreateCalls() + assert.Equal(t, 1, len(calls)) + assert.Equal(t, "gh-test", calls[0].Name) + } + }, + isTTY: true, + askStubs: func(as *prompt.AskStubber) { + as.StubOne("test") + as.StubOne(0) + }, + wantStdout: heredoc.Doc(` + ✓ Created directory gh-test + ✓ Initialized git repository + ✓ Set up extension scaffolding + + gh-test is ready for development! + + Next Steps + - run 'cd gh-test; gh extension install .; gh test' to see your new extension in action + - commit and use 'gh repo create' to share your extension with others + + For more information on writing extensions: + https://docs.github.com/github-cli/github-cli/creating-github-cli-extensions + `), + }, + { + name: "create extension with arg, --precompiled=go", + args: []string{"create", "test", "--precompiled", "go"}, + managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) { + em.CreateFunc = func(name string, tmplType extensions.ExtTemplateType) error { + return nil + } + return func(t *testing.T) { + calls := em.CreateCalls() + assert.Equal(t, 1, len(calls)) + assert.Equal(t, "gh-test", calls[0].Name) + } + }, + isTTY: true, + wantStdout: heredoc.Doc(` + ✓ Created directory gh-test + ✓ Initialized git repository + ✓ Set up extension scaffolding + ✓ Downloaded Go dependencies + ✓ Built gh-test binary + + gh-test is ready for development! + + Next Steps + - run 'cd gh-test; gh extension install .; gh test' to see your new extension in action + - use 'go build && gh test' to see changes in your code as you develop + - commit and use 'gh repo create' to share your extension with others + + For more information on writing extensions: + https://docs.github.com/github-cli/github-cli/creating-github-cli-extensions + `), + }, + { + name: "create extension with arg, --precompiled=other", + args: []string{"create", "test", "--precompiled", "other"}, + managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) { + em.CreateFunc = func(name string, tmplType extensions.ExtTemplateType) error { return nil } return func(t *testing.T) { @@ -281,11 +348,42 @@ func TestNewCmdExtension(t *testing.T) { ✓ Initialized git repository ✓ Set up extension scaffolding - gh-test is ready for development + gh-test is ready for development! - Install locally with: cd gh-test && gh extension install . + Next Steps + - run 'cd gh-test; gh extension install .' to install your extension locally + - fill in script/build.sh with your compilation script for automated builds + - compile a gh-test binary locally and run 'gh test' to see changes + - commit and use 'gh repo create' to share your extension with others - Publish to GitHub with: gh repo create gh-test + For more information on writing extensions: + https://docs.github.com/github-cli/github-cli/creating-github-cli-extensions + `), + }, + { + name: "create extension tty with argument", + args: []string{"create", "test"}, + managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) { + em.CreateFunc = func(name string, tmplType extensions.ExtTemplateType) error { + return nil + } + return func(t *testing.T) { + calls := em.CreateCalls() + assert.Equal(t, 1, len(calls)) + assert.Equal(t, "gh-test", calls[0].Name) + } + }, + isTTY: true, + wantStdout: heredoc.Doc(` + ✓ Created directory gh-test + ✓ Initialized git repository + ✓ Set up extension scaffolding + + gh-test is ready for development! + + Next Steps + - run 'cd gh-test; gh extension install .; gh test' to see your new extension in action + - commit and use 'gh repo create' to share your extension with others For more information on writing extensions: https://docs.github.com/github-cli/github-cli/creating-github-cli-extensions @@ -295,7 +393,7 @@ func TestNewCmdExtension(t *testing.T) { name: "create extension notty", args: []string{"create", "gh-test"}, managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) { - em.CreateFunc = func(name string) error { + em.CreateFunc = func(name string, tmplType extensions.ExtTemplateType) error { return nil } return func(t *testing.T) { @@ -321,6 +419,12 @@ func TestNewCmdExtension(t *testing.T) { assertFunc = tt.managerStubs(em) } + as, teardown := prompt.InitAskStubber() + defer teardown() + if tt.askStubs != nil { + tt.askStubs(as) + } + reg := httpmock.Registry{} defer reg.Verify(t) client := http.Client{Transport: ®} diff --git a/pkg/cmd/extension/ext_tmpls/buildScript.sh b/pkg/cmd/extension/ext_tmpls/buildScript.sh new file mode 100644 index 000000000..744667533 --- /dev/null +++ b/pkg/cmd/extension/ext_tmpls/buildScript.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +echo "TODO implement this script." +echo "It should build binaries in dist/-[.exe] as needed." +exit 1 \ No newline at end of file diff --git a/pkg/cmd/extension/ext_tmpls/goBinMain.go.txt b/pkg/cmd/extension/ext_tmpls/goBinMain.go.txt new file mode 100644 index 000000000..9e29dee8b --- /dev/null +++ b/pkg/cmd/extension/ext_tmpls/goBinMain.go.txt @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + + "github.com/cli/go-gh" +) + +func main() { + fmt.Println("hi world, this is the %s extension!") + client, err := gh.RESTClient(nil) + if err != nil { + fmt.Println(err) + return + } + response := struct {Login string}{} + err = client.Get("user", &response) + if err != nil { + fmt.Println(err) + return + } + fmt.Printf("running as %%s\n", response.Login) +} + +// For more examples of using go-gh, see: +// https://github.com/cli/go-gh/blob/trunk/example_gh_test.go \ No newline at end of file diff --git a/pkg/cmd/extension/ext_tmpls/goBinWorkflow.yml b/pkg/cmd/extension/ext_tmpls/goBinWorkflow.yml new file mode 100644 index 000000000..294290de0 --- /dev/null +++ b/pkg/cmd/extension/ext_tmpls/goBinWorkflow.yml @@ -0,0 +1,14 @@ +name: release +on: + push: + tags: + - "v*" +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: cli/gh-extension-precompile@v1 \ No newline at end of file diff --git a/pkg/cmd/extension/ext_tmpls/otherBinWorkflow.yml b/pkg/cmd/extension/ext_tmpls/otherBinWorkflow.yml new file mode 100644 index 000000000..08f077695 --- /dev/null +++ b/pkg/cmd/extension/ext_tmpls/otherBinWorkflow.yml @@ -0,0 +1,16 @@ +name: release +on: + push: + tags: + - "v*" +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: cli/gh-extension-precompile@v1 + with: + build_script_override: "script/build.sh" \ No newline at end of file diff --git a/pkg/cmd/extension/ext_tmpls/script.sh b/pkg/cmd/extension/ext_tmpls/script.sh new file mode 100644 index 000000000..198ec0ee8 --- /dev/null +++ b/pkg/cmd/extension/ext_tmpls/script.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -e + +echo "Hello %[1]s!" + +# Snippets to help get started: + +# Determine if an executable is in the PATH +# if ! type -p ruby >/dev/null; then +# echo "Ruby not found on the system" >&2 +# exit 1 +# fi + +# Pass arguments through to another command +# gh issue list "$@" -R cli/cli + +# Using the gh api command to retrieve and format information +# QUERY=' +# query($endCursor: String) { +# viewer { +# repositories(first: 100, after: $endCursor) { +# nodes { +# nameWithOwner +# stargazerCount +# } +# } +# } +# } +# ' +# TEMPLATE=' +# {{- range $repo := .data.viewer.repositories.nodes -}} +# {{- printf "name: %[2]s - stargazers: %[3]s\n" $repo.nameWithOwner $repo.stargazerCount -}} +# {{- end -}} +# ' +# exec gh api graphql -f query="${QUERY}" --paginate --template="${TEMPLATE}" \ No newline at end of file diff --git a/pkg/cmd/extension/manager.go b/pkg/cmd/extension/manager.go index 5d6764a8f..8ef2f0ff6 100644 --- a/pkg/cmd/extension/manager.go +++ b/pkg/cmd/extension/manager.go @@ -2,6 +2,7 @@ package extension import ( "bytes" + _ "embed" "errors" "fmt" "io" @@ -16,7 +17,6 @@ import ( "strings" "sync" - "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" "github.com/cli/cli/v2/git" "github.com/cli/cli/v2/internal/config" @@ -543,7 +543,22 @@ func (m *Manager) installDir() string { return filepath.Join(m.dataDir(), "extensions") } -func (m *Manager) Create(name string) error { +//go:embed ext_tmpls/goBinMain.go.txt +var mainGoTmpl string + +//go:embed ext_tmpls/goBinWorkflow.yml +var goBinWorkflow []byte + +//go:embed ext_tmpls/otherBinWorkflow.yml +var otherBinWorkflow []byte + +//go:embed ext_tmpls/script.sh +var scriptTmpl string + +//go:embed ext_tmpls/buildScript.sh +var buildScript []byte + +func (m *Manager) Create(name string, tmplType extensions.ExtTemplateType) error { exe, err := m.lookPath("git") if err != nil { return err @@ -560,45 +575,15 @@ func (m *Manager) Create(name string) error { return err } - fileTmpl := heredoc.Docf(` - #!/usr/bin/env bash - set -e + if tmplType == extensions.GoBinTemplateType { + return m.goBinScaffolding(exe, name) + } else if tmplType == extensions.OtherBinTemplateType { + return m.otherBinScaffolding(exe, name) + } - echo "Hello %[1]s!" - - # Snippets to help get started: - - # Determine if an executable is in the PATH - # if ! type -p ruby >/dev/null; then - # echo "Ruby not found on the system" >&2 - # exit 1 - # fi - - # Pass arguments through to another command - # gh issue list "$@" -R cli/cli - - # Using the gh api command to retrieve and format information - # QUERY=' - # query($endCursor: String) { - # viewer { - # repositories(first: 100, after: $endCursor) { - # nodes { - # nameWithOwner - # stargazerCount - # } - # } - # } - # } - # ' - # TEMPLATE=' - # {{- range $repo := .data.viewer.repositories.nodes -}} - # {{- printf "name: %[2]s - stargazers: %[3]s\n" $repo.nameWithOwner $repo.stargazerCount -}} - # {{- end -}} - # ' - # exec gh api graphql -f query="${QUERY}" --paginate --template="${TEMPLATE}" - `, name, "%s", "%v") + script := fmt.Sprintf(scriptTmpl, name, "%s", "%v") filePath := filepath.Join(name, name) - err = ioutil.WriteFile(filePath, []byte(fileTmpl), 0755) + err = ioutil.WriteFile(filePath, []byte(script), 0755) if err != nil { return err } @@ -609,8 +594,119 @@ func (m *Manager) Create(name string) error { } dir := filepath.Join(wd, name) addCmd := m.newCommand(exe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "add", name, "--chmod=+x") + return addCmd.Run() +} + +func (m *Manager) otherBinScaffolding(gitExe, name string) error { + err := os.MkdirAll(filepath.Join(name, ".github", "workflows"), 0755) + if err != nil { + return err + } + workflowPath := filepath.Join(".github", "workflows", "release.yml") + err = ioutil.WriteFile(filepath.Join(name, workflowPath), otherBinWorkflow, 0644) + if err != nil { + return err + } + err = os.Mkdir(filepath.Join(name, "script"), 0755) + if err != nil { + return err + } + buildScriptPath := filepath.Join("script", "build.sh") + err = ioutil.WriteFile(filepath.Join(name, buildScriptPath), buildScript, 0755) + if err != nil { + return err + } + + wd, err := os.Getwd() + if err != nil { + return err + } + dir := filepath.Join(wd, name) + addCmd := m.newCommand(gitExe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "add", buildScriptPath, "--chmod=+x") err = addCmd.Run() - return err + if err != nil { + return err + } + + addCmd = m.newCommand(gitExe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "add", workflowPath) + return addCmd.Run() +} + +func (m *Manager) goBinScaffolding(gitExe, name string) error { + goExe, err := m.lookPath("go") + if err != nil { + return fmt.Errorf("go is required for creating Go extensions: %w", err) + } + err = os.MkdirAll(filepath.Join(name, ".github", "workflows"), 0755) + if err != nil { + return err + } + workflowPath := filepath.Join(".github", "workflows", "release.yml") + err = ioutil.WriteFile(filepath.Join(name, workflowPath), goBinWorkflow, 0644) + if err != nil { + return err + } + + mainGo := fmt.Sprintf(mainGoTmpl, name) + mainPath := "main.go" + + err = ioutil.WriteFile(filepath.Join(name, mainPath), []byte(mainGo), 0644) + if err != nil { + return err + } + + wd, err := os.Getwd() + if err != nil { + return err + } + dir := filepath.Join(wd, name) + + host, err := m.config.DefaultHost() + if err != nil { + return err + } + + currentUser, err := api.CurrentLoginName(api.NewClientFromHTTP(m.client), host) + if err != nil { + return err + } + + goCmds := [][]string{ + {"mod", "init", fmt.Sprintf("%s/%s/%s", host, currentUser, name)}, + {"mod", "tidy"}, + {"build"}, + } + + _, ext := m.platform() + ignore := name + ext + + err = ioutil.WriteFile(filepath.Join(name, ".gitignore"), []byte(ignore), 0644) + if err != nil { + return err + } + + for _, args := range goCmds { + goCmd := m.newCommand(goExe, args...) + goCmd.Dir = dir + err = goCmd.Run() + if err != nil { + return fmt.Errorf("failed to set up go module: %w", err) + } + } + + addArgs := []string{ + "-C", dir, + "--git-dir=" + filepath.Join(dir, ".git"), + "add", + workflowPath, + mainPath, + ".gitignore", + "go.mod", + "go.sum", + } + + addCmd := m.newCommand(gitExe, addArgs...) + return addCmd.Run() } func runCmds(cmds []*exec.Cmd) error { diff --git a/pkg/cmd/extension/manager_test.go b/pkg/cmd/extension/manager_test.go index 70bbfed42..6320195a1 100644 --- a/pkg/cmd/extension/manager_test.go +++ b/pkg/cmd/extension/manager_test.go @@ -15,6 +15,7 @@ import ( "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/ghrepo" "github.com/cli/cli/v2/internal/run" + "github.com/cli/cli/v2/pkg/extensions" "github.com/cli/cli/v2/pkg/httpmock" "github.com/cli/cli/v2/pkg/iostreams" "github.com/stretchr/testify/assert" @@ -582,7 +583,7 @@ func TestManager_Create(t *testing.T) { assert.NoError(t, os.Chdir(tempDir)) t.Cleanup(func() { _ = os.Chdir(oldWd) }) m := newTestManager(tempDir, nil, nil) - err := m.Create("gh-test") + err := m.Create("gh-test", extensions.GitTemplateType) assert.NoError(t, err) files, err := ioutil.ReadDir(filepath.Join(tempDir, "gh-test")) assert.NoError(t, err) @@ -596,6 +597,63 @@ func TestManager_Create(t *testing.T) { } } +func TestManager_Create_go_binary(t *testing.T) { + tempDir := t.TempDir() + oldWd, _ := os.Getwd() + assert.NoError(t, os.Chdir(tempDir)) + t.Cleanup(func() { _ = os.Chdir(oldWd) }) + reg := httpmock.Registry{} + defer reg.Verify(t) + client := http.Client{Transport: ®} + + reg.Register( + httpmock.GraphQL(`query UserCurrent\b`), + httpmock.StringResponse(`{"data":{"viewer":{"login":"jillv"}}}`)) + m := newTestManager(tempDir, &client, nil) + + err := m.Create("gh-test", extensions.GoBinTemplateType) + assert.NoError(t, err) + + files, err := ioutil.ReadDir(filepath.Join(tempDir, "gh-test")) + assert.NoError(t, err) + assert.Equal(t, 3, len(files)) + assert.Equal(t, ".gitignore", files[1].Name()) + assert.Equal(t, "main.go", files[2].Name()) + + files, err = ioutil.ReadDir(filepath.Join(tempDir, "gh-test", ".github", "workflows")) + assert.NoError(t, err) + assert.Equal(t, 1, len(files)) + workflowFile := files[0] + assert.Equal(t, "release.yml", workflowFile.Name()) +} + +func TestManager_Create_other_binary(t *testing.T) { + tempDir := t.TempDir() + oldWd, _ := os.Getwd() + assert.NoError(t, os.Chdir(tempDir)) + t.Cleanup(func() { _ = os.Chdir(oldWd) }) + m := newTestManager(tempDir, nil, nil) + + err := m.Create("gh-test", extensions.OtherBinTemplateType) + assert.NoError(t, err) + + files, err := ioutil.ReadDir(filepath.Join(tempDir, "gh-test")) + assert.NoError(t, err) + assert.Equal(t, 2, len(files)) + + files, err = ioutil.ReadDir(filepath.Join(tempDir, "gh-test", ".github", "workflows")) + assert.NoError(t, err) + assert.Equal(t, 1, len(files)) + workflowFile := files[0] + assert.Equal(t, "release.yml", workflowFile.Name()) + + files, err = ioutil.ReadDir(filepath.Join(tempDir, "gh-test", "script")) + assert.NoError(t, err) + assert.Equal(t, 1, len(files)) + buildFile := files[0] + assert.Equal(t, "build.sh", buildFile.Name()) +} + func stubExtension(path string) error { if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { return err diff --git a/pkg/extensions/extension.go b/pkg/extensions/extension.go index ee55bfabc..5826f4cd6 100644 --- a/pkg/extensions/extension.go +++ b/pkg/extensions/extension.go @@ -6,6 +6,14 @@ import ( "github.com/cli/cli/v2/internal/ghrepo" ) +type ExtTemplateType int + +const ( + GitTemplateType ExtTemplateType = 0 + GoBinTemplateType ExtTemplateType = 1 + OtherBinTemplateType ExtTemplateType = 2 +) + //go:generate moq -rm -out extension_mock.go . Extension type Extension interface { Name() string // Extension Name without gh- @@ -24,5 +32,5 @@ type ExtensionManager interface { Upgrade(name string, force bool) error Remove(name string) error Dispatch(args []string, stdin io.Reader, stdout, stderr io.Writer) (bool, error) - Create(name string) error + Create(name string, tmplType ExtTemplateType) error } diff --git a/pkg/extensions/manager_mock.go b/pkg/extensions/manager_mock.go index c681efe7d..80e1efd57 100644 --- a/pkg/extensions/manager_mock.go +++ b/pkg/extensions/manager_mock.go @@ -19,7 +19,7 @@ var _ ExtensionManager = &ExtensionManagerMock{} // // // make and configure a mocked ExtensionManager // mockedExtensionManager := &ExtensionManagerMock{ -// CreateFunc: func(name string) error { +// CreateFunc: func(name string, tmplType ExtTemplateType) error { // panic("mock out the Create method") // }, // DispatchFunc: func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error) { @@ -48,7 +48,7 @@ var _ ExtensionManager = &ExtensionManagerMock{} // } type ExtensionManagerMock struct { // CreateFunc mocks the Create method. - CreateFunc func(name string) error + CreateFunc func(name string, tmplType ExtTemplateType) error // DispatchFunc mocks the Dispatch method. DispatchFunc func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error) @@ -74,6 +74,8 @@ type ExtensionManagerMock struct { Create []struct { // Name is the name argument value. Name string + // TmplType is the tmplType argument value. + TmplType ExtTemplateType } // Dispatch holds details about calls to the Dispatch method. Dispatch []struct { @@ -124,29 +126,33 @@ type ExtensionManagerMock struct { } // Create calls CreateFunc. -func (mock *ExtensionManagerMock) Create(name string) error { +func (mock *ExtensionManagerMock) Create(name string, tmplType ExtTemplateType) error { if mock.CreateFunc == nil { panic("ExtensionManagerMock.CreateFunc: method is nil but ExtensionManager.Create was just called") } callInfo := struct { - Name string + Name string + TmplType ExtTemplateType }{ - Name: name, + Name: name, + TmplType: tmplType, } mock.lockCreate.Lock() mock.calls.Create = append(mock.calls.Create, callInfo) mock.lockCreate.Unlock() - return mock.CreateFunc(name) + return mock.CreateFunc(name, tmplType) } // CreateCalls gets all the calls that were made to Create. // Check the length with: // len(mockedExtensionManager.CreateCalls()) func (mock *ExtensionManagerMock) CreateCalls() []struct { - Name string + Name string + TmplType ExtTemplateType } { var calls []struct { - Name string + Name string + TmplType ExtTemplateType } mock.lockCreate.RLock() calls = mock.calls.Create