This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"kforge/internal/config"
|
||||
dnsProvider "kforge/internal/dns"
|
||||
"kforge/internal/generator"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// kforge dns ensure
|
||||
// ------------------------------------------------------------
|
||||
|
||||
var dnsCmd = &cobra.Command{
|
||||
Use: "dns",
|
||||
Short: "Manage DNS records for kforge environments",
|
||||
}
|
||||
|
||||
var dnsEnsureEnvs []string
|
||||
|
||||
var dnsEnsureCmd = &cobra.Command{
|
||||
Use: "ensure",
|
||||
Short: "Create or update DNS A records for ingress hosts",
|
||||
Long: `For each ingress host with dns_record: true, creates or updates
|
||||
an A record pointing to KFORGE_NODE_IP.
|
||||
|
||||
Idempotent — safe to run on every deploy. Skips hosts where the
|
||||
record already points to the correct IP.
|
||||
|
||||
Examples:
|
||||
kforge dns ensure --env staging
|
||||
kforge dns ensure --env staging --env production`,
|
||||
RunE: runDNSEnsure,
|
||||
}
|
||||
|
||||
func init() {
|
||||
dnsEnsureCmd.Flags().StringArrayVarP(&dnsEnsureEnvs, "env", "e", nil,
|
||||
"Environment(s) to ensure DNS records for (default: all)")
|
||||
dnsCmd.AddCommand(dnsEnsureCmd)
|
||||
rootCmd.AddCommand(dnsCmd)
|
||||
}
|
||||
|
||||
func runDNSEnsure(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.DNS.SkipDNS {
|
||||
fmt.Println("dns.skip_dns is true — skipping DNS management")
|
||||
return nil
|
||||
}
|
||||
|
||||
nodeIP := cfg.DNS.NodeIP
|
||||
if nodeIP == "" {
|
||||
nodeIP = os.Getenv("KFORGE_NODE_IP")
|
||||
}
|
||||
if nodeIP == "" {
|
||||
return fmt.Errorf("node IP not set: add dns.node_ip to kforge.yml or set KFORGE_NODE_IP")
|
||||
}
|
||||
|
||||
provider, err := dnsProvider.NewProvider(cfg.DNS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialising DNS provider: %w", err)
|
||||
}
|
||||
|
||||
envKeys := dnsEnsureEnvs
|
||||
if len(envKeys) == 0 {
|
||||
envKeys = config.EnvironmentKeys(cfg)
|
||||
}
|
||||
|
||||
for _, envKey := range envKeys {
|
||||
fmt.Printf("\nEnsuring DNS records for: %s\n", envKey)
|
||||
env, err := config.ResolveEnvironment(cfg, envKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dnsProvider.EnsureRecordsForEnvironment(provider, &env, nodeIP); err != nil {
|
||||
return fmt.Errorf("env %q: %w", envKey, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// kforge gitea-actions
|
||||
// ------------------------------------------------------------
|
||||
|
||||
var (
|
||||
giteaActionsOutput string
|
||||
giteaActionsBranch string
|
||||
giteaActionsEnvs []string
|
||||
)
|
||||
|
||||
var giteaActionsCmd = &cobra.Command{
|
||||
Use: "gitea-actions",
|
||||
Short: "Generate a Gitea Actions workflow for this app",
|
||||
Long: `Generates a complete .gitea/workflows/deploy.yml that:
|
||||
- Builds and pushes the Docker image on every push to main
|
||||
- Runs kforge validate
|
||||
- Applies cluster secrets (idempotent)
|
||||
- Ensures DNS records
|
||||
- Generates manifests and applies them with kubectl
|
||||
- Rolls out the deployment
|
||||
|
||||
The generated workflow replaces your hand-written deploy.yml.
|
||||
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)
|
||||
}
|
||||
|
||||
if giteaActionsOutput == "-" {
|
||||
fmt.Print(workflow)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure parent directory exists.
|
||||
dir := giteaActionsOutput
|
||||
for i := len(dir) - 1; i >= 0; i-- {
|
||||
if dir[i] == '/' {
|
||||
dir = dir[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if dir != giteaActionsOutput {
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return fmt.Errorf("creating output directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.WriteFile(giteaActionsOutput, []byte(workflow), 0o644); err != nil {
|
||||
return fmt.Errorf("writing workflow: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Gitea Actions workflow written to %s\n", giteaActionsOutput)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user