Add extension create command
This commit is contained in:
parent
c84bfa9e66
commit
e9f7459ce2
6 changed files with 169 additions and 0 deletions
|
|
@ -141,6 +141,39 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
|
|||
return nil
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "create <name>",
|
||||
Short: "Create a new extension",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
extName := args[0]
|
||||
if !strings.HasPrefix(extName, "gh-") {
|
||||
extName = "gh-" + extName
|
||||
}
|
||||
if err := m.Create(extName); err != nil {
|
||||
return err
|
||||
}
|
||||
if io.IsStdoutTTY() {
|
||||
link := "https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions"
|
||||
cs := io.ColorScheme()
|
||||
out := heredoc.Docf(`
|
||||
%[1]s Created directory %[2]s
|
||||
%[1]s Initialized git repository
|
||||
%[1]s Set up extension scaffolding
|
||||
|
||||
%[2]s is ready for development
|
||||
|
||||
Install locally with: cd %[2]s && gh extension install .
|
||||
|
||||
Publish to GitHub with: gh repo create %[2]s
|
||||
|
||||
For more information on writing extensions: %[3]s
|
||||
`, cs.SuccessIcon(), extName, link)
|
||||
fmt.Fprint(io.Out, out)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
extCmd.Hidden = true
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/internal/config"
|
||||
"github.com/cli/cli/pkg/cmdutil"
|
||||
"github.com/cli/cli/pkg/extensions"
|
||||
|
|
@ -161,6 +162,50 @@ 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"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.CreateFunc = func(name string) 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
|
||||
|
||||
Install locally with: cd gh-test && gh extension install .
|
||||
|
||||
Publish to GitHub with: gh repo create gh-test
|
||||
|
||||
For more information on writing extensions: https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "create extension notty",
|
||||
args: []string{"create", "gh-test"},
|
||||
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
|
||||
em.CreateFunc = func(name string) 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: false,
|
||||
wantStdout: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/internal/config"
|
||||
"github.com/cli/cli/pkg/extensions"
|
||||
"github.com/cli/cli/pkg/findsh"
|
||||
|
|
@ -249,6 +250,32 @@ func (m *Manager) installDir() string {
|
|||
return filepath.Join(m.dataDir(), "extensions")
|
||||
}
|
||||
|
||||
func (m *Manager) Create(name string) error {
|
||||
exe, err := m.lookPath("git")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Mkdir(name, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
initCmd := m.newCommand(exe, "init", "-b", "main", "--quiet", name)
|
||||
err = initCmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileTmpl := heredoc.Docf(`
|
||||
#!/bin/bash
|
||||
echo "Hello %[1]s"
|
||||
`, name)
|
||||
filePath := filepath.Join(name, name)
|
||||
err = ioutil.WriteFile(filePath, []byte(fileTmpl), 0755)
|
||||
return err
|
||||
}
|
||||
|
||||
func runCmds(cmds []*exec.Cmd, stdout, stderr io.Writer) error {
|
||||
for _, cmd := range cmds {
|
||||
cmd.Stdout = stdout
|
||||
|
|
|
|||
|
|
@ -202,6 +202,26 @@ func TestManager_Install(t *testing.T) {
|
|||
assert.Equal(t, "", stderr.String())
|
||||
}
|
||||
|
||||
func TestManager_Create(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
oldWd, _ := os.Getwd()
|
||||
assert.NoError(t, os.Chdir(tempDir))
|
||||
t.Cleanup(func() { _ = os.Chdir(oldWd) })
|
||||
m := newTestManager(tempDir)
|
||||
err := m.Create("gh-test")
|
||||
assert.NoError(t, err)
|
||||
files, err := ioutil.ReadDir(filepath.Join(tempDir, "gh-test"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(files))
|
||||
extFile := files[0]
|
||||
assert.Equal(t, "gh-test", extFile.Name())
|
||||
if runtime.GOOS == "windows" {
|
||||
assert.Equal(t, os.FileMode(0666), extFile.Mode())
|
||||
} else {
|
||||
assert.Equal(t, os.FileMode(0755), extFile.Mode())
|
||||
}
|
||||
}
|
||||
|
||||
func stubExtension(path string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -21,4 +21,5 @@ type ExtensionManager interface {
|
|||
Upgrade(name string, force bool, stdout, stderr io.Writer) error
|
||||
Remove(name string) error
|
||||
Dispatch(args []string, stdin io.Reader, stdout, stderr io.Writer) (bool, error)
|
||||
Create(name string) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ var _ ExtensionManager = &ExtensionManagerMock{}
|
|||
//
|
||||
// // make and configure a mocked ExtensionManager
|
||||
// mockedExtensionManager := &ExtensionManagerMock{
|
||||
// CreateFunc: func(name string) error {
|
||||
// panic("mock out the Create method")
|
||||
// },
|
||||
// DispatchFunc: func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error) {
|
||||
// panic("mock out the Dispatch method")
|
||||
// },
|
||||
|
|
@ -43,6 +46,9 @@ var _ ExtensionManager = &ExtensionManagerMock{}
|
|||
//
|
||||
// }
|
||||
type ExtensionManagerMock struct {
|
||||
// CreateFunc mocks the Create method.
|
||||
CreateFunc func(name string) error
|
||||
|
||||
// DispatchFunc mocks the Dispatch method.
|
||||
DispatchFunc func(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error)
|
||||
|
||||
|
|
@ -63,6 +69,11 @@ type ExtensionManagerMock struct {
|
|||
|
||||
// calls tracks calls to the methods.
|
||||
calls struct {
|
||||
// Create holds details about calls to the Create method.
|
||||
Create []struct {
|
||||
// Name is the name argument value.
|
||||
Name string
|
||||
}
|
||||
// Dispatch holds details about calls to the Dispatch method.
|
||||
Dispatch []struct {
|
||||
// Args is the args argument value.
|
||||
|
|
@ -110,6 +121,7 @@ type ExtensionManagerMock struct {
|
|||
Stderr io.Writer
|
||||
}
|
||||
}
|
||||
lockCreate sync.RWMutex
|
||||
lockDispatch sync.RWMutex
|
||||
lockInstall sync.RWMutex
|
||||
lockInstallLocal sync.RWMutex
|
||||
|
|
@ -118,6 +130,37 @@ type ExtensionManagerMock struct {
|
|||
lockUpgrade sync.RWMutex
|
||||
}
|
||||
|
||||
// Create calls CreateFunc.
|
||||
func (mock *ExtensionManagerMock) Create(name string) error {
|
||||
if mock.CreateFunc == nil {
|
||||
panic("ExtensionManagerMock.CreateFunc: method is nil but ExtensionManager.Create was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Name string
|
||||
}{
|
||||
Name: name,
|
||||
}
|
||||
mock.lockCreate.Lock()
|
||||
mock.calls.Create = append(mock.calls.Create, callInfo)
|
||||
mock.lockCreate.Unlock()
|
||||
return mock.CreateFunc(name)
|
||||
}
|
||||
|
||||
// CreateCalls gets all the calls that were made to Create.
|
||||
// Check the length with:
|
||||
// len(mockedExtensionManager.CreateCalls())
|
||||
func (mock *ExtensionManagerMock) CreateCalls() []struct {
|
||||
Name string
|
||||
} {
|
||||
var calls []struct {
|
||||
Name string
|
||||
}
|
||||
mock.lockCreate.RLock()
|
||||
calls = mock.calls.Create
|
||||
mock.lockCreate.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Dispatch calls DispatchFunc.
|
||||
func (mock *ExtensionManagerMock) Dispatch(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (bool, error) {
|
||||
if mock.DispatchFunc == nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue