Skip to content

Commit e9f7459

Browse files
committed
Add extension create command
1 parent c84bfa9 commit e9f7459

File tree

6 files changed

+169
-0
lines changed

6 files changed

+169
-0
lines changed

pkg/cmd/extension/command.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,39 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
141141
return nil
142142
},
143143
},
144+
&cobra.Command{
145+
Use: "create <name>",
146+
Short: "Create a new extension",
147+
Args: cobra.ExactArgs(1),
148+
RunE: func(cmd *cobra.Command, args []string) error {
149+
extName := args[0]
150+
if !strings.HasPrefix(extName, "gh-") {
151+
extName = "gh-" + extName
152+
}
153+
if err := m.Create(extName); err != nil {
154+
return err
155+
}
156+
if io.IsStdoutTTY() {
157+
link := "https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions"
158+
cs := io.ColorScheme()
159+
out := heredoc.Docf(`
160+
%[1]s Created directory %[2]s
161+
%[1]s Initialized git repository
162+
%[1]s Set up extension scaffolding
163+
164+
%[2]s is ready for development
165+
166+
Install locally with: cd %[2]s && gh extension install .
167+
168+
Publish to GitHub with: gh repo create %[2]s
169+
170+
For more information on writing extensions: %[3]s
171+
`, cs.SuccessIcon(), extName, link)
172+
fmt.Fprint(io.Out, out)
173+
}
174+
return nil
175+
},
176+
},
144177
)
145178

146179
extCmd.Hidden = true

pkg/cmd/extension/command_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88
"testing"
99

10+
"github.com/MakeNowJust/heredoc"
1011
"github.com/cli/cli/internal/config"
1112
"github.com/cli/cli/pkg/cmdutil"
1213
"github.com/cli/cli/pkg/extensions"
@@ -161,6 +162,50 @@ func TestNewCmdExtension(t *testing.T) {
161162
},
162163
wantStdout: "gh test\tcli/gh-test\t\ngh test2\tcli/gh-test2\tUpgrade available\n",
163164
},
165+
{
166+
name: "create extension tty",
167+
args: []string{"create", "test"},
168+
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
169+
em.CreateFunc = func(name string) error {
170+
return nil
171+
}
172+
return func(t *testing.T) {
173+
calls := em.CreateCalls()
174+
assert.Equal(t, 1, len(calls))
175+
assert.Equal(t, "gh-test", calls[0].Name)
176+
}
177+
},
178+
isTTY: true,
179+
wantStdout: heredoc.Doc(`
180+
✓ Created directory gh-test
181+
✓ Initialized git repository
182+
✓ Set up extension scaffolding
183+
184+
gh-test is ready for development
185+
186+
Install locally with: cd gh-test && gh extension install .
187+
188+
Publish to GitHub with: gh repo create gh-test
189+
190+
For more information on writing extensions: https://docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions
191+
`),
192+
},
193+
{
194+
name: "create extension notty",
195+
args: []string{"create", "gh-test"},
196+
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
197+
em.CreateFunc = func(name string) error {
198+
return nil
199+
}
200+
return func(t *testing.T) {
201+
calls := em.CreateCalls()
202+
assert.Equal(t, 1, len(calls))
203+
assert.Equal(t, "gh-test", calls[0].Name)
204+
}
205+
},
206+
isTTY: false,
207+
wantStdout: "",
208+
},
164209
}
165210

166211
for _, tt := range tests {

pkg/cmd/extension/manager.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"runtime"
1414
"strings"
1515

16+
"github.com/MakeNowJust/heredoc"
1617
"github.com/cli/cli/internal/config"
1718
"github.com/cli/cli/pkg/extensions"
1819
"github.com/cli/cli/pkg/findsh"
@@ -249,6 +250,32 @@ func (m *Manager) installDir() string {
249250
return filepath.Join(m.dataDir(), "extensions")
250251
}
251252

253+
func (m *Manager) Create(name string) error {
254+
exe, err := m.lookPath("git")
255+
if err != nil {
256+
return err
257+
}
258+
259+
err = os.Mkdir(name, 0755)
260+
if err != nil {
261+
return err
262+
}
263+
264+
initCmd := m.newCommand(exe, "init", "-b", "main", "--quiet", name)
265+
err = initCmd.Run()
266+
if err != nil {
267+
return err
268+
}
269+
270+
fileTmpl := heredoc.Docf(`
271+
#!/bin/bash
272+
echo "Hello %[1]s"
273+
`, name)
274+
filePath := filepath.Join(name, name)
275+
err = ioutil.WriteFile(filePath, []byte(fileTmpl), 0755)
276+
return err
277+
}
278+
252279
func runCmds(cmds []*exec.Cmd, stdout, stderr io.Writer) error {
253280
for _, cmd := range cmds {
254281
cmd.Stdout = stdout

pkg/cmd/extension/manager_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,26 @@ func TestManager_Install(t *testing.T) {
202202
assert.Equal(t, "", stderr.String())
203203
}
204204

205+
func TestManager_Create(t *testing.T) {
206+
tempDir := t.TempDir()
207+
oldWd, _ := os.Getwd()
208+
assert.NoError(t, os.Chdir(tempDir))
209+
t.Cleanup(func() { _ = os.Chdir(oldWd) })
210+
m := newTestManager(tempDir)
211+
err := m.Create("gh-test")
212+
assert.NoError(t, err)
213+
files, err := ioutil.ReadDir(filepath.Join(tempDir, "gh-test"))
214+
assert.NoError(t, err)
215+
assert.Equal(t, 1, len(files))
216+
extFile := files[0]
217+
assert.Equal(t, "gh-test", extFile.Name())
218+
if runtime.GOOS == "windows" {
219+
assert.Equal(t, os.FileMode(0666), extFile.Mode())
220+
} else {
221+
assert.Equal(t, os.FileMode(0755), extFile.Mode())
222+
}
223+
}
224+
205225
func stubExtension(path string) error {
206226
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
207227
return err

pkg/extensions/extension.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ type ExtensionManager interface {
2121
Upgrade(name string, force bool, stdout, stderr io.Writer) error
2222
Remove(name string) error
2323
Dispatch(args []string, stdin io.Reader, stdout, stderr io.Writer) (bool, error)
24+
Create(name string) error
2425
}

pkg/extensions/manager_mock.go

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)