diff --git a/README.md b/README.md index 86ab4ab..da9a4ec 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,7 @@ A simple yet powerful Go-based CLI tool for automating Docker container deployments to remote servers. This tool handles the entire deployment process including building Docker images, transferring them to remote hosts, and managing container lifecycle. - -https://github.com/user-attachments/assets/1859f611-799e-4a6a-976e-f12ae59c232e - + ## Prerequisites @@ -72,16 +70,17 @@ go build -o copepod | Option | Environment Variable | Default | Description | |-----------------|---------------------|------------------|--------------------------------| -| --host | COPEPOD_HOST | | Remote host to deploy to | -| --user | COPEPOD_USER | | SSH user for remote host | -| --image | COPEPOD_IMAGE | app | Docker image name | -| --tag | COPEPOD_TAG | latest | Docker image tag | -| --platform | COPEPOD_PLATFORM | linux/amd64 | Docker platform | +| --host | DEPLOY_HOST | | Remote host to deploy to | +| --user | DEPLOY_USER | | SSH user for remote host | +| --image | DEPLOY_IMAGE | app | Docker image name | +| --tag | DEPLOY_TAG | latest | Docker image tag | +| --platform | DEPLOY_PLATFORM | linux/amd64 | Docker platform | | --ssh-key | SSH_KEY_PATH | | Path to SSH key | | --container-name| CONTAINER_NAME | app | Name for the container | | --container-port| CONTAINER_PORT | 3000 | Container port | | --host-port | HOST_PORT | 3000 | Host port | | --env-file | ENV_FILE | .env.production | Environment file | +| --build-arg | BUILD_ARGS | | Build arguments (KEY=VALUE) | ### Example Commands @@ -103,6 +102,23 @@ Using environment file: ./copepod --env-file .env.production ``` +Using build arguments: + +```bash +# Single build argument +./copepod --host example.com --user deploy --build-arg VERSION=1.0.0 + +# Multiple build arguments +./copepod --host example.com --user deploy --build-arg VERSION=1.0.0 --build-arg ENV=prod + +# Using environment variable +export BUILD_ARGS="VERSION=1.0.0,ENV=prod" +./copepod --host example.com --user deploy + +# Using git commit hash +./copepod --host example.com --user deploy --build-arg GIT_HASH=$(git rev-parse HEAD) +``` + ## Directory Structure Your project directory should look like this: @@ -118,7 +134,7 @@ Your project directory should look like this: 1. Validates configuration and checks prerequisites 2. Verifies Docker installation and SSH connectivity -3. Builds Docker image locally +3. Builds Docker image locally with any provided build arguments 4. Transfers image to remote host 5. Copies environment file (if specified) 6. Stops and removes existing container @@ -148,6 +164,7 @@ The tool includes error handling for common scenarios: - Uses SSH key-based authentication - Supports custom SSH key paths - Environment variables can be passed securely via env file +- Build arguments can be used for sensitive build-time variables - No sensitive information is logged ## Known Limitations diff --git a/demo.mp4 b/demo.mp4 index b248f8f..8bb7f79 100644 Binary files a/demo.mp4 and b/demo.mp4 differ diff --git a/main.go b/main.go index 2ad33be..4912494 100644 --- a/main.go +++ b/main.go @@ -14,16 +14,17 @@ import ( // Config holds the deployment configuration type Config struct { - Host string `json:"host"` - User string `json:"user"` - Image string `json:"image"` - Tag string `json:"tag"` - Platform string `json:"platform"` - SSHKey string `json:"sshKey"` - ContainerName string `json:"containerName"` - ContainerPort string `json:"containerPort"` - HostPort string `json:"hostPort"` - EnvFile string `json:"envFile"` + Host string `json:"host"` + User string `json:"user"` + Image string `json:"image"` + Tag string `json:"tag"` + Platform string `json:"platform"` + SSHKey string `json:"sshKey"` + ContainerName string `json:"containerName"` + ContainerPort string `json:"containerPort"` + HostPort string `json:"hostPort"` + EnvFile string `json:"envFile"` + BuildArgs map[string]string `json:"buildArgs"` } // Logger handles logging to both console and file @@ -61,6 +62,7 @@ Options: --container-port Container port (default: 3000) --host-port Host port (default: 3000) --env-file Environment file (default: .env.production) + --build-arg Build arguments (can be specified multiple times, format: KEY=VALUE) --help Show this help message Environment Variables: @@ -74,11 +76,12 @@ Environment Variables: CONTAINER_PORT Container port HOST_PORT Host port ENV_FILE Environment file + BUILD_ARGS Build arguments (comma-separated KEY=VALUE pairs) Examples: copepod --host example.com --user deploy - copepod --host example.com --user deploy --container-name myapp --container-port 8080 - copepod --env-file .env.production + copepod --host example.com --user deploy --build-arg VERSION=1.0.0 --build-arg ENV=prod + copepod --env-file .env.production --build-arg GIT_HASH=$(git rev-parse HEAD) ` // NewLogger creates a new logger instance @@ -120,10 +123,26 @@ func (l *Logger) Close() error { return l.file.Close() } +// arrayFlags allows for multiple flag values +type arrayFlags []string + +func (i *arrayFlags) String() string { + return strings.Join(*i, ",") +} + +func (i *arrayFlags) Set(value string) error { + *i = append(*i, value) + return nil +} + // LoadConfig loads configuration from command line flags and environment variables func LoadConfig() Config { var config Config var showHelp bool + var buildArgs arrayFlags + + // Initialize BuildArgs map + config.BuildArgs = make(map[string]string) // Define command line flags flag.StringVar(&config.Host, "host", getEnv("DEPLOY_HOST", ""), "Remote host to deploy to") @@ -136,6 +155,7 @@ func LoadConfig() Config { flag.StringVar(&config.ContainerPort, "container-port", getEnv("CONTAINER_PORT", "3000"), "Container port") flag.StringVar(&config.HostPort, "host-port", getEnv("HOST_PORT", "3000"), "Host port") flag.StringVar(&config.EnvFile, "env-file", getEnv("ENV_FILE", ".env.production"), "Environment file") + flag.Var(&buildArgs, "build-arg", "Build argument in KEY=VALUE format (can be specified multiple times)") flag.BoolVar(&showHelp, "help", false, "Show help message") // Custom usage message @@ -152,6 +172,24 @@ func LoadConfig() Config { os.Exit(0) } + // Process build arguments from command line + for _, arg := range buildArgs { + parts := strings.SplitN(arg, "=", 2) + if len(parts) == 2 { + config.BuildArgs[parts[0]] = parts[1] + } + } + + // Process build arguments from environment variable + if envBuildArgs := os.Getenv("BUILD_ARGS"); envBuildArgs != "" { + for _, arg := range strings.Split(envBuildArgs, ",") { + parts := strings.SplitN(arg, "=", 2) + if len(parts) == 2 { + config.BuildArgs[parts[0]] = parts[1] + } + } + } + // Expand home directory in SSH key path if strings.HasPrefix(config.SSHKey, "~/") { home, err := os.UserHomeDir() @@ -268,9 +306,16 @@ func Deploy(config *Config, logger *Logger) error { return fmt.Errorf("Dockerfile not found in current directory") } - // Build Docker image - buildCmd := fmt.Sprintf("docker build --platform %s -t %s:%s .", - config.Platform, config.Image, config.Tag) + // Build Docker image with build arguments + buildCmd := fmt.Sprintf("docker build --platform %s", config.Platform) + + // Add build arguments to the command + for key, value := range config.BuildArgs { + buildCmd += fmt.Sprintf(" --build-arg %s=%s", key, value) + } + + buildCmd += fmt.Sprintf(" -t %s:%s .", config.Image, config.Tag) + if _, err := ExecuteCommand(logger, buildCmd, "Building Docker image"); err != nil { return err }