cli

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2025 License: MIT Imports: 20 Imported by: 0

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

View Source
var AddCmd = &cobra.Command{
	Use:     "add",
	Aliases: []string{"create", "new", "a"},
	Short:   "Add a new consignment",
	Long:    `Create a new consignment to describe changes made to packages. This is used to track changes for release management.`,
	Example: `
shipyard add
shipyard add --type patch
shipyard add --summary "Fixed bug"
shipyard add --package api --type patch
shipyard add --type patch --summary "Fixed bug"
shipyard add --package api --type patch --summary "Fixed bug"
echo "Fixed bug" | shipyard add --type patch
echo "patch" | shipyard add --summary "Fixed bug"
echo "1" | shipyard add --summary "Fixed bug"
`,
	Run: func(cmd *cobra.Command, args []string) {

		projectConfig, err := config.LoadProjectConfig()
		if err != nil {
			logger.Error("Failed to load project configuration", "error", err)
			fmt.Println("Error: Unable to load project configuration.")
			fmt.Println("Make sure you're in a Shipyard project directory and run 'shipyard init' if needed.")
			os.Exit(1)
		}

		manager := consignment.NewManager(projectConfig)

		var (
			selectedPackages []string
			changeType       string
			summary          string
		)

		packageFlag, _ := cmd.Flags().GetStringSlice("package")
		typeFlag, _ := cmd.Flags().GetString("type")
		summaryFlag, _ := cmd.Flags().GetString("summary")

		availablePackages := manager.GetAvailablePackages()
		if len(availablePackages) == 0 {
			fmt.Println("No packages configured in this project.")
			fmt.Println("Please run 'shipyard init' to configure your packages.")
			os.Exit(1)
		}

		if projectConfig.Type == config.RepositoryTypeSingleRepo && len(packageFlag) == 0 {
			packageFlag = []string{availablePackages[0].Name}
		}

		if projectConfig.Type == config.RepositoryTypeMonorepo {
			if len(packageFlag) > 0 {

				availablePackageNames := make(map[string]bool)
				for _, pkg := range availablePackages {
					availablePackageNames[pkg.Name] = true
				}

				for _, pkg := range packageFlag {
					if !availablePackageNames[pkg] {
						fmt.Printf("Error: Package '%s' not found in project configuration\n", pkg)
						fmt.Println("Available packages:")
						for _, availablePkg := range availablePackages {
							fmt.Printf("  - %s (%s)\n", availablePkg.Name, availablePkg.Path)
						}
						os.Exit(1)
					}
				}
				selectedPackages = packageFlag
			} else {

				packageOptions := make([]huh.Option[string], 0, len(availablePackages))
				for _, pkg := range availablePackages {
					packageOptions = append(packageOptions, huh.NewOption(fmt.Sprintf("%s (%s)", pkg.Name, pkg.Path), pkg.Name))
				}

				packageForm := huh.NewForm(
					huh.NewGroup(
						huh.NewNote().
							Title("πŸ“¦ Package Selection").
							Description("Select the packages that have been changed."),
						huh.NewMultiSelect[string]().
							Title("Which packages have changed?").
							Description("Select all packages that have been modified.").
							Options(packageOptions...).
							Value(&selectedPackages),
					),
				)

				if err := packageForm.Run(); err != nil {
					logger.Error("Failed to get package selection", "error", err)
					fmt.Printf("Error: %v\n", err)
					os.Exit(1)
				}

				if len(selectedPackages) == 0 {
					fmt.Println("No packages selected. Consignment creation cancelled.")
					return
				}
			}
		} else {

			selectedPackages = []string{availablePackages[0].Name}
		}

		if typeFlag != "" {

			changeType = typeFlag
			if err := manager.ValidateChangeType(changeType); err != nil {
				fmt.Printf("Error: %v\n", err)
				fmt.Println("Available change types:")
				for _, ct := range manager.GetAvailableChangeTypes() {
					displayName := ct.DisplayName
					if displayName == "" {
						displayName = ct.Name
					}
					fmt.Printf("  - %s (%s)\n", ct.Name, displayName)
				}
				os.Exit(1)
			}
		} else {

			if isStdinPiped() {

				input, err := readStdinLine()
				if err != nil {
					fmt.Println("Error: No change type provided and unable to read from stdin")
					fmt.Println("Available change types:")
					for i, ct := range projectConfig.GetChangeTypes() {
						fmt.Printf("  %d. %s\n", i+1, ct.Name)
					}
					fmt.Println("Usage: echo '<type-name-or-number>' | shipyard add --summary 'your summary'")
					os.Exit(1)
				}

				changeTypes := projectConfig.GetChangeTypes()
				if input >= "1" && input <= fmt.Sprintf("%d", len(changeTypes)) {

					if num := int(input[0] - '0'); num >= 1 && num <= len(changeTypes) {
						changeType = changeTypes[num-1].Name
					}
				} else {

					changeType = input
				}

				if err := manager.ValidateChangeType(changeType); err != nil {
					fmt.Printf("Error: %v\n", err)
					fmt.Println("Available change types:")
					for i, ct := range changeTypes {
						fmt.Printf("  %d. %s\n", i+1, ct.Name)
					}
					os.Exit(1)
				}
			} else {

				changeTypeOptions := make([]huh.Option[string], 0, len(projectConfig.GetChangeTypes()))
				for _, ct := range projectConfig.GetChangeTypes() {
					optionText := ct.DisplayName
					if optionText == "" {
						optionText = ct.Name
					}
					changeTypeOptions = append(changeTypeOptions, huh.NewOption(optionText, ct.Name))
				}

				changeTypeForm := huh.NewForm(
					huh.NewGroup(
						huh.NewNote().
							Title("πŸ“ Change Type").
							Description("Select the type of change you've made."),
						huh.NewSelect[string]().
							Title("What type of change is this?").
							Description("Choose the appropriate change type.").
							Options(changeTypeOptions...).
							Value(&changeType),
					),
				)

				if err := changeTypeForm.Run(); err != nil {
					logger.Error("Failed to get change type", "error", err)
					fmt.Printf("Error: %v\n", err)
					os.Exit(1)
				}
			}
		}

		if summaryFlag != "" {

			summary = summaryFlag
			if strings.TrimSpace(summary) == "" {
				fmt.Println("Error: --summary flag cannot be empty")
				os.Exit(1)
			}
		} else {

			if isStdinPiped() {

				input, err := readStdinLine()
				if err != nil {
					fmt.Println("Error: No summary provided and unable to read from stdin")
					fmt.Println("Usage: echo 'your summary' | shipyard add --type <type>")
					os.Exit(1)
				}

				summary = input
				if strings.TrimSpace(summary) == "" {
					fmt.Println("Error: Summary cannot be empty")
					os.Exit(1)
				}
			} else {

				summaryForm := huh.NewForm(
					huh.NewGroup(
						huh.NewNote().
							Title("πŸ“‹ Change Summary").
							Description("Provide a summary of the changes made."),
						huh.NewText().
							Title("Summary").
							Description("Describe the changes made to the selected packages.").
							Placeholder("e.g. Fixed bug in user authentication flow").
							Value(&summary),
					),
				)

				if err := summaryForm.Run(); err != nil {
					logger.Error("Failed to get change summary", "error", err)
					fmt.Printf("Error: %v\n", err)
					os.Exit(1)
				}
			}
		}

		createdConsignment, err := manager.CreateConsignment(selectedPackages, changeType, summary)
		if err != nil {
			logger.Error("Failed to create consignment", "error", err)
			fmt.Printf("Error: Unable to create consignment: %v\n", err)
			os.Exit(1)
		}

		fmt.Printf("\nπŸŽ‰ Consignment created successfully!\n")
		fmt.Printf("πŸ“„ File: %s/%s.md\n", manager.GetConsignmentDir(), createdConsignment.ID)
		fmt.Printf("πŸ“¦ Packages: %s\n", strings.Join(selectedPackages, ", "))
		fmt.Printf("πŸ”„ Type: %s\n", changeType)
		fmt.Printf("πŸ“ Summary: %s\n", summary)
		fmt.Printf("\nπŸ’‘ Next steps:\n")
		fmt.Printf("   - Review the consignment file\n")
		fmt.Printf("   - Commit the consignment to your repository\n")
		fmt.Printf("   - Run 'shipyard version' to calculate new versions\n")

		logger.Info("Consignment created successfully",
			"id", createdConsignment.ID,
			"packages", selectedPackages,
			"type", changeType,
		)
	},
}
View Source
var InitCmd = &cobra.Command{
	Use:   "init",
	Short: "Initialize a new Shipyard project",
	Long:  "This command sets up a new Shipyard project with the necessary configuration files and directories. It can automatically detect your repository URL and scan for packages.",
	Run: func(cmd *cobra.Command, args []string) {
		skipAutoScan, _ := cmd.Flags().GetBool("skip-auto-scan")

		var (
			repoType          config.RepoType
			repoPath          string
			changelogTemplate string
			packages          []PackageConfig
			useAutoScan       bool = !skipAutoScan
		)

		if !skipAutoScan {
			welcomeForm := huh.NewForm(
				huh.NewGroup(
					huh.NewNote().
						Title("`Shipyard Init`").
						Description("Welcome to Shipyard! Let's set up your project with automatic detection."),
					huh.NewConfirm().
						Title("Auto-scan for packages and repository?").
						Description("Shipyard can automatically detect your repository URL and scan for packages. Would you like to use auto-detection?").
						Value(&useAutoScan),
				),
			)

			if err := welcomeForm.Run(); err != nil {
				logger.Error("Failed to get user input", "error", err)
				return
			}
		} else {

			fmt.Println("🚒 Shipyard Init")
			fmt.Println("Setting up your project with manual configuration...")
		}

		if useAutoScan {
			logger.Info("Auto-detecting repository and package information...")

			if detectedRepo, err := autoDetectRepoInfo(); err == nil {
				repoPath = detectedRepo
				logger.Info("Detected repository", "repo", repoPath)
			} else {
				logger.Warn("Could not auto-detect repository", "error", err.Error())
			}

			if detectedPackages, err := autoScanPackages(); err == nil && len(detectedPackages) > 0 {
				logger.Info("Found packages", "count", len(detectedPackages))
				for _, pkg := range detectedPackages {
					packages = append(packages, *pkg)
					logger.Info("Found package", "name", pkg.Name, "ecosystem", pkg.Ecosystem, "path", pkg.Path)
				}

				repoType = determineRepoType(detectedPackages)
				logger.Info("Determined repository type", "type", repoType)
			} else {
				if err != nil {
					logger.Warn("Could not auto-scan packages", "error", err.Error())
				} else {
					logger.Info("No packages found automatically")
				}
			}
		}

		repoForm := huh.NewForm(
			huh.NewGroup(
				huh.NewNote().
					Title("Repository Configuration").
					Description("Please review and update the repository configuration."),
				huh.NewInput().
					Title("Repository Path").
					Value(&repoPath).
					Placeholder("e.g., github.com/your-org/your-repo").
					Description("Enter the path to your repository. This can be a GitHub, GitLab, or Bitbucket URL."),
				huh.NewSelect[config.RepoType]().
					Value(&repoType).
					Title("Select Repository Type").
					Description("Choose the type of repository for your project.").
					Options(
						huh.NewOption("Monorepo", config.RepositoryTypeMonorepo),
						huh.NewOption("Single Repo", config.RepositoryTypeSingleRepo),
					),
				huh.NewInput().
					Title("Changelog Template").
					Value(&changelogTemplate).
					Placeholder("e.g., keepachangelog").
					Description("Enter the template for your changelog. This can be a predefined template like 'keepachangelog' or a custom one."),
			),
		)

		if err := repoForm.Run(); err != nil {
			logger.Error("Failed to initialize project", "error", err)
			return
		}

		if len(packages) == 0 || !useAutoScan {

			if repoType == config.RepositoryTypeMonorepo {
				// TODO: add an option to scan for supported package ecosystems
				var addAnother bool = true

				for addAnother {
					var pkgConfig PackageConfig

					packageForm := huh.NewForm(
						packageGroup(&pkgConfig),
					)

					if err := packageForm.Run(); err != nil {
						logger.Error("Failed to configure package", "error", err)
						return
					}

					packages = append(packages, pkgConfig)

					continueForm := huh.NewForm(
						huh.NewGroup(
							huh.NewConfirm().
								Title("Add Another Package?").
								Description("Would you like to add another package to your monorepo?").
								Value(&addAnother),
						),
					)

					if err := continueForm.Run(); err != nil {
						logger.Error("Failed to get user input", "error", err)
						return
					}
				}
			} else {
				// Single repo - configure only one package
				var pkgConfig PackageConfig

				packageForm := huh.NewForm(
					packageGroup(&pkgConfig),
				)

				if err := packageForm.Run(); err != nil {
					logger.Error("Failed to configure package", "error", err)
					return
				}

				packages = append(packages, pkgConfig)
			}
		} else {
			// Review auto-detected packages
			var confirmPackages bool
			var packagesDesc strings.Builder
			packagesDesc.WriteString("The following packages were detected:\n")
			for _, pkg := range packages {
				packagesDesc.WriteString(fmt.Sprintf("β€’ %s (%s) at %s\n", pkg.Name, pkg.Ecosystem, pkg.Path))
			}
			packagesDesc.WriteString("\nWould you like to use these packages?")

			reviewForm := huh.NewForm(
				huh.NewGroup(
					huh.NewNote().
						Title("Package Review").
						Description(packagesDesc.String()),
					huh.NewConfirm().
						Title("Use detected packages?").
						Description("Accept the auto-detected packages or configure manually?").
						Value(&confirmPackages),
				),
			)

			if err := reviewForm.Run(); err != nil {
				logger.Error("Failed to get user input", "error", err)
				return
			}

			if !confirmPackages {

				packages = []PackageConfig{}
				if repoType == config.RepositoryTypeMonorepo {
					var addAnother bool = true

					for addAnother {
						var pkgConfig PackageConfig

						packageForm := huh.NewForm(
							packageGroup(&pkgConfig),
						)

						if err := packageForm.Run(); err != nil {
							logger.Error("Failed to configure package", "error", err)
							return
						}

						packages = append(packages, pkgConfig)

						continueForm := huh.NewForm(
							huh.NewGroup(
								huh.NewConfirm().
									Title("Add Another Package?").
									Description("Would you like to add another package to your monorepo?").
									Value(&addAnother),
							),
						)

						if err := continueForm.Run(); err != nil {
							logger.Error("Failed to get user input", "error", err)
							return
						}
					}
				} else {
					// Single repo - configure only one package
					var pkgConfig PackageConfig

					packageForm := huh.NewForm(
						packageGroup(&pkgConfig),
					)

					if err := packageForm.Run(); err != nil {
						logger.Error("Failed to configure package", "error", err)
						return
					}

					packages = append(packages, pkgConfig)
				}
			}
		}

		configMap := map[string]interface{}{
			"type": repoType,
			"repo": repoPath,
			"changelog": map[string]interface{}{
				"template": changelogTemplate,
			},
		}

		if repoType == config.RepositoryTypeMonorepo {
			configMap["packages"] = convertPackagesForConfig(packages)
		} else {

			configMap["package"] = convertPackageForConfig(packages[0])
		}

		if err := config.InitProjectConfig(configMap); err != nil {
			logger.Error("Failed to initialize project configuration", "error", err)
			return
		}

		logger.Info("Successfully initialized Shipyard project", "type", repoType, "repo", repoPath)
		logger.Info("Configuration saved to", "path", config.AppConfig.GetString("config"))

		fmt.Println("\nπŸŽ‰ Shipyard project initialized successfully!")
		fmt.Printf("πŸ“ Configuration saved to: %s\n", config.AppConfig.GetString("config"))
		fmt.Printf("πŸ“ Repository type: %s\n", repoType)
		fmt.Printf("πŸ”— Repository: %s\n", repoPath)
		fmt.Printf("πŸ“‹ Changelog template: %s\n", changelogTemplate)

		if repoType == config.RepositoryTypeMonorepo {
			fmt.Printf("πŸ“¦ Packages configured: %d\n", len(packages))
			for _, pkg := range packages {
				fmt.Printf("   - %s (%s) at %s\n", pkg.Name, pkg.Ecosystem, pkg.Path)
			}
		} else {
			fmt.Printf("πŸ“¦ Package: %s (%s) at %s\n", packages[0].Name, packages[0].Ecosystem, packages[0].Path)
		}

		fmt.Println("\nπŸš€ Next steps:")
		fmt.Println("   - Review the generated configuration file")
		fmt.Println("   - Run 'shipyard --help' to see available commands")
		fmt.Println("   - Start managing your releases with Shipyard!")
	},
}
View Source
var ReleaseNotesCmd = &cobra.Command{
	Use:   "release-notes [version]",
	Short: "Get release notes for a specific version",
	Long:  `Get release notes for a specific version from shipment history.`,
	Example: `
  shipyard release-notes 1.2.3          # Get release notes for version 1.2.3
  shipyard release-notes v1.2.3         # Get release notes for version v1.2.3 (v prefix is optional)
  shipyard release-notes 1.2.3 -p myapp # Get release notes for version 1.2.3 of package myapp (monorepo)
  shipyard release-notes 1.2.3 --raw    # Get raw markdown output instead of rendered
  shipyard release-notes 1.2.3 -t simple # Use a different template for rendering`,
	Args: cobra.ExactArgs(1),
	Run: func(cmd *cobra.Command, args []string) {

		projectConfig, err := config.LoadProjectConfig()
		if err != nil {
			logger.Error("Failed to load project configuration", "error", err)
			fmt.Printf("Error: Unable to load project configuration: %v\n", err)
			fmt.Println("Please run 'shipyard init' to initialize your project.")
			os.Exit(1)
		}

		versionStr := args[0]

		versionStr = strings.TrimPrefix(versionStr, "v")

		requestedVersion, err := semver.Parse(versionStr)
		if err != nil {
			logger.Error("Invalid version format", "version", versionStr, "error", err)
			fmt.Printf("Error: Invalid version format '%s': %v\n", versionStr, err)
			fmt.Println("Version should be in format like '1.2.3' or 'v1.2.3'")
			os.Exit(1)
		}

		packageFilter, _ := cmd.Flags().GetString("package")
		if packageFilter != "" {
			if projectConfig.Type != config.RepositoryTypeMonorepo {
				logger.Error("Package filter can only be used with monorepo projects")
				fmt.Printf("Error: --package flag can only be used with monorepo projects\n")
				os.Exit(1)
			}

			packageExists := false
			for _, pkg := range projectConfig.Packages {
				if pkg.Name == packageFilter {
					packageExists = true
					break
				}
			}

			if !packageExists {
				logger.Error("Package not found", "package", packageFilter)
				fmt.Printf("Error: Package '%s' not found in project configuration\n", packageFilter)
				os.Exit(1)
			}
		}

		shipmentHistory := shipment.NewShipmentHistory(projectConfig)
		history, err := shipmentHistory.LoadHistory()
		if err != nil {
			logger.Error("Failed to load shipment history", "error", err)
			fmt.Printf("Error: Unable to load shipment history: %v\n", err)
			os.Exit(1)
		}

		if len(history) == 0 {
			logger.Error("No shipment history found")
			fmt.Printf("Error: No shipment history found\n")
			fmt.Println("Use 'shipyard version' to create some releases first.")
			os.Exit(1)
		}

		// Find the shipment for the requested version
		var matchingShipment *shipment.Shipment
		var matchingPackageName string

		for _, ship := range history {

			for packageName, version := range ship.Versions {

				if packageFilter != "" && packageName != packageFilter {
					continue
				}

				if projectConfig.Type == config.RepositoryTypeSingleRepo && packageFilter == "" {
					packageName = projectConfig.Package.Name
				}

				if version.Equals(requestedVersion) {
					matchingShipment = ship
					matchingPackageName = packageName
					break
				}
			}
			if matchingShipment != nil {
				break
			}
		}

		if matchingShipment == nil {
			logger.Error("Version not found in shipment history", "version", requestedVersion.String(), "package", packageFilter)
			if packageFilter != "" {
				fmt.Printf("Error: Version %s not found for package '%s' in shipment history\n", requestedVersion.String(), packageFilter)
			} else {
				fmt.Printf("Error: Version %s not found in shipment history\n", requestedVersion.String())
			}
			fmt.Println("Available versions:")

			if packageFilter != "" {
				fmt.Printf("  Package '%s':\n", packageFilter)
				for _, ship := range history {
					if version, exists := ship.Versions[packageFilter]; exists {
						fmt.Printf("    - %s (shipped: %s)\n", version.String(), ship.Date.Format("2006-01-02"))
					}
				}
			} else {

				versionMap := make(map[string][]string)
				for _, ship := range history {
					for pkgName, version := range ship.Versions {
						versionMap[pkgName] = append(versionMap[pkgName], fmt.Sprintf("%s (shipped: %s)", version.String(), ship.Date.Format("2006-01-02")))
					}
				}

				for pkgName, versions := range versionMap {
					fmt.Printf("  Package '%s':\n", pkgName)
					for _, v := range versions {
						fmt.Printf("    - %s\n", v)
					}
				}
			}
			os.Exit(1)
		}

		templateOverride, _ := cmd.Flags().GetString("template")
		if templateOverride != "" {

			if err := changelog.ValidateTemplate(templateOverride); err != nil {
				logger.Error("Invalid template specified", "template", templateOverride, "error", err)
				fmt.Printf("Error: %v\n", err)
				os.Exit(1)
			}
			projectConfig.Changelog.Template = templateOverride
		}

		templateEngine := templates.NewTemplateEngine(projectConfig)

		releaseNotesContent, err := generateReleaseNotesForShipment(matchingShipment, matchingPackageName, requestedVersion, templateEngine, projectConfig)
		if err != nil {
			logger.Error("Failed to generate release notes", "error", err)
			fmt.Printf("Error: Unable to generate release notes: %v\n", err)
			os.Exit(1)
		}

		raw, _ := cmd.Flags().GetBool("raw")
		if raw {
			fmt.Print(releaseNotesContent)
		} else {

			rendered, err := renderMarkdown(releaseNotesContent)
			if err != nil {
				logger.Error("Failed to render markdown", "error", err)
				fmt.Printf("Warning: Failed to render markdown, showing raw content: %v\n", err)
				fmt.Print(releaseNotesContent)
			} else {
				fmt.Printf("πŸ“„ Release Notes for %s %s\n", matchingPackageName, requestedVersion.String())
				fmt.Printf("========================================\n\n")
				fmt.Print(rendered)
			}
		}

		logger.Info("Release notes command completed successfully",
			"version", requestedVersion.String(),
			"package", matchingPackageName,
			"shipment_id", matchingShipment.ID,
			"shipment_date", matchingShipment.Date.Format("2006-01-02"),
		)
	},
}
View Source
var RemoteCmd = &cobra.Command{
	Use:   "remote",
	Short: "Manage remote configuration sources",
	Long: `Manage remote configuration sources for Shipyard.

Remote configurations allow teams to extend a shared configuration from:
- HTTP/HTTPS URLs: https://example.com/config.yaml
- GitHub repositories: github:owner/repo/path/to/config.yaml[@ref] (supports SSH and HTTPS)
- Git repositories: git+https://github.com/owner/repo.git/path/to/config.yaml[@ref] (HTTPS)
- Git repositories: git+git@github.com:owner/repo.git/path/to/config.yaml[@ref] (SSH)

Authentication is supported for private repositories via SSH keys, tokens, and git credentials.
Remote configurations are cached locally for performance and can be refreshed as needed.`,
}
View Source
var RootCmd = &cobra.Command{
	Use:   "shipyard",
	Short: "Shipyard, Where releases are built",
	Long:  "Shipyard is a tool for managing change notes, versions, and releases.",
	PersistentPreRun: func(cmd *cobra.Command, args []string) {

		initLogger()
	},
	Run: func(cmd *cobra.Command, args []string) {
		println("Welcome to Shipyard!")
		_, err := config.LoadProjectConfig()
		if err != nil {
			println("You need to run `shipyard init` to set up your project.")
			logger.Error("Error loading config", "error", err)
			os.Exit(1)
		}
		logger.Info("Shipyard initialized successfully")
		println("You are in a Shipyard project!")
		println("Use `shipyard --help` to see available commands.")
		println("For more information, visit https://shipyard.tamez.dev/docs")
	},
}
View Source
var StatusCmd = &cobra.Command{
	Use:   "status",
	Short: "Show consignment status and version information",
	Long:  "Display current consignments, what new versions would be, and optionally output release notes for packages.",
	Run: func(cmd *cobra.Command, args []string) {

		projectConfig, err := config.LoadProjectConfig()
		if err != nil {
			logger.Error("Failed to load project configuration", "error", err)
			fmt.Printf("Error: Unable to load project configuration: %v\n", err)
			fmt.Println("Please run 'shipyard init' to initialize your project.")
			os.Exit(1)
		}

		manager := consignment.NewManager(projectConfig)

		consignments, err := manager.GetConsignmens()
		if err != nil {
			logger.Error("Failed to get consignments", "error", err)
			fmt.Printf("Error: Unable to read consignments: %v\n", err)
			os.Exit(1)
		}

		packageFilter, _ := cmd.Flags().GetString("package")
		if packageFilter != "" {
			if projectConfig.Type != config.RepositoryTypeMonorepo {
				logger.Error("Package filter can only be used with monorepo projects")
				fmt.Printf("Error: --package flag can only be used with monorepo projects\n")
				os.Exit(1)
			}

			packageExists := false
			for _, pkg := range projectConfig.Packages {
				if pkg.Name == packageFilter {
					packageExists = true
					break
				}
			}

			if !packageExists {
				logger.Error("Package not found", "package", packageFilter)
				fmt.Printf("Error: Package '%s' not found in project configuration\n", packageFilter)
				os.Exit(1)
			}
		}

		fmt.Printf("πŸ“Š Shipyard Project Status\n")
		fmt.Printf("==========================\n\n")

		fmt.Printf("πŸ“ Project Type: %s\n", projectConfig.Type)
		if projectConfig.Type == config.RepositoryTypeMonorepo {
			fmt.Printf("πŸ“¦ Packages: %d\n", len(projectConfig.Packages))
		} else {
			fmt.Printf("πŸ“¦ Package: %s\n", projectConfig.Package.Name)
		}
		fmt.Printf("πŸ“ Changelog Template: %s\n", projectConfig.Changelog.Template)
		fmt.Printf("\n")

		fmt.Printf("πŸ“‹ Consignments\n")
		fmt.Printf("===============\n")

		if len(consignments) == 0 {
			fmt.Println("No consignments found.")
			fmt.Println("Create some consignments with 'shipyard add' first.")
			return
		}

		fmt.Printf("Total consignments: %d\n\n", len(consignments))

		filteredConsignments := consignments
		if packageFilter != "" {
			filteredConsignments = make([]*consignment.Consignment, 0)
			for _, c := range consignments {
				if _, exists := c.Packages[packageFilter]; exists {
					filteredConsignments = append(filteredConsignments, c)
				}
			}
		}

		for _, c := range filteredConsignments {
			fmt.Printf("β€’ %s (Created: %s)\n", c.ID, c.Created.Format(time.RFC3339))
			fmt.Printf("  Summary: %s\n", c.Summary)
			fmt.Printf("  Packages: ")
			first := true
			for pkgName, changeType := range c.Packages {
				if packageFilter != "" && pkgName != packageFilter {
					continue
				}
				if !first {
					fmt.Printf(", ")
				}
				fmt.Printf("%s (%s)", pkgName, changeType)
				first = false
			}
			fmt.Printf("\n\n")
		}

		fmt.Printf("πŸ”– Version Information\n")
		fmt.Printf("=====================\n")

		versions, err := manager.CalculateAllVersions()
		if err != nil {
			logger.Error("Failed to calculate versions", "error", err)
			fmt.Printf("Error: Unable to calculate versions: %v\n", err)
			os.Exit(1)
		}

		if packageFilter != "" {
			if version, exists := versions[packageFilter]; exists {
				fmt.Printf("Next version for %s: %s\n", packageFilter, version.String())
			} else {
				fmt.Printf("No version changes for %s\n", packageFilter)
			}
		} else {
			fmt.Printf("Next versions:\n")
			for pkgName, version := range versions {
				fmt.Printf("  %s: %s\n", pkgName, version.String())
			}
		}

		releaseNotes, _ := cmd.Flags().GetBool("release-notes")
		if releaseNotes {
			fmt.Printf("\nπŸ“„ Release Notes\n")
			fmt.Printf("================\n\n")

			templateOverride, _ := cmd.Flags().GetString("template")
			if templateOverride != "" {

				if err := changelog.ValidateTemplate(templateOverride); err != nil {
					logger.Error("Invalid template specified", "template", templateOverride, "error", err)
					fmt.Printf("Error: %v\n", err)
					os.Exit(1)
				}
				projectConfig.Changelog.Template = templateOverride
			}

			generator, err := changelog.NewGenerator(projectConfig)
			if err != nil {
				logger.Error("Failed to create changelog generator", "error", err)
				fmt.Printf("Error: Unable to create changelog generator: %v\n", err)
				os.Exit(1)
			}

			// Generate release notes
			var releaseNotesContent string
			if packageFilter != "" {

				version, exists := versions[packageFilter]
				if !exists {
					fmt.Printf("No version changes for package '%s'\n", packageFilter)
					return
				}

				releaseNotesContent, err = generator.GenerateChangelogForPackage(packageFilter, consignments, version)
				if err != nil {
					logger.Error("Failed to generate release notes for package", "package", packageFilter, "error", err)
					fmt.Printf("Error: Unable to generate release notes for package '%s': %v\n", packageFilter, err)
					os.Exit(1)
				}
			} else {

				releaseNotesContent, err = generator.GenerateChangelog(consignments, versions)
				if err != nil {
					logger.Error("Failed to generate release notes", "error", err)
					fmt.Printf("Error: Unable to generate release notes: %v\n", err)
					os.Exit(1)
				}
			}

			raw, _ := cmd.Flags().GetBool("raw")
			if raw {
				fmt.Print(releaseNotesContent)
			} else {

				rendered, err := renderMarkdown(releaseNotesContent)
				if err != nil {
					logger.Error("Failed to render markdown", "error", err)
					fmt.Printf("Warning: Failed to render markdown, showing raw content: %v\n", err)
					fmt.Print(releaseNotesContent)
				} else {
					fmt.Print(rendered)
				}
			}
		}

		fmt.Printf("\nπŸ’‘ Next steps:\n")
		fmt.Printf("   - Run 'shipyard version --preview' to see the changelog\n")
		fmt.Printf("   - Run 'shipyard version --dry-run' to see changelog and version info\n")
		fmt.Printf("   - Run 'shipyard version' to generate changelog and apply versions\n")

		logger.Info("Status command completed successfully",
			"consignments", len(consignments),
			"packages", len(versions),
		)
	},
}
View Source
var VersionCmd = &cobra.Command{
	Use:   "version",
	Short: "Generate changelogs and apply version updates",
	Long:  `Generate changelogs and apply version updates to package manifests.`,
	Run: func(cmd *cobra.Command, args []string) {

		projectConfig, err := config.LoadProjectConfig()
		if err != nil {
			logger.Error("Failed to load project configuration", "error", err)
			fmt.Printf("Error: Unable to load project configuration: %v\n", err)
			fmt.Println("Please run 'shipyard init' to initialize your project.")
			os.Exit(1)
		}

		manager := consignment.NewManager(projectConfig)

		consignments, err := manager.GetConsignmens()
		if err != nil {
			logger.Error("Failed to get consignments", "error", err)
			fmt.Printf("Error: Unable to read consignments: %v\n", err)
			os.Exit(1)
		}

		regenerate, _ := cmd.Flags().GetBool("regenerate")

		// Calculate versions for packages that have consignments (if any)
		var versions map[string]*semver.Version
		if len(consignments) > 0 && !regenerate {
			versions, err = manager.CalculateAllVersions()
			if err != nil {
				logger.Error("Failed to calculate versions", "error", err)
				fmt.Printf("Error: Unable to calculate versions: %v\n", err)
				os.Exit(1)
			}
		} else {

			versions = make(map[string]*semver.Version)
		}

		templateOverride, _ := cmd.Flags().GetString("template")
		if templateOverride != "" {

			if err := changelog.ValidateTemplate(templateOverride); err != nil {
				logger.Error("Invalid template specified", "template", templateOverride, "error", err)
				fmt.Printf("Error: %v\n", err)
				os.Exit(1)
			}
			projectConfig.Changelog.Template = templateOverride
		}

		generator, err := changelog.NewGenerator(projectConfig)
		if err != nil {
			logger.Error("Failed to create changelog generator", "error", err)
			fmt.Printf("Error: Unable to create changelog generator: %v\n", err)
			os.Exit(1)
		}

		packageFilter, _ := cmd.Flags().GetString("package")
		if packageFilter != "" {
			if projectConfig.Type != config.RepositoryTypeMonorepo {
				logger.Error("Package filter can only be used with monorepo projects")
				fmt.Printf("Error: --package flag can only be used with monorepo projects\n")
				os.Exit(1)
			}

			packageExists := false
			for _, pkg := range projectConfig.Packages {
				if pkg.Name == packageFilter {
					packageExists = true
					break
				}
			}

			if !packageExists {
				logger.Error("Package not found", "package", packageFilter)
				fmt.Printf("Error: Package '%s' not found in project configuration\n", packageFilter)
				os.Exit(1)
			}
		}

		// Generate changelog from shipment history (primary) or current consignments (fallback)
		var changelogContent string
		var packageChangelogs map[string]string // For monorepo separate changelogs
		hasCurrentConsignments := len(consignments) > 0 && !regenerate

		if regenerate {

			if packageFilter != "" {
				changelogContent, err = generator.GenerateChangelogFromHistoryForPackage(packageFilter)
				if err != nil {
					logger.Error("Failed to generate changelog from history for package", "package", packageFilter, "error", err)
					fmt.Printf("Error: Unable to generate changelog from history for package '%s': %v\n", packageFilter, err)
					fmt.Println("No shipment history found. Use 'shipyard version' to ship some consignments first.")
					os.Exit(1)
				}
			} else {

				if projectConfig.Type == config.RepositoryTypeMonorepo {
					packageChangelogs, err = generator.GenerateChangelogsFromHistoryForPackages()
					if err != nil {
						logger.Error("Failed to generate package changelogs from history", "error", err)
						fmt.Printf("Error: Unable to generate package changelogs from history: %v\n", err)
						fmt.Println("No shipment history found. Use 'shipyard version' to ship some consignments first.")
						os.Exit(1)
					}
				} else {
					changelogContent, err = generator.GenerateChangelogFromHistory()
					if err != nil {
						logger.Error("Failed to generate changelog from history", "error", err)
						fmt.Printf("Error: Unable to generate changelog from history: %v\n", err)
						fmt.Println("No shipment history found. Use 'shipyard version' to ship some consignments first.")
						os.Exit(1)
					}
				}
			}
		} else {

			if packageFilter != "" {

				changelogContent, err = generator.GenerateChangelogFromHistoryForPackage(packageFilter)
				if err != nil {

					if hasCurrentConsignments {
						logger.Info("No shipment history found for package, using current consignments", "package", packageFilter)
						version, exists := versions[packageFilter]
						if !exists {
							logger.Error("No version calculated for package", "package", packageFilter)
							fmt.Printf("Error: No version calculated for package '%s'\n", packageFilter)
							os.Exit(1)
						}
						changelogContent, err = generator.GenerateChangelogForPackage(packageFilter, consignments, version)
						if err != nil {
							logger.Error("Failed to generate changelog for package", "package", packageFilter, "error", err)
							fmt.Printf("Error: Unable to generate changelog for package '%s': %v\n", packageFilter, err)
							os.Exit(1)
						}
					} else {
						logger.Error("No shipment history or current consignments found for package", "package", packageFilter)
						fmt.Printf("Error: No shipment history found for package '%s' and no current consignments to process\n", packageFilter)
						fmt.Println("Create some consignments with 'shipyard add' first, or ensure shipment history exists.")
						os.Exit(1)
					}
				}
			} else {

				if projectConfig.Type == config.RepositoryTypeMonorepo {

					packageChangelogs, err = generator.GenerateChangelogsFromHistoryForPackages()
					if err != nil {

						if hasCurrentConsignments {
							logger.Info("No shipment history found, using current consignments for package changelogs")
							packageChangelogs, err = generator.GenerateChangelogsForPackages(consignments, versions)
							if err != nil {
								logger.Error("Failed to generate package changelogs", "error", err)
								fmt.Printf("Error: Unable to generate package changelogs: %v\n", err)
								os.Exit(1)
							}
						} else {
							logger.Error("No shipment history or current consignments found for monorepo")
							fmt.Printf("Error: No shipment history found for monorepo and no current consignments to process\n")
							fmt.Println("Create some consignments with 'shipyard add' first, or ensure shipment history exists.")
							os.Exit(1)
						}
					}
				} else {

					changelogContent, err = generator.GenerateChangelogFromHistory()
					if err != nil {

						if hasCurrentConsignments {
							logger.Info("No shipment history found, using current consignments")
							changelogContent, err = generator.GenerateChangelog(consignments, versions)
							if err != nil {
								logger.Error("Failed to generate changelog", "error", err)
								fmt.Printf("Error: Unable to generate changelog: %v\n", err)
								os.Exit(1)
							}
						} else {
							logger.Error("No shipment history or current consignments found")
							fmt.Printf("Error: No shipment history found and no current consignments to process\n")
							fmt.Println("Create some consignments with 'shipyard add' first, or ensure shipment history exists.")
							os.Exit(1)
						}
					}
				}
			}
		}

		dryRun, _ := cmd.Flags().GetBool("dry-run")
		preview, _ := cmd.Flags().GetBool("preview")

		if dryRun || preview {

			if dryRun {

				if packageChangelogs != nil {

					fmt.Println("πŸ“„ Generated Package Changelogs (Raw):")
					fmt.Println("======================================")
					fmt.Println()
					for packageName, changelog := range packageChangelogs {
						fmt.Printf("--- Package: %s ---\n", packageName)
						fmt.Print(changelog)
						fmt.Println()
					}
				} else {

					fmt.Println("πŸ“„ Generated Changelog (Raw):")
					fmt.Println("=============================")
					fmt.Println()
					fmt.Print(changelogContent)
				}
				fmt.Println()
				fmt.Println("πŸ”– Version Information:")
				fmt.Println("======================")
				if packageFilter != "" {
					if version, exists := versions[packageFilter]; exists {
						fmt.Printf("%s: %s\n", packageFilter, version.String())
					}
				} else {
					for pkgName, version := range versions {
						fmt.Printf("%s: %s\n", pkgName, version.String())
					}
				}
				fmt.Println()
				fmt.Printf("πŸ“¦ Consignments to be processed: %d\n", len(consignments))
				fmt.Printf("πŸ“ Template: %s\n", projectConfig.Changelog.Template)
				if packageFilter != "" {
					fmt.Printf("🎯 Package filter: %s\n", packageFilter)
				}
				if packageChangelogs != nil {
					fmt.Printf("πŸ“š Generated %d package changelogs\n", len(packageChangelogs))
				}
				fmt.Println()
				fmt.Println("πŸ’‘ To apply these changes, run 'shipyard version' without --dry-run")
			} else {

				if packageChangelogs != nil {

					fmt.Println("πŸ“„ Generated Package Changelogs Preview:")
					fmt.Println("========================================")
					fmt.Println()
					for packageName, changelog := range packageChangelogs {
						fmt.Printf("--- Package: %s ---\n", packageName)
						rendered, err := renderMarkdown(changelog)
						if err != nil {
							logger.Error("Failed to render markdown for package", "package", packageName, "error", err)
							fmt.Printf("Warning: Failed to render markdown for package %s, showing raw content: %v\n", packageName, err)
							fmt.Print(changelog)
						} else {
							fmt.Print(rendered)
						}
						fmt.Println("=====================================")
						fmt.Println()
					}
					fmt.Printf("πŸ“š Generated %d package changelogs\n", len(packageChangelogs))
				} else {

					rendered, err := renderMarkdown(changelogContent)
					if err != nil {
						logger.Error("Failed to render markdown", "error", err)
						fmt.Printf("Warning: Failed to render markdown, showing raw content: %v\n", err)
						fmt.Print(changelogContent)
					} else {
						fmt.Println("πŸ“„ Generated Changelog Preview:")
						fmt.Println("===============================")
						fmt.Println()
						fmt.Print(rendered)
						fmt.Println("===============================")
					}
				}
				fmt.Printf("πŸ“ Template: %s\n", projectConfig.Changelog.Template)
				fmt.Printf("πŸ“¦ Consignments processed: %d\n", len(consignments))
				if packageFilter != "" {
					fmt.Printf("🎯 Package filter: %s\n", packageFilter)
				}
			}
			return
		}

		yes, _ := cmd.Flags().GetBool("yes")
		if !yes {
			var confirm bool
			var confirmationMessage string
			if regenerate {
				confirmationMessage = "This will regenerate the changelog from shipment history. Are you sure?"
			} else if hasCurrentConsignments {
				confirmationMessage = fmt.Sprintf("This will generate changelog and update package versions, clearing %d consignments. Are you sure?", len(consignments))
			} else {
				confirmationMessage = "This will regenerate the changelog from shipment history. Are you sure?"
			}

			form := huh.NewForm(
				huh.NewGroup(
					huh.NewConfirm().
						Title("Generate Changelog?").
						Description(confirmationMessage).
						Value(&confirm),
				),
			)

			if err := form.Run(); err != nil {
				logger.Error("Failed to get user confirmation", "error", err)
				fmt.Printf("Error: %v\n", err)
				os.Exit(1)
			}

			if !confirm {
				fmt.Println("Changelog generation cancelled.")
				return
			}
		}

		outputPath, _ := cmd.Flags().GetString("output")
		if outputPath == "" {
			outputPath = "CHANGELOG.md"
		}

		if _, err := os.Stat(outputPath); err == nil {
			if !yes {
				var overwrite bool
				form := huh.NewForm(
					huh.NewGroup(
						huh.NewConfirm().
							Title("Overwrite Existing Changelog?").
							Description(fmt.Sprintf("The file %s already exists. Do you want to overwrite it?", outputPath)).
							Value(&overwrite),
					),
				)

				if err := form.Run(); err != nil {
					logger.Error("Failed to get user confirmation", "error", err)
					fmt.Printf("Error: %v\n", err)
					os.Exit(1)
				}

				if !overwrite {
					fmt.Println("Changelog generation cancelled.")
					return
				}
			}
		}

		if packageChangelogs != nil {

			fmt.Printf("\nπŸ“ Writing package changelogs...\n")

			usePackagePaths := projectConfig.ShouldUsePackagePaths()
			defaultFilename := projectConfig.GetChangelogOutputPath()

			for packageName, changelog := range packageChangelogs {
				var packageOutputPath string

				if usePackagePaths {

					pkg := projectConfig.GetPackageByName(packageName)
					if pkg != nil {
						packageOutputPath = pkg.GetChangelogPath(defaultFilename)
					} else {

						packageOutputPath = filepath.Join(packageName, defaultFilename)
					}
				} else {

					if outputPath == "" {
						outputPath = defaultFilename
					}
					packageOutputPath = fmt.Sprintf("%s.%s.md", strings.TrimSuffix(outputPath, ".md"), packageName)
				}

				dir := filepath.Dir(packageOutputPath)
				if dir != "." {
					err := os.MkdirAll(dir, 0755)
					if err != nil {
						logger.Error("Failed to create directory for package changelog", "package", packageName, "dir", dir, "error", err)
						fmt.Printf("Error: Failed to create directory %s for package %s: %v\n", dir, packageName, err)
						os.Exit(1)
					}
				}

				err := os.WriteFile(packageOutputPath, []byte(changelog), 0644)
				if err != nil {
					logger.Error("Failed to write package changelog", "package", packageName, "path", packageOutputPath, "error", err)
					fmt.Printf("Error: Failed to write changelog for package %s to %s: %v\n", packageName, packageOutputPath, err)
					os.Exit(1)
				}
				fmt.Printf("   βœ“ %s: %s\n", packageName, packageOutputPath)
			}
		} else {

			if outputPath == "" {
				outputPath = projectConfig.GetChangelogOutputPath()
			}
			fmt.Printf("\nπŸ“ Writing changelog to %s...\n", outputPath)

			dir := filepath.Dir(outputPath)
			if dir != "." {
				err := os.MkdirAll(dir, 0755)
				if err != nil {
					logger.Error("Failed to create directory for changelog", "dir", dir, "error", err)
					fmt.Printf("Error: Failed to create directory %s: %v\n", dir, err)
					os.Exit(1)
				}
			}

			err := os.WriteFile(outputPath, []byte(changelogContent), 0644)
			if err != nil {
				logger.Error("Failed to write changelog", "path", outputPath, "error", err)
				fmt.Printf("Error: Failed to write changelog to %s: %v\n", outputPath, err)
				os.Exit(1)
			}
			fmt.Printf("   βœ“ Changelog written successfully\n")
		}

		// Apply version updates and record shipment history BEFORE writing changelog
		var appliedVersions map[string]*semver.Version
		if hasCurrentConsignments && !regenerate {
			fmt.Printf("\nπŸ”„ Recording shipment history...\n")

			gitOps := git.NewGitOperations(projectConfig)

			gitPush, _ := cmd.Flags().GetBool("git-push")
			gitTag, _ := cmd.Flags().GetBool("git-tag")

			if gitPush {
				gitTag = true
			}

			// Create git tags if requested and git is available
			var gitTags map[string]string
			if gitTag && gitOps.IsAvailable() {

				tempVersions, err := manager.CalculateAllVersions()
				if err != nil {
					logger.Error("Failed to calculate versions for git tags", "error", err)
					fmt.Printf("Error: Unable to calculate versions for git tags: %v\n", err)
					os.Exit(1)
				}

				// Get consignment summaries for commit message
				var consignmentSummaries []string
				for _, c := range consignments {
					consignmentSummaries = append(consignmentSummaries, c.Summary)
				}

				commitMessage := gitOps.CreateShipmentCommitMessage(tempVersions, consignmentSummaries)
				gitTags, err = gitOps.CreateShipmentTags(tempVersions, commitMessage)
				if err != nil {
					logger.Error("Failed to create git tags", "error", err)
					fmt.Printf("Warning: Failed to create git tags: %v\n", err)

					gitTags = nil
				} else {
					fmt.Printf("βœ… Git tags created!\n")
				}
			}

			templateName := projectConfig.Changelog.Template
			appliedVersions, err = manager.RecordShipmentHistoryWithTags(templateName, gitTags)
			if err != nil {
				logger.Error("Failed to record shipment history", "error", err)
				fmt.Printf("Error: Unable to record shipment history: %v\n", err)
				os.Exit(1)
			}

			fmt.Printf("βœ… Shipment history recorded!\n")

			fmt.Printf("\nπŸ”„ Regenerating changelog from updated history...\n")
			if packageFilter != "" {
				changelogContent, err = generator.GenerateChangelogFromHistoryForPackage(packageFilter)
				if err != nil {
					logger.Error("Failed to generate changelog from history for package", "package", packageFilter, "error", err)
					fmt.Printf("Error: Unable to generate changelog from history for package '%s': %v\n", packageFilter, err)
					os.Exit(1)
				}
			} else {
				changelogContent, err = generator.GenerateChangelogFromHistory()
				if err != nil {
					logger.Error("Failed to generate changelog from history", "error", err)
					fmt.Printf("Error: Unable to generate changelog from history: %v\n", err)
					os.Exit(1)
				}
			}

			fmt.Printf("βœ… Changelog regenerated from updated history!\n")
		} else if regenerate {
			fmt.Printf("\nοΏ½ Changelog regenerated from shipment history (no version updates applied)\n")
			appliedVersions = make(map[string]*semver.Version)
		} else {
			fmt.Printf("\nπŸ“‹ No current consignments to apply - changelog regenerated from shipment history\n")
			appliedVersions = make(map[string]*semver.Version)
		}

		if err := writeChangelog(outputPath, changelogContent); err != nil {
			logger.Error("Failed to write changelog", "error", err)
			fmt.Printf("Error: Unable to write changelog: %v\n", err)
			os.Exit(1)
		}

		fmt.Printf("βœ… Changelog generated successfully!\n")
		fmt.Printf("πŸ“ File: %s\n", outputPath)

		if hasCurrentConsignments && !regenerate {
			fmt.Printf("\nπŸ”„ Applying version updates...\n")

			err = manager.ApplyVersionUpdatesAndClearConsignments(appliedVersions)
			if err != nil {
				logger.Error("Failed to apply version updates", "error", err)
				fmt.Printf("Error: Unable to apply version updates: %v\n", err)
				os.Exit(1)
			}

			fmt.Printf("\nβœ… Version updates applied successfully!\n")
			fmt.Printf("πŸ“¦ Packages updated:\n")
			for pkgName, version := range appliedVersions {
				fmt.Printf("   - %s: %s\n", pkgName, version.String())
			}
			fmt.Printf("\n🧹 Consignments cleared: %d\n", len(consignments))

			gitCommit, _ := cmd.Flags().GetBool("git-commit")
			gitPush, _ := cmd.Flags().GetBool("git-push")

			if gitPush {
				gitCommit = true
			}

			if gitCommit || gitPush {
				gitOps := git.NewGitOperations(projectConfig)
				if gitOps.IsAvailable() {
					// Get consignment summaries for commit message
					var consignmentSummaries []string
					for _, c := range consignments {
						consignmentSummaries = append(consignmentSummaries, c.Summary)
					}

					changelogGitPath := outputPath
					if changelogGitPath == "" {
						changelogGitPath = "CHANGELOG.md"
					}

					err = gitOps.PerformShipmentGitOperations(appliedVersions, changelogGitPath, consignmentSummaries, gitCommit, gitPush)
					if err != nil {
						logger.Error("Failed to perform git operations", "error", err)
						fmt.Printf("Warning: Failed to perform git operations: %v\n", err)

					}
				} else {
					fmt.Printf("⚠️  Git repository not found - skipping git operations\n")
				}
			}
		}

		fmt.Printf("πŸ“ Template: %s\n", projectConfig.Changelog.Template)
		if packageFilter != "" {
			fmt.Printf("🎯 Package filter: %s\n", packageFilter)
		}

		gitCommit, _ := cmd.Flags().GetBool("git-commit")
		gitPush, _ := cmd.Flags().GetBool("git-push")
		gitTag, _ := cmd.Flags().GetBool("git-tag")

		if gitPush {
			gitCommit = true
			gitTag = true
		}

		fmt.Printf("\nπŸ’‘ Next steps:\n")
		fmt.Printf("   - Review the generated changelog\n")
		if hasCurrentConsignments && !regenerate {
			fmt.Printf("   - Review updated package manifests\n")
		}

		if !gitCommit {
			fmt.Printf("   - Commit all changes to your repository\n")
		}
		if hasCurrentConsignments && !regenerate && !gitTag {
			fmt.Printf("   - Create and push git tags for the new versions\n")
		}

		logger.Info("Changelog generation completed successfully",
			"output", outputPath,
			"template", projectConfig.Changelog.Template,
			"packages_updated", len(appliedVersions),
			"consignments_cleared", len(consignments),
			"regenerate_mode", regenerate,
			"from_history", regenerate || !hasCurrentConsignments,
		)
	},
}

Functions ΒΆ

This section is empty.

Types ΒΆ

type PackageConfig ΒΆ

type PackageConfig struct {
	Name      string
	Path      string
	Ecosystem string
	Manifest  string
}

Jump to

Keyboard shortcuts

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