Add project open
This commit is contained in:
parent
b7960ed1b4
commit
bf213be9fb
@ -54,6 +54,7 @@ cache:
|
||||
```
|
||||
## TODO
|
||||
|
||||
- [ ] Fix initial setup requiring project path, and set https:// as default for gitlab host
|
||||
- [ ] Fix NPE when cache is reset or project for whatever reason leaves an orphaned alias
|
||||
- [ ] Add config setters and getters
|
||||
- [ ] Add TTL check to cache load, and add -f / --force flag to re-build regardless
|
||||
@ -64,6 +65,7 @@ cache:
|
||||
- [ ] Add open command
|
||||
- [ ] config should exist for editor (vim, code, etc..)
|
||||
- [ ] Update README for shell completion, aliases, usage
|
||||
- [ ] Add fzf to `plist` / `gpm projects list`
|
||||
- [ ] Make a Makefile
|
||||
- [ ] Add git repo status to project go (up-to-date, pending commits, etc..)
|
||||
- [x] Update `gpm project show` with pterm box like `gpm project list`
|
||||
|
185
cmd/project_open.go
Normal file
185
cmd/project_open.go
Normal file
@ -0,0 +1,185 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var projectOpenCmd = &cobra.Command{
|
||||
Use: "open [fuzzy alias search]",
|
||||
Short: "Open project in your IDE",
|
||||
Aliases: []string{"goto", "cd"},
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
ArgAliases: []string{"project"},
|
||||
ValidArgsFunction: validAliasesFunc,
|
||||
Long: projOpenCmdLong,
|
||||
Run: projectOpenCmdRun,
|
||||
}
|
||||
|
||||
var knownEditors = []string{
|
||||
"vim",
|
||||
"emacs",
|
||||
"code",
|
||||
"codium",
|
||||
"/Applications/GoLand.app/Contents/MacOS/goland",
|
||||
}
|
||||
|
||||
var entrypointFiles = []string{
|
||||
"README.md",
|
||||
"README",
|
||||
"main.go",
|
||||
"main.py",
|
||||
"app.py",
|
||||
"index.php",
|
||||
"server.php",
|
||||
"index.js",
|
||||
"app.js",
|
||||
"server.js",
|
||||
"app.ts",
|
||||
"main.c",
|
||||
"main.cpp",
|
||||
}
|
||||
|
||||
func projectOpenCmdRun(cmd *cobra.Command, args []string) {
|
||||
// Find an editor
|
||||
editor := findEditor()
|
||||
if editor == "" {
|
||||
plog.Fatal("No usable editor found")
|
||||
}
|
||||
|
||||
// Identify search terms
|
||||
var searchTerm string
|
||||
if len(args) > 0 {
|
||||
searchTerm = args[0]
|
||||
}
|
||||
|
||||
// Find a project
|
||||
project := fzfSearchProjectAliases(searchTerm)
|
||||
if project == nil {
|
||||
plog.Fatal("No project to open, nothing to do")
|
||||
}
|
||||
|
||||
// Check the project
|
||||
path := cache.GetProjectPath(project)
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
plog.Fatal("Unable to open project", plog.Args("error", err))
|
||||
}
|
||||
|
||||
// Open the project with the editor
|
||||
file := getEntrypointFile(path)
|
||||
openEditor(editor, path+"/"+file)
|
||||
}
|
||||
|
||||
func openEditor(editor string, path string) {
|
||||
// Compile arguments
|
||||
args := make([]string, 0, 1)
|
||||
if conf.Editor.OpenFlags != "" {
|
||||
args = append(args, conf.Editor.OpenFlags)
|
||||
}
|
||||
args = append(args, path)
|
||||
|
||||
// Launch editor
|
||||
cmd := exec.CommandContext(rootCmd.Context(), editor, args...)
|
||||
cmd.Dir = filepath.Dir(path)
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
plog.Error("Failed to open project", plog.Args(
|
||||
"error", err.Error(),
|
||||
"command", cmd.String(),
|
||||
))
|
||||
}
|
||||
|
||||
cmd.Wait()
|
||||
}
|
||||
|
||||
func getEntrypointFile(projectPath string) string {
|
||||
if err := os.Chdir(projectPath); err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, f := range entrypointFiles {
|
||||
if _, err := os.Stat(f); err == nil {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findEditor() string {
|
||||
var editor string
|
||||
var err error
|
||||
|
||||
// First try configured editor
|
||||
if conf.Editor.Binary != "" {
|
||||
editor, err = getEditor(conf.Editor.Binary)
|
||||
if err != nil || editor == "" {
|
||||
plog.Error("Configured editor is not usable, finding others", plog.Args(
|
||||
"error", err,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Then try to find a known editor
|
||||
if editor == "" || err != nil {
|
||||
conf.Editor.OpenFlags = ""
|
||||
for _, e := range knownEditors {
|
||||
path, err := getEditor(e)
|
||||
if err == nil && path != "" {
|
||||
editor = path
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plog.Debug("Editor found for open", plog.Args(
|
||||
"editor", editor,
|
||||
"command", editor+" "+conf.Editor.OpenFlags,
|
||||
))
|
||||
|
||||
return editor
|
||||
}
|
||||
|
||||
func getEditor(editor string) (string, error) {
|
||||
path, err := getEditorPath(editor)
|
||||
if path != "" && err == nil {
|
||||
if !isEditorExecutable(path) {
|
||||
err = errors.New("Editor is not executable")
|
||||
}
|
||||
}
|
||||
return path, err
|
||||
}
|
||||
|
||||
func getEditorPath(editor string) (string, error) {
|
||||
// Check path first
|
||||
if editor[0] != '/' {
|
||||
editor, _ = exec.LookPath(editor)
|
||||
}
|
||||
|
||||
return resolvePath(editor)
|
||||
}
|
||||
|
||||
func isEditorExecutable(editor string) bool {
|
||||
var canExec bool
|
||||
|
||||
stat, err := os.Stat(editor)
|
||||
|
||||
if err == nil && (stat.Mode()&0444 != 0 && stat.Mode()&0111 != 0) {
|
||||
canExec = true
|
||||
}
|
||||
|
||||
return canExec
|
||||
}
|
||||
|
||||
func init() {
|
||||
projectCmd.AddCommand(projectOpenCmd)
|
||||
projectOpenCmd.PersistentFlags().String("displayName", "", "Set display name of editor (meant for config file)")
|
||||
projectOpenCmd.PersistentFlags().String("binary", "", "Path to editor binary")
|
||||
projectOpenCmd.PersistentFlags().String("openFlags", "", "Optional flags when opening project (e.g. --reuse-window)")
|
||||
viper.BindPFlag("editor.displayName", projectOpenCmd.Flag("displayName"))
|
||||
viper.BindPFlag("editor.binary", projectOpenCmd.Flag("binary"))
|
||||
viper.BindPFlag("editor.openFlags", projectOpenCmd.Flag("openFlags"))
|
||||
}
|
@ -44,6 +44,12 @@ uses fuzzy find to locate the project`
|
||||
const projShowCmdLong = `Shows detail for a particular project
|
||||
Will always fuzzy find`
|
||||
|
||||
const projOpenCmdLong = `Opens the given project directory in the editor
|
||||
of your choice. Will find certain well-known entrypoints (e.g. main.go).
|
||||
|
||||
If your editor is set in your config file, it will be used, otherwise
|
||||
one will be found in your path from a list of known defaults.`
|
||||
|
||||
const configCmdLong = `Commands for managing configuration, particulary
|
||||
useful for seeding a new config file`
|
||||
|
||||
|
@ -24,3 +24,7 @@ plist () {
|
||||
pshow () {
|
||||
gitlab-project-manager project show --current
|
||||
}
|
||||
|
||||
popen () {
|
||||
gitlab-project-manager project open $1
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package config
|
||||
import "time"
|
||||
|
||||
type Config struct {
|
||||
Editor editorConfig `yaml:"editor" json:"editor"`
|
||||
GitlabHost string `yaml:"gitlabHost" json:"gitlabHost"`
|
||||
GitlabToken string `yaml:"gitlabToken" json:"gitlabToken"`
|
||||
LogLevel string `yaml:"logLevel" json:"logLevel" enum:"info,warn,debug,error"`
|
||||
@ -13,6 +14,12 @@ type Config struct {
|
||||
}
|
||||
}
|
||||
|
||||
type editorConfig struct {
|
||||
DisplayName string `yaml:"displanName" json:"displanName"`
|
||||
Binary string `yaml:"binary" json:"binary"`
|
||||
OpenFlags string `yaml:"openFlags" json:"openFlags"`
|
||||
}
|
||||
|
||||
type loadConfig struct {
|
||||
OwnerOnly bool `yaml:"ownerOnly" json:"ownerOnly"`
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/gitlab"
|
||||
)
|
||||
|
||||
const gitCloneTimeoutSecs = 10
|
||||
const gitCloneTimeoutSecs = 30
|
||||
|
||||
// Will either read in the current repo, preparing a report
|
||||
// on its current state, or will clone the project if it has not
|
||||
|
Loading…
Reference in New Issue
Block a user