cmd

package
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2025 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// Default config file paths
	DefaultConfigPathYML  = ".config/binstaller.yml"
	DefaultConfigPathYAML = ".config/binstaller.yaml"
)

Variables

View Source
var EmbedChecksumsCommand = &cobra.Command{
	Use:   "embed-checksums",
	Short: "Embed checksums for release assets into a binstaller configuration",
	Long: `Reads an InstallSpec configuration file and embeds checksums for the assets.
This command supports three modes of operation:
- download: Fetches the checksum file from GitHub releases
- checksum-file: Uses a local checksum file
- calculate: Downloads the assets and calculates checksums directly`,
	RunE: func(cmd *cobra.Command, args []string) error {
		log.Info("Running embed-checksums command...")

		cfgFile, err := resolveConfigFile(configFile)
		if err != nil {
			log.WithError(err).Error("Config file detection failed")
			return err
		}
		if configFile == "" {
			log.Infof("Using default config file: %s", cfgFile)
		}
		log.Debugf("Using config file: %s", cfgFile)

		log.Debugf("Reading InstallSpec from: %s", cfgFile)

		ast, err := parser.ParseFile(cfgFile, parser.ParseComments)
		if err != nil {
			return err
		}

		yamlData, err := os.ReadFile(cfgFile)
		if err != nil {
			log.WithError(err).Errorf("Failed to read install spec file: %s", cfgFile)
			return fmt.Errorf("failed to read install spec file %s: %w", cfgFile, err)
		}

		log.Debug("Unmarshalling InstallSpec YAML")
		var installSpec spec.InstallSpec
		err = yaml.UnmarshalWithOptions(yamlData, &installSpec, yaml.UseOrderedMap())
		if err != nil {
			log.WithError(err).Errorf("Failed to unmarshal install spec YAML from: %s", cfgFile)
			return fmt.Errorf("failed to unmarshal install spec YAML from %s: %w", cfgFile, err)
		}

		// Create the embedder
		var mode checksums.EmbedMode
		switch embedMode {
		case "download":
			mode = checksums.EmbedModeDownload
		case "checksum-file":
			mode = checksums.EmbedModeChecksumFile
		case "calculate":
			mode = checksums.EmbedModeCalculate
		default:
			return fmt.Errorf("invalid mode: %s. Must be one of: download, checksum-file, calculate", embedMode)
		}

		if mode == checksums.EmbedModeChecksumFile && embedFile == "" {
			log.Error("--file flag is required for checksum-file mode")
			return fmt.Errorf("--file flag is required for checksum-file mode")
		}

		embedder := &checksums.Embedder{
			Mode:         mode,
			Version:      embedVersion,
			Spec:         &installSpec,
			SpecAST:      ast,
			ChecksumFile: embedFile,
		}

		log.Infof("Embedding checksums using %s mode for version: %s", mode, embedVersion)
		if err := embedder.Embed(); err != nil {
			log.WithError(err).Error("Failed to embed checksums")
			return fmt.Errorf("failed to embed checksums: %w", err)
		}

		outputFile := embedOutput
		if outputFile == "" {
			outputFile = cfgFile
			log.Infof("No output specified, overwriting input file: %s", outputFile)
		}

		log.Infof("Writing updated InstallSpec to file: %s", outputFile)

		outputDir := filepath.Dir(outputFile)
		if err := os.MkdirAll(outputDir, 0755); err != nil {
			log.WithError(err).Errorf("Failed to create output directory: %s", outputDir)
			return fmt.Errorf("failed to create output directory %s: %w", outputDir, err)
		}

		if err := os.WriteFile(outputFile, []byte(ast.String()), 0644); err != nil {
			log.WithError(err).Errorf("Failed to write InstallSpec to file: %s", outputFile)
			return fmt.Errorf("failed to write InstallSpec to file %s: %w", outputFile, err)
		}
		log.Infof("InstallSpec successfully updated with embedded checksums")

		return nil
	},
}

EmbedChecksumsCommand represents the embed-checksums command

View Source
var GenCommand = &cobra.Command{
	Use:   "gen",
	Short: "Generate an installer script from an InstallSpec config file",
	Long: `Reads an InstallSpec configuration file (e.g., .binstaller.yml) and
generates a POSIX-compatible shell installer script.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		log.Info("Running gen command...")

		cfgFile, err := resolveConfigFile(configFile)
		if err != nil {
			log.WithError(err).Error("Config file detection failed")
			return err
		}
		if configFile == "" {
			log.Infof("Using default config file: %s", cfgFile)
		}
		log.Debugf("Using config file: %s", cfgFile)

		log.Debugf("Reading InstallSpec from: %s", cfgFile)
		var yamlData []byte
		if cfgFile == "-" {
			log.Debug("Reading install spec from stdin")
			yamlData, err = io.ReadAll(os.Stdin)
			if err != nil {
				log.WithError(err).Error("Failed to read install spec from stdin")
				return fmt.Errorf("failed to read install spec from stdin: %w", err)
			}
		} else {
			yamlData, err = os.ReadFile(cfgFile)
			if err != nil {
				log.WithError(err).Errorf("Failed to read install spec file: %s", cfgFile)
				return fmt.Errorf("failed to read install spec file %s: %w", cfgFile, err)
			}
		}

		log.Debug("Unmarshalling InstallSpec YAML")
		var installSpec spec.InstallSpec
		err = yaml.Unmarshal(yamlData, &installSpec)
		if err != nil {
			log.WithError(err).Errorf("Failed to unmarshal install spec YAML from: %s", cfgFile)
			return fmt.Errorf("failed to unmarshal install spec YAML from %s: %w", cfgFile, err)
		}

		log.Info("Generating installer script...")
		scriptBytes, err := shell.GenerateWithVersion(&installSpec, genTargetVersion)
		if err != nil {
			log.WithError(err).Error("Failed to generate installer script")
			return fmt.Errorf("failed to generate installer script: %w", err)
		}
		log.Debug("Installer script generated successfully")

		if genOutputFile == "" || genOutputFile == "-" {

			log.Debug("Writing installer script to stdout")
			fmt.Print(string(scriptBytes))
			log.Info("Installer script written to stdout")
		} else {

			log.Infof("Writing installer script to file: %s", genOutputFile)

			outputDir := filepath.Dir(genOutputFile)
			if err := os.MkdirAll(outputDir, 0755); err != nil {
				log.WithError(err).Errorf("Failed to create output directory: %s", outputDir)
				return fmt.Errorf("failed to create output directory %s: %w", outputDir, err)
			}

			err = os.WriteFile(genOutputFile, scriptBytes, 0755)
			if err != nil {
				log.WithError(err).Errorf("Failed to write installer script to file: %s", genOutputFile)
				return fmt.Errorf("failed to write installer script to file %s: %w", genOutputFile, err)
			}
			log.Infof("Installer script successfully written to %s", genOutputFile)
		}

		return nil
	},
}

GenCommand represents the gen command

View Source
var InitCommand = &cobra.Command{
	Use:   "init",
	Short: "Generate an InstallSpec config file from various sources",
	Long: `Initializes a binstaller configuration file (.config/binstaller.yml) by detecting
settings from a source like a GoReleaser config file or a GitHub repository.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		log.Infof("Running init command...")

		var adapter datasource.SourceAdapter

		switch initSource {
		case "goreleaser":
			adapter = datasource.NewGoReleaserAdapter(
				initRepo,
				initSourceFile,
				initCommitSHA,
				initName,
			)
		case "github":
			adapter = datasource.NewGitHubAdapter(initRepo)
		case "aqua":

			switch initSourceFile {
			case "":

				if initRepo == "" {
					return fmt.Errorf("--repo is required for aqua source when --file is not specified")
				}
				adapter = datasource.NewAquaRegistryAdapterFromRepo(initRepo, initCommitSHA)
			case "-":

				adapter = datasource.NewAquaRegistryAdapterFromReader(os.Stdin)
			default:

				f, err := os.Open(initSourceFile)
				if err != nil {
					return fmt.Errorf("failed to open aqua registry file: %w", err)
				}
				defer f.Close()
				adapter = datasource.NewAquaRegistryAdapterFromReader(f)
			}
		default:
			err := fmt.Errorf("unknown source specified: %s. Valid sources are: goreleaser, github, aqua", initSource)
			log.WithError(err).Error("invalid source")
			return err
		}

		ctx := context.Background()

		log.Infof("Generating InstallSpec using source: %s", initSource)
		installSpec, err := adapter.GenerateInstallSpec(ctx)
		if err != nil {
			log.WithError(err).Error("Failed to detect install spec")
			return fmt.Errorf("failed to detect install spec: %w", err)
		}
		if spec.StringValue(installSpec.Schema) == "" {
			installSpec.Schema = spec.StringPtr("v1")
		}
		log.Info("Successfully detected InstallSpec")

		log.Debug("Marshalling InstallSpec to YAML")
		yamlData, err := yaml.Marshal(installSpec)
		if err != nil {
			log.WithError(err).Error("Failed to marshal InstallSpec to YAML")
			return fmt.Errorf("failed to marshal install spec to YAML: %w", err)
		}

		schemaComment := "# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/output/@typespec/json-schema/InstallSpec.json\n"
		yamlData = append([]byte(schemaComment), yamlData...)

		if initOutputFile == "" || initOutputFile == "-" {

			log.Debug("Writing InstallSpec YAML to stdout")
			fmt.Println(string(yamlData))
			log.Info("InstallSpec YAML written to stdout")
		} else {

			log.Infof("Writing InstallSpec YAML to file: %s", initOutputFile)

			outputDir := filepath.Dir(initOutputFile)
			if err := os.MkdirAll(outputDir, 0755); err != nil {
				log.WithError(err).Errorf("Failed to create output directory: %s", outputDir)
				return fmt.Errorf("failed to create output directory %s: %w", outputDir, err)
			}

			err = os.WriteFile(initOutputFile, yamlData, 0644)
			if err != nil {
				log.WithError(err).Errorf("Failed to write InstallSpec to file: %s", initOutputFile)
				return fmt.Errorf("failed to write install spec to file %s: %w", initOutputFile, err)
			}
			log.Infof("InstallSpec successfully written to %s", initOutputFile)
		}

		return nil
	},
}

InitCommand represents the init command

View Source
var RootCmd = &cobra.Command{
	Use:   "binst",
	Short: "binst installs binaries from various sources using a spec file.",
	Long: `binstaller (binst) is a tool to generate installer scripts or directly
install binaries based on an InstallSpec configuration file.

It supports generating the spec from sources like GoReleaser config or GitHub releases.`,
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		log.SetHandler(cli.Default)
		if verbose {
			log.SetLevel(log.DebugLevel)
			log.Debugf("Verbose logging enabled")
		} else if quiet {
			log.SetLevel(log.ErrorLevel)
		} else {
			log.SetLevel(log.InfoLevel)
		}
		log.Debugf("Config file: %s", configFile)
	},
}

RootCmd represents the base command when called without any subcommands

Functions

func Execute

func Execute()

Execute adds all child commands to the root command and sets flags appropriately. This is called by main.main(). It only needs to happen once to the RootCmd.

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL