55 "encoding/json"
66 "fmt"
77 "os"
8+ "path/filepath"
89
910 "github.com/spf13/cobra"
1011
@@ -21,12 +22,15 @@ var (
2122 kubeconfig string
2223 kubeContext string
2324 bootstrapAgeKey string
25+ encryption string
26+ gitcryptKeyFile string
27+ appPath string
2428)
2529
2630var bootstrapCmd = & cobra.Command {
2731 Use : "bootstrap <environment>" ,
2832 Short : "Bootstrap a Kubernetes cluster with ArgoCD and secrets" ,
29- Long : `Decrypts the SOPS-encrypted secrets file, installs ArgoCD,
33+ Long : `Decrypts the secrets file, installs ArgoCD,
3034creates Kubernetes secrets, and applies the App of Apps root Application.
3135
3236Replaces the manual install.sh process.` ,
@@ -35,12 +39,15 @@ Replaces the manual install.sh process.`,
3539}
3640
3741func init () {
38- bootstrapCmd .Flags ().StringVar (& secretsFile , "secrets-file" , "" , "path to SOPS-encrypted secrets file (default: secrets.<env>.enc.yaml)" )
42+ bootstrapCmd .Flags ().StringVar (& secretsFile , "secrets-file" , "" , "path to secrets file (default: secrets.<env>.enc.yaml or secrets.<env> .yaml)" )
3943 bootstrapCmd .Flags ().BoolVar (& dryRun , "dry-run" , false , "print manifests without applying" )
4044 bootstrapCmd .Flags ().BoolVar (& skipArgoCDInstall , "skip-argocd-install" , false , "skip ArgoCD installation" )
4145 bootstrapCmd .Flags ().StringVar (& kubeconfig , "kubeconfig" , "" , "path to kubeconfig file" )
4246 bootstrapCmd .Flags ().StringVar (& kubeContext , "context" , "" , "kubeconfig context to use" )
4347 bootstrapCmd .Flags ().StringVar (& bootstrapAgeKey , "age-key-file" , "" , "path to age private key file for SOPS decryption" )
48+ bootstrapCmd .Flags ().StringVar (& encryption , "encryption" , "sops" , "encryption backend (sops|git-crypt)" )
49+ bootstrapCmd .Flags ().StringVar (& gitcryptKeyFile , "gitcrypt-key-file" , "" , "path to git-crypt symmetric key file (creates K8s secret)" )
50+ bootstrapCmd .Flags ().StringVar (& appPath , "app-path" , "apps" , "path inside the Git repo for the App of Apps source" )
4451
4552 rootCmd .AddCommand (bootstrapCmd )
4653}
@@ -50,16 +57,34 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
5057
5158 fmt .Printf ("==> Bootstrapping cluster for environment: %s\n " , env )
5259
53- // Step 2: Decrypt secrets
54- sf := secretsFile
55- if sf == "" {
56- sf = config .SecretsFileName (env )
57- }
58- fmt .Printf ("==> Decrypting secrets from %s...\n " , sf )
59- sopsOpts := & sops.Options {AgeKeyFile : bootstrapAgeKey }
60- envSecrets , err := config .LoadSecrets (sf , sopsOpts )
61- if err != nil {
62- return err
60+ // Load secrets based on encryption backend
61+ var envSecrets * config.EnvironmentSecrets
62+ var err error
63+
64+ switch encryption {
65+ case "git-crypt" :
66+ sf := secretsFile
67+ if sf == "" {
68+ sf = filepath .Join (baseDir , config .SecretsFileNamePlain (env ))
69+ }
70+ fmt .Printf ("==> Loading plaintext secrets from %s...\n " , sf )
71+ envSecrets , err = config .LoadSecretsPlaintext (sf )
72+ if err != nil {
73+ return err
74+ }
75+ case "sops" :
76+ sf := secretsFile
77+ if sf == "" {
78+ sf = filepath .Join (baseDir , config .SecretsFileName (env ))
79+ }
80+ fmt .Printf ("==> Decrypting secrets from %s...\n " , sf )
81+ sopsOpts := & sops.Options {AgeKeyFile : bootstrapAgeKey }
82+ envSecrets , err = config .LoadSecrets (sf , sopsOpts )
83+ if err != nil {
84+ return err
85+ }
86+ default :
87+ return fmt .Errorf ("unsupported encryption backend: %s (use sops or git-crypt)" , encryption )
6388 }
6489
6590 if verbose {
@@ -68,18 +93,18 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
6893 }
6994
7095 if dryRun {
71- return printDryRun (envSecrets , env )
96+ return printDryRun (envSecrets , env , appPath )
7297 }
7398
74- // Step 3: Create k8s client
99+ // Create k8s client
75100 client , err := k8s .NewClient (kubeconfig , kubeContext )
76101 if err != nil {
77102 return fmt .Errorf ("failed to create kubernetes client: %w" , err )
78103 }
79104
80105 ctx := context .Background ()
81106
82- // Step 4: Create Kubernetes secrets (before Helm install, as the chart may reference them)
107+ // Create Kubernetes secrets (before Helm install, as the chart may reference them)
83108 fmt .Println ("==> Creating Kubernetes secrets..." )
84109 if err := client .EnsureNamespace (ctx , "argocd" ); err != nil {
85110 return err
@@ -88,21 +113,33 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
88113 return err
89114 }
90115
91- // Step 5: Install ArgoCD via Helm
116+ // If git-crypt key file provided, store it as a K8s secret
117+ if gitcryptKeyFile != "" {
118+ keyData , err := os .ReadFile (gitcryptKeyFile )
119+ if err != nil {
120+ return fmt .Errorf ("failed to read git-crypt key file: %w" , err )
121+ }
122+ fmt .Println ("==> Creating git-crypt-key secret..." )
123+ if err := client .CreateGitCryptKeySecret (ctx , keyData ); err != nil {
124+ return err
125+ }
126+ }
127+
128+ // Install ArgoCD via Helm
92129 if ! skipArgoCDInstall {
93130 fmt .Println ("==> Installing ArgoCD via Helm..." )
94- if err := helm .InstallArgoCD (ctx , kubeconfig , kubeContext , env , verbose ); err != nil {
131+ if err := helm .InstallArgoCD (ctx , kubeconfig , kubeContext , env , baseDir , verbose ); err != nil {
95132 return fmt .Errorf ("failed to install ArgoCD: %w" , err )
96133 }
97134 }
98135
99- // Step 6: Apply App of Apps
136+ // Apply App of Apps
100137 fmt .Printf ("==> Applying App of Apps for environment: %s\n " , env )
101- if _ , err := client .ApplyAppOfApps (ctx , envSecrets .Repo .URL , envSecrets .Repo .TargetRevision , env , false ); err != nil {
138+ if _ , err := client .ApplyAppOfApps (ctx , envSecrets .Repo .URL , envSecrets .Repo .TargetRevision , env , appPath , false ); err != nil {
102139 return err
103140 }
104141
105- // Step 7: Print access instructions
142+ // Print access instructions
106143 fmt .Println ("\n ==> Done! ArgoCD is installed and the app-of-apps root Application has been created." )
107144 fmt .Println (" Access the ArgoCD UI:" )
108145 fmt .Println (" kubectl port-forward svc/argocd-server -n argocd 8080:443" )
@@ -112,7 +149,7 @@ func runBootstrap(cmd *cobra.Command, args []string) error {
112149 return nil
113150}
114151
115- func printDryRun (envSecrets * config.EnvironmentSecrets , env string ) error {
152+ func printDryRun (envSecrets * config.EnvironmentSecrets , env , appPath string ) error {
116153 fmt .Println ("\n --- DRY RUN: Kubernetes Secrets ---" )
117154
118155 // Repo SSH secret
@@ -155,7 +192,7 @@ func printDryRun(envSecrets *config.EnvironmentSecrets, env string) error {
155192 "source" : map [string ]interface {}{
156193 "repoURL" : envSecrets .Repo .URL ,
157194 "targetRevision" : envSecrets .Repo .TargetRevision ,
158- "path" : "apps" ,
195+ "path" : appPath ,
159196 "helm" : map [string ]interface {}{
160197 "valueFiles" : []string {
161198 fmt .Sprintf ("values/%s.yaml" , env ),
0 commit comments