c1f196640b
Build and Publish Arch Package / build-arch (amd64, x86_64) (push) Successful in 44s
Build and Publish Arch Package / build-arch (arm64, aarch64) (push) Successful in 49s
Build and Publish Docker Image / build-apk (amd64, x86_64) (push) Successful in 44s
Build and Publish Docker Image / build-apk (arm64, aarch64) (push) Successful in 44s
Build and Publish Docker Image / build-and-push-docker (push) Successful in 10m47s
138 lines
3.7 KiB
Go
138 lines
3.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"axolotl/models"
|
|
"axolotl/service"
|
|
"axolotl/store"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func getNodeService() (service.NodeService, error) {
|
|
if token := os.Getenv("AX_TOKEN"); token != "" {
|
|
if cfg.Remote.Host != "" {
|
|
base := fmt.Sprintf("http://%s:%d", cfg.Remote.Host, cfg.Remote.Port)
|
|
return service.NewRemoteNodeService(base, ""), nil
|
|
}
|
|
st, err := store.FindAndOpenSQLiteStore()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
agentID := service.LookupAgentToken(st, token)
|
|
if agentID == "" {
|
|
return nil, fmt.Errorf("invalid AX_TOKEN: agent not found")
|
|
}
|
|
return service.NewLocalNodeService(st, agentID), nil
|
|
}
|
|
user := cfg.User
|
|
if user == "" {
|
|
return nil, fmt.Errorf("no user configured: run 'ax user set <username>' first")
|
|
}
|
|
if cfg.Remote.Host != "" {
|
|
base := fmt.Sprintf("http://%s:%d", cfg.Remote.Host, cfg.Remote.Port)
|
|
return service.NewRemoteNodeService(base, user), nil
|
|
}
|
|
st, err := store.FindAndOpenSQLiteStore()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return service.NewLocalNodeService(st, user), nil
|
|
}
|
|
|
|
var jsonFlag bool
|
|
var cfg *store.Config
|
|
var rootCmd = &cobra.Command{Use: "ax", Short: "The axolotl issue tracker"}
|
|
|
|
func Execute() {
|
|
var err error
|
|
cfg, err = store.LoadConfig()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "failed to load config:", err)
|
|
os.Exit(1)
|
|
}
|
|
RegisterAliasCommands()
|
|
if err := rootCmd.Execute(); err != nil {
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.PersistentFlags().BoolVar(&jsonFlag, "json", false, "")
|
|
}
|
|
|
|
func RegisterAliasCommands() {
|
|
rootCmd.AddGroup(&cobra.Group{ID: "aliases", Title: "Aliases:"})
|
|
aliases := cfg.Aliases
|
|
for _, a := range aliases {
|
|
rootCmd.AddCommand(&cobra.Command{
|
|
Use: a.Name,
|
|
Short: a.Description,
|
|
GroupID: "aliases",
|
|
DisableFlagParsing: true,
|
|
Run: func(ccmd *cobra.Command, args []string) {
|
|
acmd := a.Command
|
|
acmd = strings.ReplaceAll(acmd, "$me", cfg.User)
|
|
parts := strings.Fields(acmd)
|
|
var expanded []string
|
|
usedArgs := make([]bool, len(args))
|
|
for _, part := range parts {
|
|
if part == "$@" {
|
|
expanded = append(expanded, args...)
|
|
for i := range usedArgs {
|
|
usedArgs[i] = true
|
|
}
|
|
continue
|
|
}
|
|
hasCatchAll := strings.Contains(part, "$@")
|
|
replaced := part
|
|
if hasCatchAll {
|
|
replaced = strings.ReplaceAll(replaced, "$@", strings.Join(args, " "))
|
|
for i := range usedArgs {
|
|
usedArgs[i] = true
|
|
}
|
|
}
|
|
for i := len(args) - 1; i >= 0; i-- {
|
|
placeholder := fmt.Sprintf("$%d", i+1)
|
|
if strings.Contains(replaced, placeholder) {
|
|
replaced = strings.ReplaceAll(replaced, placeholder, args[i])
|
|
usedArgs[i] = true
|
|
}
|
|
}
|
|
expanded = append(expanded, replaced)
|
|
}
|
|
// Forward any unconsumed args (e.g. --json flag).
|
|
for i, arg := range args {
|
|
if !usedArgs[i] {
|
|
expanded = append(expanded, arg)
|
|
}
|
|
}
|
|
rootCmd.SetArgs(expanded)
|
|
if err := rootCmd.Execute(); err != nil {
|
|
os.Exit(1)
|
|
}
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
// parseRelInput parses a rel string into a RelInput.
|
|
// Formats:
|
|
// - "prefix::value" → property rel with no target (tag)
|
|
// - "relname:target" → edge rel with a target node
|
|
// - "tagname" → simple label rel with no target (alias for --tag)
|
|
func parseRelInput(s string) (service.RelInput, error) {
|
|
if strings.Contains(s, "::") {
|
|
// Property: name::value — no target node.
|
|
return service.RelInput{Type: models.RelType(s), Target: ""}, nil
|
|
}
|
|
if idx := strings.Index(s, ":"); idx >= 0 {
|
|
// Edge rel: relname:target.
|
|
return service.RelInput{Type: models.RelType(s[:idx]), Target: s[idx+1:]}, nil
|
|
}
|
|
// Simple label tag — no target node.
|
|
return service.RelInput{Type: models.RelType(s), Target: ""}, nil
|
|
}
|