package cmd import ( "fmt" "os" "kforge/internal/generator" "github.com/spf13/cobra" ) // ------------------------------------------------------------ // kforge gitea-actions // ------------------------------------------------------------ var ( giteaActionsOutput string giteaActionsBranch string giteaActionsEnvs []string ) var giteaActionsCmd = &cobra.Command{ Use: "gitea-actions", Short: "Generate a Gitea Actions deploy workflow for this app", Long: `Generates .gitea/workflows/deploy.yml that: - Builds and pushes the Docker image on push to main - Runs kforge validate - Applies cluster secrets (idempotent) - Generates manifests and applies them with kubectl - Rolls out the deployment DNS is handled automatically by external-dns reading the Ingress annotations that kforge writes — no separate DNS step needed. Re-run whenever you add environments or change deploy options. Examples: kforge gitea-actions kforge gitea-actions --output .gitea/workflows/deploy.yml kforge gitea-actions --env staging --env production`, RunE: runGiteaActions, } func init() { giteaActionsCmd.Flags().StringVarP(&giteaActionsOutput, "output", "o", ".gitea/workflows/deploy.yml", "Output path for the generated workflow file") giteaActionsCmd.Flags().StringVar(&giteaActionsBranch, "branch", "main", "Branch that triggers the workflow") giteaActionsCmd.Flags().StringArrayVarP(&giteaActionsEnvs, "env", "e", nil, "Environments to deploy (default: all, staging before production)") rootCmd.AddCommand(giteaActionsCmd) } func runGiteaActions(cmd *cobra.Command, args []string) error { cfg, err := loadConfig() if err != nil { return err } workflow, err := generator.GenerateGiteaActions(cfg, generator.GiteaActionsOptions{ Branch: giteaActionsBranch, Environments: giteaActionsEnvs, }) if err != nil { return fmt.Errorf("generating workflow: %w", err) } return writeWorkflow(giteaActionsOutput, workflow) } // ------------------------------------------------------------ // kforge gitea-preview // ------------------------------------------------------------ var giteaPreviewOutput string var giteaPreviewCmd = &cobra.Command{ Use: "gitea-preview", Short: "Generate a Gitea Actions workflow for PR preview environments", Long: `Generates .gitea/workflows/preview.yml that: - On PR open/sync: builds a PR-tagged image, applies secrets, generates manifests (including a Namespace), and deploys. - On PR close: deletes the preview namespace and all resources. Requires preview.enabled: true in kforge.yml. Example kforge.yml preview block: preview: enabled: true base_environment: staging namespace_prefix: preview-pr hostname_template: "pr-${PR_NUMBER}.${name}.example.com" Examples: kforge gitea-preview kforge gitea-preview --output .gitea/workflows/preview.yml`, RunE: runGiteaPreview, } func init() { giteaPreviewCmd.Flags().StringVarP(&giteaPreviewOutput, "output", "o", ".gitea/workflows/preview.yml", "Output path for the generated preview workflow file") rootCmd.AddCommand(giteaPreviewCmd) } func runGiteaPreview(cmd *cobra.Command, args []string) error { cfg, err := loadConfig() if err != nil { return err } workflow, err := generator.GeneratePreviewActions(cfg) if err != nil { return fmt.Errorf("generating preview workflow: %w", err) } return writeWorkflow(giteaPreviewOutput, workflow) } // ------------------------------------------------------------ // Shared helpers // ------------------------------------------------------------ func writeWorkflow(path, content string) error { if path == "-" { fmt.Print(content) return nil } // Ensure parent directory exists. dir := path for i := len(dir) - 1; i >= 0; i-- { if dir[i] == '/' || dir[i] == '\\' { dir = dir[:i] break } } if dir != path { if err := os.MkdirAll(dir, 0o755); err != nil { return fmt.Errorf("creating output directory: %w", err) } } if err := os.WriteFile(path, []byte(content), 0o644); err != nil { return fmt.Errorf("writing workflow: %w", err) } fmt.Printf("✓ Workflow written to %s\n", path) return nil }