package main import ( "fmt" "os" "os/exec" "path/filepath" "strings" ) const fluxConfigTmpl = `--- apiVersion: source.toolkit.fluxcd.io/v1beta2 kind: HelmRepository metadata: name: ghcr-charts namespace: flux-system spec: interval: 5m type: oci url: oci://ghcr.io/%s secretRef: name: ghcr-auth --- apiVersion: source.toolkit.fluxcd.io/v1 kind: GitRepository metadata: name: cicd-deployment-manifests namespace: flux-system spec: interval: 1m0s url: https://github.com/%s/%s ref: branch: main secretRef: name: github-auth --- apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: namespaces namespace: flux-system spec: interval: 10m path: ./namespaces prune: true sourceRef: kind: GitRepository name: cicd-deployment-manifests --- apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: preview-apps namespace: flux-system spec: dependsOn: - name: namespaces interval: 2m0s path: ./apps/previews prune: true sourceRef: kind: GitRepository name: cicd-deployment-manifests --- apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: staging-apps namespace: flux-system spec: dependsOn: - name: namespaces interval: 2m0s path: ./apps/staging prune: true sourceRef: kind: GitRepository name: cicd-deployment-manifests --- apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: production-apps namespace: flux-system spec: dependsOn: - name: namespaces interval: 2m0s path: ./apps/production prune: true sourceRef: kind: GitRepository name: cicd-deployment-manifests ` func initializeManifestsRepo() { fullRepoName := fmt.Sprintf("%s/%s", orgName, manifestsRepoName) fmt.Printf("\n[INFO] Setting up manifests repository '%s'...\n", fullRepoName) // Check if repository exists, create if not if err := runCommandQuiet("gh", "repo", "view", fullRepoName); err != nil { fmt.Printf("[INFO] Creating repository '%s'...\n", fullRepoName) if err := runCommand("gh", "repo", "create", fullRepoName, "--private", "--description", "Centralized deployment manifests for Flux CD"); err != nil { fmt.Printf("[ERROR] Failed to create repository: %v\n", err) os.Exit(1) } fmt.Println("[SUCCESS] Manifests repository created.") } else { fmt.Println("[SUCCESS] Manifests repository already exists.") } // Clone and setup repository structure setupRepository(fullRepoName, createManifestsStructure) } func initializeFluxRepo() { fullRepoName := fmt.Sprintf("%s/%s", orgName, fluxRepoName) fmt.Printf("\n[INFO] Setting up flux repository '%s'...\n", fullRepoName) // Check if repository exists, create if not if err := runCommandQuiet("gh", "repo", "view", fullRepoName); err != nil { fmt.Printf("[INFO] Creating repository '%s'...\n", fullRepoName) if err := runCommand("gh", "repo", "create", fullRepoName, "--private", "--description", "Flux CD cluster configurations"); err != nil { fmt.Printf("[ERROR] Failed to create repository: %v\n", err) os.Exit(1) } fmt.Println("[SUCCESS] Flux repository created.") } else { fmt.Println("[SUCCESS] Flux repository already exists.") } // Clone and setup repository structure setupRepository(fullRepoName, createFluxStructure) } func setupRepository(fullRepoName string, structureFunc func(string)) { // Create temporary directory for cloning tempDir, err := os.MkdirTemp("", "repo-setup-*") if err != nil { fmt.Printf("[ERROR] Failed to create temporary directory: %v\n", err) os.Exit(1) } defer os.RemoveAll(tempDir) // Clone repository fmt.Printf("[INFO] Cloning repository into temporary directory...\n") repoURL := fmt.Sprintf("https://github.com/%s.git", fullRepoName) if err := runCommand("git", "clone", repoURL, tempDir); err != nil { fmt.Printf("[ERROR] Failed to clone repository: %v\n", err) os.Exit(1) } // Create repository structure fmt.Println("[INFO] Creating repository structure...") structureFunc(tempDir) // Commit and push changes if any exist fmt.Println("[INFO] Committing and pushing changes...") commitAndPush(tempDir, fullRepoName) } func createManifestsStructure(baseDir string) { // Create README for manifests repository manifestsReadme := fmt.Sprintf(manifestsReadmeTmpl, manifestsRepoName) writeFile(filepath.Join(baseDir, "README.md"), manifestsReadme) // Create namespaces directory and files namespacesDir := filepath.Join(baseDir, "namespaces") if err := os.MkdirAll(namespacesDir, 0755); err != nil { fmt.Printf("[ERROR] Failed to create namespaces directory: %v\n", err) os.Exit(1) } // Create staging namespace stagingNamespace := `apiVersion: v1 kind: Namespace metadata: name: staging ` writeFile(filepath.Join(namespacesDir, "staging.yaml"), stagingNamespace) // Create production namespace productionNamespace := `apiVersion: v1 kind: Namespace metadata: name: production ` writeFile(filepath.Join(namespacesDir, "production.yaml"), productionNamespace) // Create namespaces kustomization namespacesKustomization := `apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - staging.yaml - production.yaml ` writeFile(filepath.Join(namespacesDir, "kustomization.yaml"), namespacesKustomization) // Create apps directory structure with .gitkeep files environments := []string{"staging", "production", "previews"} for _, env := range environments { dirPath := filepath.Join(baseDir, "apps", env) if err := os.MkdirAll(dirPath, 0755); err != nil { fmt.Printf("[ERROR] Failed to create directory %s: %v\n", dirPath, err) os.Exit(1) } // Create .gitkeep to ensure empty directories are tracked writeFile(filepath.Join(dirPath, ".gitkeep"), "") } } func createFluxStructure(baseDir string) { // Create README for flux repository fluxReadme := fmt.Sprintf(fluxReadmeTmpl, fluxRepoName) writeFile(filepath.Join(baseDir, "README.md"), fluxReadme) // Create clusters/dev-cluster directory clusterDir := filepath.Join(baseDir, "clusters", "dev-cluster") if err := os.MkdirAll(clusterDir, 0755); err != nil { fmt.Printf("[ERROR] Failed to create cluster directory: %v\n", err) os.Exit(1) } // Create the flux configuration file with dynamic organization name fluxConfig := fmt.Sprintf(fluxConfigTmpl, strings.ToLower(orgName), orgName, manifestsRepoName) writeFile(filepath.Join(clusterDir, "cicd-manifests-repo.yaml"), fluxConfig) } func commitAndPush(tempDir, repoName string) { // Configure git user for the commit runCommandInDir(tempDir, "git", "config", "user.name", "Maidn") runCommandInDir(tempDir, "git", "config", "user.email", "maidn@free-maidn.com") runCommandInDir(tempDir, "git", "add", ".") // Check if there are changes to commit statusCmd := exec.Command("git", "status", "--porcelain") statusCmd.Dir = tempDir output, _ := statusCmd.Output() if len(output) == 0 { fmt.Println("[INFO] No changes to commit. Repository is already up to date.") } else { // Commit and push changes commitMsg := "feat: Initialize repository structure for CI/CD" if err := runCommandInDir(tempDir, "git", "commit", "-m", commitMsg); err != nil { fmt.Printf("[ERROR] Failed to commit changes: %v\n", err) os.Exit(1) } if err := runCommandInDir(tempDir, "git", "push", "origin", "main"); err != nil { fmt.Printf("[ERROR] Failed to push changes: %v\n", err) os.Exit(1) } fmt.Printf("[SUCCESS] Changes pushed to %s successfully.\n", repoName) } }