diff --git a/cmd/deploy.go b/cmd/deploy.go index 1463ae1..10b4b6a 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -11,6 +11,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/go-gh/v2/pkg/api" + "github.com/github/gh-runtime-cli/internal/config" "github.com/spf13/cobra" ) @@ -19,6 +20,7 @@ type deployCmdFlags struct { app string revisionName string sha string + config string } func zipDirectory(sourceDir, destinationZip string) error { @@ -91,25 +93,35 @@ func init() { Use: "deploy", Short: "Deploy app to GitHub Runtime", Long: heredoc.Doc(` - Deploys a directory to a GitHub Runtime app + Deploys a directory to a GitHub Runtime app. + You can specify the app name using --app flag, --config flag to read from a runtime config file, + or it will automatically read from runtime.config.json in the current directory if it exists. `), Example: heredoc.Doc(` $ gh runtime deploy --dir ./dist --app my-app [--sha ] # => Deploys the contents of the 'dist' directory to the app named 'my-app'. + + $ gh runtime deploy --dir ./dist --config runtime.config.json + # => Deploys using app name from the config file. + + $ gh runtime deploy --dir ./dist + # => Deploys using app name from runtime.config.json in current directory (if it exists). `), RunE: func(cmd *cobra.Command, args []string) error { if deployCmdFlags.dir == "" { return fmt.Errorf("--dir flag is required") } - if deployCmdFlags.app == "" { - return fmt.Errorf("--app flag is required") + + appName, err := config.ResolveAppName(deployCmdFlags.app, deployCmdFlags.config) + if err != nil { + return err } if _, err := os.Stat(deployCmdFlags.dir); os.IsNotExist(err) { return fmt.Errorf("directory '%s' does not exist", deployCmdFlags.dir) } - _, err := os.ReadDir(deployCmdFlags.dir) + _, err = os.ReadDir(deployCmdFlags.dir) if err != nil { return fmt.Errorf("error reading directory '%s': %v", deployCmdFlags.dir, err) } @@ -127,7 +139,7 @@ func init() { return fmt.Errorf("error creating REST client: %v", err) } - deploymentsUrl := fmt.Sprintf("runtime/%s/deployment/bundle", deployCmdFlags.app) + deploymentsUrl := fmt.Sprintf("runtime/%s/deployment/bundle", appName) params := url.Values{} if deployCmdFlags.revisionName != "" { @@ -161,6 +173,7 @@ func init() { } deployCmd.Flags().StringVarP(&deployCmdFlags.dir, "dir", "d", "", "The directory to deploy") deployCmd.Flags().StringVarP(&deployCmdFlags.app, "app", "a", "", "The app to deploy") + deployCmd.Flags().StringVarP(&deployCmdFlags.config, "config", "c", "", "Path to runtime config file") deployCmd.Flags().StringVarP(&deployCmdFlags.revisionName, "revision-name", "r", "", "The revision name to deploy") deployCmd.Flags().StringVarP(&deployCmdFlags.sha, "sha", "s", "", "SHA of the app being deployed") diff --git a/cmd/get.go b/cmd/get.go index 995222a..c337313 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -3,14 +3,17 @@ package cmd import ( "fmt" "net/url" + "github.com/MakeNowJust/heredoc" "github.com/cli/go-gh/v2/pkg/api" + "github.com/github/gh-runtime-cli/internal/config" "github.com/spf13/cobra" ) type getCmdFlags struct { - app string - revisionName string + app string + revisionName string + config string } type serverResponse struct { @@ -23,18 +26,27 @@ func init() { Use: "get", Short: "Get details of a GitHub Runtime app", Long: heredoc.Doc(` - Get details of a GitHub Runtime app + Get details of a GitHub Runtime app. + You can specify the app name using --app flag, --config flag to read from a runtime config file, + or it will automatically read from runtime.config.json in the current directory if it exists. `), Example: heredoc.Doc(` $ gh runtime get --app my-app # => Retrieves details of the app named 'my-app' + + $ gh runtime get --config runtime.config.json + # => Retrieves details using app name from the config file. + + $ gh runtime get + # => Retrieves details using app name from runtime.config.json in current directory (if it exists). `), RunE: func(cmd *cobra.Command, args []string) error { - if getCmdFlags.app == "" { - return fmt.Errorf("--app flag is required") + appName, err := config.ResolveAppName(getCmdFlags.app, getCmdFlags.config) + if err != nil { + return err } - getUrl := fmt.Sprintf("runtime/%s/deployment", getCmdFlags.app) + getUrl := fmt.Sprintf("runtime/%s/deployment", appName) params := url.Values{} if getCmdFlags.revisionName != "" { params.Add("revision_name", getCmdFlags.revisionName) @@ -60,6 +72,7 @@ func init() { } getCmd.Flags().StringVarP(&getCmdFlags.app, "app", "a", "", "The app to retrieve details for") + getCmd.Flags().StringVarP(&getCmdFlags.config, "config", "c", "", "Path to runtime config file") getCmd.Flags().StringVarP(&getCmdFlags.revisionName, "revision-name", "r", "", "The revision name to use for the app") rootCmd.AddCommand(getCmd) } diff --git a/cmd/init.go b/cmd/init.go index 5b10694..7c555cb 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -8,6 +8,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/cli/go-gh/v2/pkg/api" + "github.com/github/gh-runtime-cli/internal/config" "github.com/spf13/cobra" ) @@ -16,10 +17,6 @@ type initCmdFlags struct { out string } -type runtimeConfig struct { - App string `json:"app"` -} - type appResponse struct { AppUrl string `json:"app_url"` } @@ -67,7 +64,7 @@ func init() { } // Create runtime config - config := runtimeConfig{ + configStruct := config.RuntimeConfig{ App: initCmdFlags.app, } @@ -84,7 +81,7 @@ func init() { } } - configBytes, err := json.MarshalIndent(config, "", " ") + configBytes, err := json.MarshalIndent(configStruct, "", " ") if err != nil { return fmt.Errorf("error creating configuration: %v", err) } diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..4d4f605 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,57 @@ +package config + +import ( + "encoding/json" + "fmt" + "os" +) + +// RuntimeConfig represents the structure of the runtime configuration file +type RuntimeConfig struct { + App string `json:"app"` +} + +// ReadRuntimeConfig reads and parses a runtime configuration file +func ReadRuntimeConfig(configPath string) (string, error) { + configBytes, err := os.ReadFile(configPath) + if err != nil { + return "", fmt.Errorf("error reading config file '%s': %w", configPath, err) + } + + var config RuntimeConfig + err = json.Unmarshal(configBytes, &config) + if err != nil { + return "", fmt.Errorf("error parsing config file '%s': %w", configPath, err) + } + + return config.App, nil +} + +// ResolveAppName resolves the app name using the priority order: +// 1. appFlag (--app) if provided +// 2. configPath (--config) if provided +// 3. runtime.config.json in current directory if it exists +// Returns an error if no app name can be resolved +func ResolveAppName(appFlag, configPath string) (string, error) { + // Priority 1: Use --app flag if provided + if appFlag != "" { + return appFlag, nil + } + + // Priority 2: Use --config file if provided + if configPath != "" { + return ReadRuntimeConfig(configPath) + } + + // Priority 3: Try default runtime.config.json + if _, err := os.Stat("runtime.config.json"); err == nil { + appName, err := ReadRuntimeConfig("runtime.config.json") + if err != nil { + return "", fmt.Errorf("found runtime.config.json but failed to read it: %v", err) + } + return appName, nil + } + + // No app name could be resolved + return "", fmt.Errorf("--app flag is required, --config must be specified, or runtime.config.json must exist in current directory") +}