Add project open
This commit is contained in:
parent
b7960ed1b4
commit
bf213be9fb
@ -54,6 +54,7 @@ cache:
|
|||||||
```
|
```
|
||||||
## TODO
|
## 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
|
- [ ] Fix NPE when cache is reset or project for whatever reason leaves an orphaned alias
|
||||||
- [ ] Add config setters and getters
|
- [ ] Add config setters and getters
|
||||||
- [ ] Add TTL check to cache load, and add -f / --force flag to re-build regardless
|
- [ ] Add TTL check to cache load, and add -f / --force flag to re-build regardless
|
||||||
@ -64,6 +65,7 @@ cache:
|
|||||||
- [ ] Add open command
|
- [ ] Add open command
|
||||||
- [ ] config should exist for editor (vim, code, etc..)
|
- [ ] config should exist for editor (vim, code, etc..)
|
||||||
- [ ] Update README for shell completion, aliases, usage
|
- [ ] Update README for shell completion, aliases, usage
|
||||||
|
- [ ] Add fzf to `plist` / `gpm projects list`
|
||||||
- [ ] Make a Makefile
|
- [ ] Make a Makefile
|
||||||
- [ ] Add git repo status to project go (up-to-date, pending commits, etc..)
|
- [ ] 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`
|
- [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
|
const projShowCmdLong = `Shows detail for a particular project
|
||||||
Will always fuzzy find`
|
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
|
const configCmdLong = `Commands for managing configuration, particulary
|
||||||
useful for seeding a new config file`
|
useful for seeding a new config file`
|
||||||
|
|
||||||
|
@ -24,3 +24,7 @@ plist () {
|
|||||||
pshow () {
|
pshow () {
|
||||||
gitlab-project-manager project show --current
|
gitlab-project-manager project show --current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
popen () {
|
||||||
|
gitlab-project-manager project open $1
|
||||||
|
}
|
||||||
|
@ -3,16 +3,23 @@ package config
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
GitlabHost string `yaml:"gitlabHost" json:"gitlabHost"`
|
Editor editorConfig `yaml:"editor" json:"editor"`
|
||||||
GitlabToken string `yaml:"gitlabToken" json:"gitlabToken"`
|
GitlabHost string `yaml:"gitlabHost" json:"gitlabHost"`
|
||||||
LogLevel string `yaml:"logLevel" json:"logLevel" enum:"info,warn,debug,error"`
|
GitlabToken string `yaml:"gitlabToken" json:"gitlabToken"`
|
||||||
ProjectPath string `yaml:"projectPath" json:"projectPath"`
|
LogLevel string `yaml:"logLevel" json:"logLevel" enum:"info,warn,debug,error"`
|
||||||
Cache cacheConfig `yaml:"cache" json:"cache"`
|
ProjectPath string `yaml:"projectPath" json:"projectPath"`
|
||||||
|
Cache cacheConfig `yaml:"cache" json:"cache"`
|
||||||
Dump struct {
|
Dump struct {
|
||||||
Full bool
|
Full bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type editorConfig struct {
|
||||||
|
DisplayName string `yaml:"displanName" json:"displanName"`
|
||||||
|
Binary string `yaml:"binary" json:"binary"`
|
||||||
|
OpenFlags string `yaml:"openFlags" json:"openFlags"`
|
||||||
|
}
|
||||||
|
|
||||||
type loadConfig struct {
|
type loadConfig struct {
|
||||||
OwnerOnly bool `yaml:"ownerOnly" json:"ownerOnly"`
|
OwnerOnly bool `yaml:"ownerOnly" json:"ownerOnly"`
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"gitlab.sweetwater.com/it/devops/tools/gitlab-project-manager/internal/gitlab"
|
"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
|
// Will either read in the current repo, preparing a report
|
||||||
// on its current state, or will clone the project if it has not
|
// on its current state, or will clone the project if it has not
|
||||||
|
Loading…
Reference in New Issue
Block a user