build.go

86 lines
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
package commands

import (
	"cmp"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

const buildUsage = `Build a Congo project for production

Usage:
  congo build [flags]

Flags:
  --os <os>        Target OS (default: current)
  --arch <arch>    Target architecture (default: current)
  --output <path>  Output binary path (default: build/<project-name>)

Examples:
  congo build
  congo build --os linux --arch amd64
  congo build --output myapp
`

func Build() {
	fs := flag.NewFlagSet("build", flag.ExitOnError)
	fs.Usage = func() { fmt.Print(buildUsage) }

	targetOS := fs.String("os", "", "Target OS")
	targetArch := fs.String("arch", "", "Target architecture")
	output := fs.String("output", "", "Output binary path")

	fs.Parse(os.Args[2:])

	wd, err := os.Getwd()
	if err != nil {
		log.Fatalf("could not determine working directory: %v", err)
	}
	name := filepath.Base(wd)
	dir := findAppDir()

	outPath := cmp.Or(*output, filepath.Join("build", name))
	os.MkdirAll(filepath.Dir(outPath), 0755)

	env := os.Environ()
	if *targetOS != "" {
		env = append(env, "GOOS="+*targetOS)
	}
	if *targetArch != "" {
		env = append(env, "GOARCH="+*targetArch)
	}

	cmd := exec.Command("go", "build", "-o", outPath, "./"+dir)
	cmd.Env = env
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	fmt.Printf("Building ./%s → %s", dir, outPath)
	if *targetOS != "" || *targetArch != "" {
		fmt.Printf(" (%s/%s)", cmp.Or(*targetOS, "native"), cmp.Or(*targetArch, "native"))
	}
	fmt.Println()

	if err := cmd.Run(); err != nil {
		log.Fatalf("Build failed: %v", err)
	}

	// Show binary size
	if info, err := os.Stat(outPath); err == nil {
		fmt.Printf("\n  %s (%.1f MB)\n", outPath, float64(info.Size())/(1024*1024))
	}

	fmt.Println("\nNext steps:")
	fmt.Printf("  ./%s                    # run locally\n", outPath)
	if _, err := os.Stat("Dockerfile"); err == nil {
		fmt.Println("  docker build -t app .     # containerize")
	}
	if _, err := os.Stat("infra.json"); err == nil {
		fmt.Println("  congo launch              # deploy to server")
	}
	fmt.Println()
}