mirror of
https://github.com/shadoll/just-commons.git
synced 2025-12-20 00:25:43 +00:00
Refactor: Enhance container image management with intelligent auto-discovery and multi-architecture support
This commit is contained in:
95
README.md
95
README.md
@@ -178,18 +178,25 @@ just volumes list [pattern] # List volumes (option
|
||||
|
||||
### 🐋 Images Module (`images`)
|
||||
|
||||
**Universal image operations with automatic registry detection**
|
||||
**Universal image operations with intelligent auto-discovery and cross-platform builds**
|
||||
|
||||
```bash
|
||||
just images build <project> [tag] # Build project image
|
||||
just images push <project> [tag] # Push image to registry
|
||||
just images pull <project> [tag] # Pull image from registry
|
||||
just images build [project] [tag] # Build project image (auto-discovers project and Containerfile)
|
||||
just images push [project] [tag] # Push image to registry (auto-discovers project)
|
||||
just images pull [project] [tag] # Pull image from registry (auto-discovers project)
|
||||
just images tag <project> <new-tag> # Tag existing image
|
||||
just images info <project> [tag] # Show image information
|
||||
just images clean <project> # Remove project images (with confirmation)
|
||||
just images build-all # Build all projects with Containerfiles
|
||||
just images info [project] [tag] # Show image information (auto-discovers project)
|
||||
just images clean [project] # Remove project images (auto-discovers project, with confirmation)
|
||||
just images build-all # Build all projects with Containerfiles (uses discovery logic)
|
||||
```
|
||||
|
||||
**Auto-Discovery Features:**
|
||||
- **Project Detection**: Automatically uses directory basename when project not specified
|
||||
- **Containerfile Discovery**: Finds `Containerfile`/`Dockerfile` in current directory or `./docker/` folder
|
||||
- **Cross-Platform Builds**: Handles macOS compatibility for Linux containers automatically
|
||||
- **Smart Parameter Parsing**: `just images push latest` correctly interprets "latest" as tag, not project
|
||||
- **Unified Discovery Logic**: Consistent behavior whether using auto-discovery or explicit project directories
|
||||
|
||||
### 🔐 Registry Module (`registry`)
|
||||
|
||||
**Container registry authentication management**
|
||||
@@ -202,14 +209,24 @@ just registry check # Check authentication
|
||||
|
||||
### 🔧 Core Utilities
|
||||
|
||||
**Shared functions and environment checking**
|
||||
**Shared functions, auto-discovery, and environment checking**
|
||||
|
||||
```bash
|
||||
just env-check # Check container runtime availability
|
||||
just _detect_runtime # Internal: Detect Docker/Podman
|
||||
just _detect_compose # Internal: Detect compose command
|
||||
just _discover_project # Internal: Auto-discover project name from directory
|
||||
just _discover_containerfile_in [dir] # Internal: Discover Containerfile/Dockerfile in directory
|
||||
just _discover_containerfile # Internal: Discover Containerfile/Dockerfile in current directory
|
||||
```
|
||||
|
||||
**Auto-Discovery Functions:**
|
||||
- **`_discover_project`**: Returns `$(basename $(pwd))` - the current directory name as project name
|
||||
- **`_discover_containerfile_in`**: Searches for `Containerfile`/`Dockerfile` in specified directory, also checks `./docker/` subfolder when searching current directory
|
||||
- **`_discover_containerfile`**: Convenience wrapper for current directory discovery
|
||||
|
||||
These functions enable consistent auto-discovery behavior across all modules.
|
||||
|
||||
## 🎨 Modern Just Features
|
||||
|
||||
### Built-in Colors
|
||||
@@ -235,12 +252,47 @@ Destructive operations use `[confirm]` attribute:
|
||||
|
||||
## 🌍 Environment Configuration
|
||||
|
||||
### Required for Registry Operations
|
||||
### Registry Configuration
|
||||
|
||||
**Authentication vs Registry Namespace**
|
||||
|
||||
The system separates authentication credentials from registry image paths, allowing you to authenticate with your personal account while pushing to organization namespaces.
|
||||
|
||||
```bash
|
||||
# .env file
|
||||
GITHUB_USERNAME=your-username
|
||||
GITHUB_TOKEN=ghp_your-token
|
||||
REGISTRY=ghcr.io
|
||||
# .env file - Registry settings
|
||||
GITHUB_USERNAME=your-auth-username # For authentication (just registry login)
|
||||
GITHUB_TOKEN=ghp_your-token # For authentication
|
||||
REGISTRY_NAMESPACE=org-or-username # For image paths (ghcr.io/NAMESPACE/project:tag)
|
||||
REGISTRY=ghcr.io # Registry URL (optional, defaults to ghcr.io)
|
||||
```
|
||||
|
||||
**Configuration Hierarchy:**
|
||||
|
||||
1. **Registry Namespace** (for image paths):
|
||||
- `REGISTRY_NAMESPACE` (if set) - highest priority
|
||||
- `GITHUB_USERNAME` (fallback) - backward compatibility
|
||||
- Error if neither is set
|
||||
|
||||
2. **Registry URL**:
|
||||
- `REGISTRY` (if set) - custom registry
|
||||
- `ghcr.io` (default) - GitHub Container Registry
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Organization setup
|
||||
GITHUB_USERNAME=john-doe # Personal auth
|
||||
REGISTRY_NAMESPACE=my-company # Company namespace
|
||||
# Result: ghcr.io/my-company/project:tag
|
||||
|
||||
# Personal setup
|
||||
GITHUB_USERNAME=john-doe # Personal auth and namespace
|
||||
# Result: ghcr.io/john-doe/project:tag
|
||||
|
||||
# Custom registry
|
||||
REGISTRY=docker.io
|
||||
REGISTRY_NAMESPACE=myorg
|
||||
# Result: docker.io/myorg/project:tag
|
||||
```
|
||||
|
||||
### Optional Overrides
|
||||
@@ -263,17 +315,18 @@ just container start postgres # Start PostgreSQL service
|
||||
just container logs postgres # View logs
|
||||
just postgres check # Test database connection
|
||||
|
||||
# Development operations
|
||||
# Development operations (with auto-discovery)
|
||||
just postgres sql "CREATE DATABASE myapp;" # Create development database
|
||||
just images build myapp # Build application image
|
||||
just images push myapp dev # Push development version
|
||||
just images build # Build application image (auto-discovers project and Containerfile)
|
||||
just images push dev # Push development version (auto-discovers project, "dev" as tag)
|
||||
just images push # Push with auto-generated commit tag
|
||||
```
|
||||
|
||||
### Production Deployment
|
||||
```bash
|
||||
# Pull latest images
|
||||
just images pull myapp latest
|
||||
just images pull postgres 15
|
||||
# Pull latest images (with auto-discovery)
|
||||
just images pull latest # Pull latest version (auto-discovers project)
|
||||
just images pull postgres 15 # Pull specific PostgreSQL version
|
||||
|
||||
# Database operations
|
||||
just postgres restore latest-backup.sql # Restore from backup (with confirmation)
|
||||
@@ -282,6 +335,10 @@ just postgres sql "ANALYZE;" # Optimize database
|
||||
# Volume management
|
||||
just volumes list "production_*" # List production volumes
|
||||
just volumes clean-all compose.prod.yml # Clean up old volumes (with confirmation)
|
||||
|
||||
# Build and deployment
|
||||
just images build-all # Build all projects with Containerfiles (auto-discovery)
|
||||
just images info # Show image information (auto-discovers project)
|
||||
```
|
||||
|
||||
### Multi-Database Setup
|
||||
|
||||
134
core.just
134
core.just
@@ -5,6 +5,127 @@ _discover_project:
|
||||
#!/usr/bin/env bash
|
||||
echo "$(basename $(pwd))"
|
||||
|
||||
# Enhanced project name detection with priority order
|
||||
# Priority: PROJECT_NAME env var > auto-detection > folder name
|
||||
_get_project_name:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 1. Check PROJECT_NAME environment variable (highest priority)
|
||||
if [ -n "${PROJECT_NAME:-}" ]; then
|
||||
echo "$PROJECT_NAME"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2. Auto-detection logic
|
||||
project_name=""
|
||||
|
||||
# Check if we're in root with single Containerfile
|
||||
if [ -f "Containerfile" ] || [ -f "Dockerfile" ] || [ -f "docker/Containerfile" ] || [ -f "docker/Dockerfile" ]; then
|
||||
# Check for multiple projects (subdirectories with Containerfiles)
|
||||
subproject_count=0
|
||||
for dir in */; do
|
||||
if [ -d "$dir" ] && ([ -f "${dir}Containerfile" ] || [ -f "${dir}Dockerfile" ]); then
|
||||
subproject_count=$((subproject_count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $subproject_count -eq 0 ]; then
|
||||
# Single project in root
|
||||
project_name="$(basename $(pwd))"
|
||||
else
|
||||
# Multiple subprojects detected - this should be handled per-project
|
||||
project_name="$(basename $(pwd))"
|
||||
fi
|
||||
else
|
||||
# No Containerfile in root, use current directory name
|
||||
project_name="$(basename $(pwd))"
|
||||
fi
|
||||
|
||||
# 3. Fallback to current folder basename
|
||||
if [ -z "$project_name" ]; then
|
||||
project_name="$(basename $(pwd))"
|
||||
fi
|
||||
|
||||
echo "$project_name"
|
||||
|
||||
# Get full image name with priority logic
|
||||
# Priority: IMAGE_NAME env var > built from components
|
||||
_get_image_name tag="":
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
tag="{{tag}}"
|
||||
|
||||
# 1. Check IMAGE_NAME environment variable (highest priority)
|
||||
if [ -n "${IMAGE_NAME:-}" ]; then
|
||||
if [ -n "$tag" ]; then
|
||||
echo "${IMAGE_NAME}:$tag"
|
||||
else
|
||||
echo "$IMAGE_NAME"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2. Build from components
|
||||
registry="${REGISTRY:-ghcr.io}"
|
||||
namespace="${REGISTRY_NAMESPACE:-${GITHUB_USERNAME:-}}"
|
||||
project_name=$(just _get_project_name)
|
||||
|
||||
if [ -z "$namespace" ]; then
|
||||
echo "Error: REGISTRY_NAMESPACE or GITHUB_USERNAME must be set" >&2
|
||||
echo "Set REGISTRY_NAMESPACE in .env file for registry operations" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$tag" ]; then
|
||||
echo "$registry/$namespace/$project_name:$tag"
|
||||
else
|
||||
echo "$registry/$namespace/$project_name"
|
||||
fi
|
||||
|
||||
# Generate default tag based on git or timestamp
|
||||
_generate_default_tag:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
echo "commit-$(git rev-parse --short HEAD)"
|
||||
else
|
||||
echo "build-$(date '+%Y%m%d-%H%M%S')"
|
||||
fi
|
||||
|
||||
# Detect platforms from Containerfile
|
||||
_detect_platforms containerfile="":
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
containerfile="{{containerfile}}"
|
||||
default_platforms="${DEFAULT_PLATFORMS:-linux/amd64,linux/arm64}"
|
||||
|
||||
if [ -z "$containerfile" ]; then
|
||||
echo "$default_platforms"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -f "$containerfile" ]; then
|
||||
echo "$default_platforms"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for --platform specification in FROM statements
|
||||
if grep -q "FROM.*--platform" "$containerfile"; then
|
||||
# Extract platform from first FROM statement with --platform
|
||||
platform=$(grep "FROM.*--platform" "$containerfile" | head -1 | sed -n 's/.*--platform=\([^ ]*\).*/\1/p')
|
||||
if [ -n "$platform" ]; then
|
||||
echo "$platform"
|
||||
else
|
||||
echo "$default_platforms"
|
||||
fi
|
||||
else
|
||||
echo "$default_platforms"
|
||||
fi
|
||||
|
||||
# Auto-discover containerfile and return path and project info
|
||||
# Usage: _discover_containerfile [directory]
|
||||
# Returns: containerfile_path project_name build_context
|
||||
@@ -49,6 +170,19 @@ _discover_containerfile:
|
||||
# Detect container runtime (Docker or Podman)
|
||||
_detect_runtime:
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Check if DOCKER environment variable is set (highest priority)
|
||||
if [ -n "${DOCKER:-}" ]; then
|
||||
if command -v "$DOCKER" >/dev/null 2>&1; then
|
||||
echo "$DOCKER"
|
||||
exit 0
|
||||
else
|
||||
echo "Error: Specified runtime '$DOCKER' not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Auto-detect available runtime
|
||||
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
|
||||
echo "docker"
|
||||
elif command -v podman >/dev/null 2>&1; then
|
||||
|
||||
301
images/README.md
Normal file
301
images/README.md
Normal file
@@ -0,0 +1,301 @@
|
||||
# Container Images Module
|
||||
|
||||
The images module provides comprehensive container image management capabilities including building, pushing, pulling, tagging, and cleaning images with support for multi-architecture builds and flexible naming conventions.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Build image with default settings
|
||||
just images build
|
||||
|
||||
# Push with auto-generated commit tag
|
||||
just images push
|
||||
|
||||
# Push latest (tags and pushes all architectures as latest)
|
||||
just images push latest
|
||||
|
||||
# Show image information
|
||||
just images info
|
||||
|
||||
# List all project images
|
||||
just images list
|
||||
|
||||
# Clean project images
|
||||
just images clean
|
||||
```
|
||||
|
||||
## Image Naming Logic
|
||||
|
||||
The module uses a flexible naming system with clear priority order:
|
||||
|
||||
### 1. Full Image Name Override (Highest Priority)
|
||||
```bash
|
||||
# .env
|
||||
IMAGE_NAME=ghcr.io/shkafnik/nikamebel-info
|
||||
```
|
||||
|
||||
When `IMAGE_NAME` is set, it's used exactly as specified for all operations.
|
||||
|
||||
### 2. Component-Based Naming (Default)
|
||||
```bash
|
||||
# .env
|
||||
REGISTRY=ghcr.io
|
||||
REGISTRY_NAMESPACE=shkafnik
|
||||
PROJECT_NAME=nikamebel-info
|
||||
```
|
||||
|
||||
Image name is built as: `{REGISTRY}/{REGISTRY_NAMESPACE}/{PROJECT_NAME}:{TAG}`
|
||||
|
||||
### 3. Project Name Detection Priority
|
||||
1. **`PROJECT_NAME`** from `.env` (highest priority)
|
||||
2. **Auto-detection logic**:
|
||||
- If single Containerfile in root → use root folder name
|
||||
- If multiple Containerfiles in subfolders → use subfolder name
|
||||
- If `docker/` subfolder detected → use root folder name
|
||||
3. **Current folder basename** (fallback)
|
||||
|
||||
## Multi-Architecture Support
|
||||
|
||||
### Default Behavior
|
||||
- **Always builds**: `linux/amd64` and `linux/arm64`
|
||||
- **Automatic detection**: Checks Containerfile for platform specifications
|
||||
- **Platform override**: If `--platform` found in Containerfile, builds only that platform
|
||||
|
||||
### Environment Configuration
|
||||
```bash
|
||||
# .env - Override default platforms
|
||||
DEFAULT_PLATFORMS=linux/arm64,linux/amd64
|
||||
|
||||
# Single platform builds
|
||||
DEFAULT_PLATFORMS=linux/amd64
|
||||
```
|
||||
|
||||
### Platform Detection Examples
|
||||
```dockerfile
|
||||
# This will build only for linux/amd64
|
||||
FROM --platform=linux/amd64 alpine:latest
|
||||
|
||||
# This will build for default platforms (arm64 + amd64)
|
||||
FROM alpine:latest
|
||||
```
|
||||
|
||||
## Tag Management
|
||||
|
||||
### Default Tagging
|
||||
- **Regular builds**: `commit-{short-git-hash}` (e.g., `commit-a1b2c3d`)
|
||||
- **Non-git repos**: `build-{timestamp}` (e.g., `build-20240315-143022`)
|
||||
|
||||
### Manual Tags
|
||||
```bash
|
||||
# Build with custom tag
|
||||
just images build "" "v1.2.3"
|
||||
|
||||
# Push specific tag
|
||||
just images push "" "v1.2.3"
|
||||
```
|
||||
|
||||
### Latest Tag Handling
|
||||
```bash
|
||||
# Special latest command - finds all existing tags and:
|
||||
# 1. Tags them as 'latest'
|
||||
# 2. Pushes all 'latest' images (all architectures)
|
||||
just images push latest
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Registry Configuration
|
||||
```bash
|
||||
# Registry settings
|
||||
REGISTRY=ghcr.io # Default: ghcr.io
|
||||
REGISTRY_NAMESPACE=shkafnik # Required for registry operations
|
||||
GITHUB_USERNAME=shkafnik # Fallback for REGISTRY_NAMESPACE
|
||||
GITHUB_TOKEN=ghp_xxxx # Required for private registries
|
||||
|
||||
# Full override (highest priority)
|
||||
IMAGE_NAME=ghcr.io/custom/path # Overrides all other naming logic
|
||||
```
|
||||
|
||||
### Project Configuration
|
||||
```bash
|
||||
# Project identification
|
||||
PROJECT_NAME=nikamebel-info # Override auto-detection
|
||||
|
||||
# Build configuration
|
||||
DEFAULT_PLATFORMS=linux/arm64,linux/amd64 # Default platforms to build
|
||||
```
|
||||
|
||||
### Runtime Configuration
|
||||
```bash
|
||||
# Container runtime
|
||||
DOCKER=podman # Default: docker
|
||||
DOCKER_COMPOSE=podman-compose # Default: docker compose
|
||||
```
|
||||
|
||||
## Commands Reference
|
||||
|
||||
### Build Commands
|
||||
```bash
|
||||
# Auto-discover and build with commit tag
|
||||
just images build
|
||||
|
||||
# Build specific project
|
||||
just images build myproject
|
||||
|
||||
# Build with custom tag
|
||||
just images build "" "v1.2.3"
|
||||
|
||||
# Build specific project with tag
|
||||
just images build myproject "v1.2.3"
|
||||
```
|
||||
|
||||
### Push Commands
|
||||
```bash
|
||||
# Push with auto-generated tag
|
||||
just images push
|
||||
|
||||
# Push specific tag
|
||||
just images push "" "v1.2.3"
|
||||
|
||||
# Push latest (special handling)
|
||||
just images push latest
|
||||
|
||||
# Push specific project
|
||||
just images push myproject
|
||||
```
|
||||
|
||||
### Pull Commands
|
||||
```bash
|
||||
# Pull latest tag
|
||||
just images pull
|
||||
|
||||
# Pull specific tag
|
||||
just images pull "" "v1.2.3"
|
||||
|
||||
# Pull specific project
|
||||
just images pull myproject "v1.2.3"
|
||||
```
|
||||
|
||||
### Management Commands
|
||||
```bash
|
||||
# Tag existing image
|
||||
just images tag myproject "new-tag"
|
||||
|
||||
# Show image information
|
||||
just images info
|
||||
|
||||
# List all project images
|
||||
just images list
|
||||
|
||||
# Clean project images (with confirmation)
|
||||
just images clean
|
||||
|
||||
# Build all projects with Containerfiles
|
||||
just images build-all
|
||||
```
|
||||
|
||||
## Architecture Detection
|
||||
|
||||
The module automatically detects the build context and Containerfile location:
|
||||
|
||||
1. **Current directory**: `./Containerfile` or `./Dockerfile`
|
||||
2. **Docker subdirectory**: `./docker/Containerfile` or `./docker/Dockerfile`
|
||||
3. **Project subdirectories**: `{project}/Containerfile` or `{project}/Dockerfile`
|
||||
|
||||
## Multi-Platform Build Process
|
||||
|
||||
### For Multi-Platform Images
|
||||
```bash
|
||||
# Creates manifest with multiple architectures
|
||||
docker buildx build --platform linux/amd64,linux/arm64 \
|
||||
-t ghcr.io/shkafnik/nikamebel-info:commit-abc123 \
|
||||
--push .
|
||||
```
|
||||
|
||||
### For Single Platform Images
|
||||
```bash
|
||||
# Regular build for detected platform
|
||||
docker build -t ghcr.io/shkafnik/nikamebel-info:commit-abc123 .
|
||||
```
|
||||
|
||||
## Latest Tag Workflow
|
||||
|
||||
When you run `just images push latest`:
|
||||
|
||||
1. **Discovery**: Auto-discover project name
|
||||
2. **Search**: Find all existing images for the project
|
||||
3. **Tag**: Tag all found images with `latest`
|
||||
4. **Push**: Push all `latest` tagged images (all architectures)
|
||||
|
||||
This ensures that `latest` always points to your most recent build across all supported architectures.
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Issues and Solutions
|
||||
|
||||
**Missing registry credentials:**
|
||||
```bash
|
||||
# Set in .env
|
||||
REGISTRY_NAMESPACE=your-namespace
|
||||
GITHUB_TOKEN=your-token
|
||||
```
|
||||
|
||||
**Platform build failures:**
|
||||
```bash
|
||||
# Check buildx setup
|
||||
docker buildx create --use
|
||||
docker buildx inspect --bootstrap
|
||||
```
|
||||
|
||||
**Image not found:**
|
||||
```bash
|
||||
# List available images
|
||||
just images list
|
||||
|
||||
# Check project name detection
|
||||
just images info
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Workflow
|
||||
```bash
|
||||
# Build and push commit-tagged image
|
||||
just images build
|
||||
just images push
|
||||
|
||||
# Later, mark as latest and push
|
||||
just images push latest
|
||||
```
|
||||
|
||||
### Custom Project Setup
|
||||
```bash
|
||||
# .env configuration
|
||||
PROJECT_NAME=my-custom-name
|
||||
REGISTRY_NAMESPACE=myorg
|
||||
DEFAULT_PLATFORMS=linux/amd64
|
||||
|
||||
# Build and push
|
||||
just images build
|
||||
just images push
|
||||
```
|
||||
|
||||
### Multi-Project Repository
|
||||
```bash
|
||||
# Build specific project
|
||||
just images build frontend
|
||||
just images build backend
|
||||
|
||||
# Build all projects
|
||||
just images build-all
|
||||
```
|
||||
|
||||
## Integration with just-commons
|
||||
|
||||
This module integrates seamlessly with other just-commons modules:
|
||||
|
||||
- **Registry module**: For authentication (`just registry login`)
|
||||
- **Container module**: For runtime management
|
||||
- **Core utilities**: For project discovery and runtime detection
|
||||
|
||||
For complete project setup, see the main project documentation.
|
||||
322
images/mod.just
322
images/mod.just
@@ -1,6 +1,7 @@
|
||||
# Universal container image operations
|
||||
# Container images module - comprehensive image management
|
||||
# Supports multi-architecture builds, flexible naming, and registry operations
|
||||
|
||||
# Build project image (auto-discovers Dockerfile if project not specified)
|
||||
# Build project image with multi-architecture support
|
||||
[no-cd]
|
||||
build project="" tag="":
|
||||
#!/usr/bin/env bash
|
||||
@@ -9,10 +10,12 @@ build project="" tag="":
|
||||
project="{{project}}"
|
||||
tag="{{tag}}"
|
||||
|
||||
# Determine discovery directory
|
||||
# Determine discovery directory and project
|
||||
if [ -z "$project" ]; then
|
||||
discovery_dir="."
|
||||
echo -e "{{BLUE}}🔍 Auto-discovering Dockerfile/Containerfile...{{NORMAL}}"
|
||||
echo -e "{{BLUE}}🔍 Auto-discovering project...{{NORMAL}}"
|
||||
project=$(just _get_project_name)
|
||||
echo -e "{{GREEN}}✓ Using project: $project{{NORMAL}}"
|
||||
else
|
||||
# Check if project directory exists
|
||||
if [ ! -d "$project" ]; then
|
||||
@@ -20,63 +23,90 @@ build project="" tag="":
|
||||
exit 1
|
||||
fi
|
||||
discovery_dir="$project"
|
||||
echo -e "{{BLUE}}🔍 Auto-discovering Dockerfile/Containerfile in $project...{{NORMAL}}"
|
||||
echo -e "{{BLUE}}🔍 Building project: $project{{NORMAL}}"
|
||||
fi
|
||||
|
||||
# Use unified discovery logic
|
||||
discovery_result=$(just _discover_containerfile_in "$discovery_dir")
|
||||
read -r containerfile discovered_project build_context <<< "$discovery_result"
|
||||
|
||||
# Use discovered project name if not explicitly set
|
||||
if [ -z "$project" ]; then
|
||||
project="$discovered_project"
|
||||
fi
|
||||
|
||||
echo -e "{{GREEN}}✓ Found $containerfile{{NORMAL}}"
|
||||
|
||||
# Generate tag if not provided
|
||||
if [ -z "$tag" ]; then
|
||||
if git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
tag="commit-$(git rev-parse --short HEAD)"
|
||||
else
|
||||
tag="build-$(date '+%Y%m%d-%H%M%S')"
|
||||
fi
|
||||
tag=$(just _generate_default_tag)
|
||||
fi
|
||||
|
||||
runtime=$(just _detect_runtime)
|
||||
registry="${REGISTRY:-ghcr.io}"
|
||||
username="${GITHUB_USERNAME:-}"
|
||||
# Get image name using new naming logic
|
||||
image_name=$(just _get_image_name "$tag")
|
||||
|
||||
if [ -n "$username" ]; then
|
||||
image_name="$registry/$username/$project:$tag"
|
||||
else
|
||||
image_name="$project:$tag"
|
||||
fi
|
||||
# Detect platforms
|
||||
platforms=$(just _detect_platforms "$containerfile")
|
||||
|
||||
echo -e "{{BLUE}}Building image: $image_name{{NORMAL}}"
|
||||
echo -e "{{YELLOW}}Using: $containerfile{{NORMAL}}"
|
||||
echo -e "{{YELLOW}}Build context: $build_context{{NORMAL}}"
|
||||
echo -e "{{YELLOW}}Platforms: $platforms{{NORMAL}}"
|
||||
|
||||
# Handle cross-platform builds with macOS compatibility workaround
|
||||
if grep -q "FROM.*--platform" "$containerfile"; then
|
||||
echo -e "{{YELLOW}}Cross-platform build detected, using macOS compatibility workaround{{NORMAL}}"
|
||||
runtime=$(just _detect_runtime)
|
||||
|
||||
# Create temporary Containerfile without platform specification for local build
|
||||
temp_containerfile="/tmp/Containerfile.local.$$"
|
||||
sed 's/FROM --platform=[^ ]* /FROM /' "$containerfile" > "$temp_containerfile"
|
||||
# Check if multi-platform build is needed
|
||||
if [[ "$platforms" == *","* ]]; then
|
||||
echo -e "{{BLUE}}Multi-platform build detected{{NORMAL}}"
|
||||
|
||||
echo -e "{{BLUE}}Building without platform emulation (will be multi-arch compatible){{NORMAL}}"
|
||||
$runtime build -f "$temp_containerfile" -t "$image_name" --load "$build_context"
|
||||
# Use buildx for multi-platform
|
||||
if ! $runtime buildx version >/dev/null 2>&1; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} buildx required for multi-platform builds" >&2
|
||||
echo "{{YELLOW}}Run: $runtime buildx create --use{{NORMAL}}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up temp file
|
||||
rm -f "$temp_containerfile"
|
||||
# Ensure buildx builder exists and is active
|
||||
if ! $runtime buildx inspect >/dev/null 2>&1; then
|
||||
echo -e "{{BLUE}}Setting up buildx builder...{{NORMAL}}"
|
||||
$runtime buildx create --use --name just-commons-builder >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# For multi-platform builds, we can't load locally, so build for local platform only
|
||||
local_platform="linux/$(uname -m)"
|
||||
if [ "$(uname -m)" = "x86_64" ]; then
|
||||
local_platform="linux/amd64"
|
||||
elif [ "$(uname -m)" = "arm64" ] || [ "$(uname -m)" = "aarch64" ]; then
|
||||
local_platform="linux/arm64"
|
||||
fi
|
||||
|
||||
echo -e "{{YELLOW}}Multi-platform builds cannot be loaded locally{{NORMAL}}"
|
||||
echo -e "{{BLUE}}Building for local platform: $local_platform{{NORMAL}}"
|
||||
|
||||
$runtime buildx build \
|
||||
--platform "$local_platform" \
|
||||
-f "$containerfile" \
|
||||
-t "$image_name" \
|
||||
--load \
|
||||
"$build_context"
|
||||
else
|
||||
$runtime build -f "$containerfile" -t "$image_name" --load "$build_context"
|
||||
echo -e "{{BLUE}}Single platform build{{NORMAL}}"
|
||||
|
||||
# Handle single platform builds with potential platform specification
|
||||
if grep -q "FROM.*--platform" "$containerfile"; then
|
||||
echo -e "{{YELLOW}}Platform specified in Containerfile, using compatibility mode{{NORMAL}}"
|
||||
|
||||
# Create temporary Containerfile without platform specification for local build
|
||||
temp_containerfile="/tmp/Containerfile.local.$$"
|
||||
sed 's/FROM --platform=[^ ]* /FROM /' "$containerfile" > "$temp_containerfile"
|
||||
|
||||
$runtime build -f "$temp_containerfile" -t "$image_name" "$build_context"
|
||||
|
||||
# Clean up temp file
|
||||
rm -f "$temp_containerfile"
|
||||
else
|
||||
$runtime build -f "$containerfile" -t "$image_name" "$build_context"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "{{GREEN}}✓ Successfully built: $image_name{{NORMAL}}"
|
||||
|
||||
# Push project image to registry
|
||||
# Push project image to registry with smart latest handling
|
||||
[no-cd]
|
||||
push project="" tag="":
|
||||
#!/usr/bin/env bash
|
||||
@@ -85,39 +115,85 @@ push project="" tag="":
|
||||
project="{{project}}"
|
||||
tag="{{tag}}"
|
||||
|
||||
# Handle case where user provides tag without project (e.g., "just images push latest")
|
||||
if [ -n "$project" ] && [ -z "$tag" ] && [[ "$project" =~ ^(latest|commit-.*|[0-9]+\.[0-9]+.*|v[0-9]+.*)$ ]]; then
|
||||
# First parameter looks like a tag, treat it as such
|
||||
tag="$project"
|
||||
project=""
|
||||
fi
|
||||
|
||||
# Auto-discover project if not specified
|
||||
if [ -z "$project" ]; then
|
||||
echo -e "{{BLUE}}🔍 Auto-discovering project...{{NORMAL}}"
|
||||
project=$(just _discover_project)
|
||||
project=$(just _get_project_name)
|
||||
echo -e "{{GREEN}}✓ Using project: $project{{NORMAL}}"
|
||||
fi
|
||||
|
||||
# Generate tag if not provided
|
||||
if [ -z "$tag" ]; then
|
||||
if git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
tag="commit-$(git rev-parse --short HEAD)"
|
||||
else
|
||||
tag="latest"
|
||||
fi
|
||||
tag=$(just _generate_default_tag)
|
||||
fi
|
||||
|
||||
runtime=$(just _detect_runtime)
|
||||
registry="${REGISTRY:-ghcr.io}"
|
||||
username="${GITHUB_USERNAME:-}"
|
||||
|
||||
if [ -z "$username" ]; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} GITHUB_USERNAME not set" >&2
|
||||
echo "{{YELLOW}}Set GITHUB_USERNAME in .env file{{NORMAL}}" >&2
|
||||
exit 1
|
||||
# Special handling for latest tag
|
||||
if [ "$tag" = "latest" ]; then
|
||||
echo -e "{{BLUE}}🔍 Latest tag requested - finding existing images to tag...{{NORMAL}}"
|
||||
|
||||
# Get base image name without tag
|
||||
image_base=$(just _get_image_name "")
|
||||
|
||||
# Find only commit-tagged images for this project (most recent build)
|
||||
commit_images=$($runtime images | grep "^$image_base" | grep " commit-" | awk '{print $1":"$2}' || true)
|
||||
|
||||
if [ -z "$commit_images" ]; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} No commit-tagged images found to tag as latest" >&2
|
||||
echo "{{YELLOW}}Build an image first with: just images build{{NORMAL}}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the most recent commit image (first in the list since images are sorted by creation time)
|
||||
latest_commit_image=$(echo "$commit_images" | head -1)
|
||||
|
||||
echo -e "{{BLUE}}Found latest commit image: $latest_commit_image{{NORMAL}}"
|
||||
|
||||
# Tag only the latest commit image as latest
|
||||
latest_image_name=$(just _get_image_name "latest")
|
||||
|
||||
# First push the commit-tagged image to registry
|
||||
echo -e "{{BLUE}}Pushing commit image: $latest_commit_image{{NORMAL}}"
|
||||
$runtime push "$latest_commit_image"
|
||||
|
||||
echo -e "{{BLUE}}📋 Tagging $latest_commit_image as latest{{NORMAL}}"
|
||||
$runtime tag "$latest_commit_image" "$latest_image_name"
|
||||
|
||||
echo -e "{{GREEN}}✓ Tagged latest commit image as latest{{NORMAL}}"
|
||||
|
||||
# Push latest tag
|
||||
echo -e "{{BLUE}}Pushing latest tag: $latest_image_name{{NORMAL}}"
|
||||
$runtime push "$latest_image_name"
|
||||
|
||||
echo -e "{{GREEN}}✓ Successfully pushed both commit and latest tags{{NORMAL}}"
|
||||
else
|
||||
# Regular tag push
|
||||
image_name=$(just _get_image_name "$tag")
|
||||
|
||||
# Check if the image exists locally
|
||||
if ! $runtime images "$image_name" >/dev/null 2>&1; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} Image $image_name not found locally" >&2
|
||||
echo "{{YELLOW}}Build the image first with: just images build{{NORMAL}}" >&2
|
||||
echo "{{YELLOW}}Available images:{{NORMAL}}" >&2
|
||||
image_base=$(just _get_image_name "")
|
||||
$runtime images | grep "$image_base" || echo "No images found for project: $project" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "{{BLUE}}Pushing image: $image_name{{NORMAL}}"
|
||||
$runtime push "$image_name"
|
||||
|
||||
echo -e "{{GREEN}}✓ Successfully pushed: $image_name{{NORMAL}}"
|
||||
fi
|
||||
|
||||
image_name="$registry/$username/$project:$tag"
|
||||
|
||||
echo -e "{{BLUE}}Pushing image: $image_name{{NORMAL}}"
|
||||
$runtime push "$image_name"
|
||||
|
||||
echo -e "{{GREEN}}✓ Successfully pushed: $image_name{{NORMAL}}"
|
||||
|
||||
# Pull project image from registry
|
||||
[no-cd]
|
||||
pull project="" tag="latest":
|
||||
@@ -130,64 +206,49 @@ pull project="" tag="latest":
|
||||
# Auto-discover project if not specified
|
||||
if [ -z "$project" ]; then
|
||||
echo -e "{{BLUE}}🔍 Auto-discovering project...{{NORMAL}}"
|
||||
project=$(just _discover_project)
|
||||
project=$(just _get_project_name)
|
||||
echo -e "{{GREEN}}✓ Using project: $project{{NORMAL}}"
|
||||
fi
|
||||
|
||||
image_name=$(just _get_image_name "$tag")
|
||||
runtime=$(just _detect_runtime)
|
||||
registry="${REGISTRY:-ghcr.io}"
|
||||
username="${GITHUB_USERNAME:-}"
|
||||
|
||||
if [ -z "$username" ]; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} GITHUB_USERNAME not set" >&2
|
||||
echo "{{YELLOW}}Set GITHUB_USERNAME in .env file{{NORMAL}}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
image_name="$registry/$username/$project:$tag"
|
||||
|
||||
echo -e "{{BLUE}}Pulling image: $image_name{{NORMAL}}"
|
||||
$runtime pull "$image_name"
|
||||
|
||||
echo -e "{{GREEN}}✓ Successfully pulled: $image_name{{NORMAL}}"
|
||||
|
||||
# Tag existing image
|
||||
# Tag existing image with new tag
|
||||
[no-cd]
|
||||
tag project new_tag:
|
||||
tag source_tag new_tag:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
project="{{project}}"
|
||||
source_tag="{{source_tag}}"
|
||||
new_tag="{{new_tag}}"
|
||||
|
||||
if [ -z "$project" ] || [ -z "$new_tag" ]; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} Project name and new tag are required" >&2
|
||||
echo "{{YELLOW}}Usage:{{NORMAL}} just images tag myproject newtag" >&2
|
||||
if [ -z "$source_tag" ] || [ -z "$new_tag" ]; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} Both source and new tags are required" >&2
|
||||
echo "{{YELLOW}}Usage:{{NORMAL}} just images tag commit-abc123 v1.2.3" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
runtime=$(just _detect_runtime)
|
||||
registry="${REGISTRY:-ghcr.io}"
|
||||
username="${GITHUB_USERNAME:-}"
|
||||
|
||||
if [ -n "$username" ]; then
|
||||
old_image="$registry/$username/$project"
|
||||
new_image="$registry/$username/$project:$new_tag"
|
||||
else
|
||||
old_image="$project"
|
||||
new_image="$project:$new_tag"
|
||||
fi
|
||||
source_image=$(just _get_image_name "$source_tag")
|
||||
new_image=$(just _get_image_name "$new_tag")
|
||||
|
||||
# Find the most recent tag for the project
|
||||
latest_image=$($runtime images | grep "^$old_image" | awk '{print $1":"$2}' | head -1)
|
||||
|
||||
if [ -z "$latest_image" ]; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} No images found for project: $project" >&2
|
||||
# Check if source image exists
|
||||
if ! $runtime images "$source_image" >/dev/null 2>&1; then
|
||||
echo "{{BOLD}}{{RED}}Error:{{NORMAL}} Source image not found: $source_image" >&2
|
||||
echo "{{YELLOW}}Available images:{{NORMAL}}" >&2
|
||||
image_base=$(just _get_image_name "")
|
||||
$runtime images | grep "$image_base" || echo "No images found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "{{BLUE}}Tagging image: $latest_image → $new_image{{NORMAL}}"
|
||||
$runtime tag "$latest_image" "$new_image"
|
||||
echo -e "{{BLUE}}Tagging image: $source_image → $new_image{{NORMAL}}"
|
||||
$runtime tag "$source_image" "$new_image"
|
||||
|
||||
echo -e "{{GREEN}}✓ Successfully tagged: $new_image{{NORMAL}}"
|
||||
|
||||
@@ -203,37 +264,73 @@ info project="" tag="":
|
||||
# Auto-discover project if not specified
|
||||
if [ -z "$project" ]; then
|
||||
echo -e "{{BLUE}}🔍 Auto-discovering project...{{NORMAL}}"
|
||||
project=$(just _discover_project)
|
||||
project=$(just _get_project_name)
|
||||
echo -e "{{GREEN}}✓ Using project: $project{{NORMAL}}"
|
||||
fi
|
||||
|
||||
runtime=$(just _detect_runtime)
|
||||
registry="${REGISTRY:-ghcr.io}"
|
||||
username="${GITHUB_USERNAME:-}"
|
||||
|
||||
if [ -n "$username" ]; then
|
||||
image_base="$registry/$username/$project"
|
||||
else
|
||||
image_base="$project"
|
||||
fi
|
||||
|
||||
if [ -n "$tag" ]; then
|
||||
image_name="$image_base:$tag"
|
||||
image_name=$(just _get_image_name "$tag")
|
||||
else
|
||||
image_name="$image_base"
|
||||
image_name=$(just _get_image_name "")
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "{{BLUE}}Image information for: $image_name{{NORMAL}}"
|
||||
echo ""
|
||||
echo -e "{{BLUE}}Configuration:{{NORMAL}}"
|
||||
echo -e "{{YELLOW}}Project:{{NORMAL}} $project"
|
||||
echo -e "{{YELLOW}}Registry:{{NORMAL}} ${REGISTRY:-ghcr.io}"
|
||||
echo -e "{{YELLOW}}Namespace:{{NORMAL}} ${REGISTRY_NAMESPACE:-${GITHUB_USERNAME:-'Not set'}}"
|
||||
echo -e "{{YELLOW}}Image Name Override:{{NORMAL}} ${IMAGE_NAME:-'Not set'}"
|
||||
echo -e "{{YELLOW}}Default Platforms:{{NORMAL}} ${DEFAULT_PLATFORMS:-linux/amd64,linux/arm64}"
|
||||
echo ""
|
||||
|
||||
# Show image details
|
||||
$runtime images "$image_name"
|
||||
if [ -n "$tag" ]; then
|
||||
echo -e "{{BLUE}}Local image details:{{NORMAL}}"
|
||||
$runtime images "$image_name" 2>/dev/null || echo "Image not found locally: $image_name"
|
||||
else
|
||||
echo -e "{{BLUE}}All project images:{{NORMAL}}"
|
||||
$runtime images | grep "$image_name" 2>/dev/null || echo "No images found for project: $project"
|
||||
fi
|
||||
|
||||
# List all project images
|
||||
[no-cd]
|
||||
list project="":
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
project="{{project}}"
|
||||
|
||||
# Auto-discover project if not specified
|
||||
if [ -z "$project" ]; then
|
||||
echo -e "{{BLUE}}🔍 Auto-discovering project...{{NORMAL}}"
|
||||
project=$(just _get_project_name)
|
||||
echo -e "{{GREEN}}✓ Using project: $project{{NORMAL}}"
|
||||
fi
|
||||
|
||||
runtime=$(just _detect_runtime)
|
||||
image_base=$(just _get_image_name "")
|
||||
|
||||
echo ""
|
||||
echo -e "{{BLUE}}Image history:{{NORMAL}}"
|
||||
$runtime history "$image_name" 2>/dev/null || echo "Image not found locally"
|
||||
echo -e "{{BLUE}}Images for project: $project{{NORMAL}}"
|
||||
echo -e "{{BLUE}}Base name: $image_base{{NORMAL}}"
|
||||
echo ""
|
||||
|
||||
# Remove project images
|
||||
# List all images for this project
|
||||
images=$($runtime images | grep "$image_base" || true)
|
||||
|
||||
if [ -z "$images" ]; then
|
||||
echo -e "{{YELLOW}}No images found for project: $project{{NORMAL}}"
|
||||
echo -e "{{YELLOW}}Build an image with: just images build{{NORMAL}}"
|
||||
else
|
||||
echo "REPOSITORY TAG SIZE CREATED"
|
||||
echo "$images"
|
||||
fi
|
||||
|
||||
# Remove project images with confirmation
|
||||
[confirm]
|
||||
[no-cd]
|
||||
clean project="":
|
||||
@@ -245,24 +342,17 @@ clean project="":
|
||||
# Auto-discover project if not specified
|
||||
if [ -z "$project" ]; then
|
||||
echo -e "{{BLUE}}🔍 Auto-discovering project...{{NORMAL}}"
|
||||
project=$(just _discover_project)
|
||||
project=$(just _get_project_name)
|
||||
echo -e "{{GREEN}}✓ Using project: $project{{NORMAL}}"
|
||||
fi
|
||||
|
||||
runtime=$(just _detect_runtime)
|
||||
registry="${REGISTRY:-ghcr.io}"
|
||||
username="${GITHUB_USERNAME:-}"
|
||||
|
||||
if [ -n "$username" ]; then
|
||||
image_pattern="$registry/$username/$project"
|
||||
else
|
||||
image_pattern="$project"
|
||||
fi
|
||||
image_base=$(just _get_image_name "")
|
||||
|
||||
echo -e "{{YELLOW}}Removing all images for project: $project{{NORMAL}}"
|
||||
|
||||
# Find and remove all images for the project
|
||||
images=$($runtime images --format "{{{{.Repository}}}}:{{{{.Tag}}}}" | grep "^$image_pattern" || true)
|
||||
images=$($runtime images | grep "^$image_base" | awk '{print $1":"$2}' || true)
|
||||
|
||||
if [ -z "$images" ]; then
|
||||
echo -e "{{YELLOW}}No images found for project: $project{{NORMAL}}"
|
||||
@@ -282,7 +372,7 @@ clean project="":
|
||||
|
||||
echo -e "{{GREEN}}✓ Project images cleaned{{NORMAL}}"
|
||||
|
||||
# Build all known projects
|
||||
# Build all projects with Containerfiles
|
||||
[no-cd]
|
||||
build-all:
|
||||
#!/usr/bin/env bash
|
||||
@@ -292,11 +382,11 @@ build-all:
|
||||
|
||||
# Find all directories with Containerfile or Dockerfile using discovery logic
|
||||
projects=""
|
||||
has_current_project=false
|
||||
|
||||
# Check current directory first (handles docker/ subfolder case)
|
||||
has_current_project=false
|
||||
if just _discover_containerfile_in "." >/dev/null 2>&1; then
|
||||
current_project=$(just _discover_project)
|
||||
current_project=$(just _get_project_name)
|
||||
has_current_project=true
|
||||
echo -e "{{GREEN}}✓ Found project in current directory: $current_project{{NORMAL}}"
|
||||
fi
|
||||
@@ -341,4 +431,4 @@ build-all:
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "{{GREEN}}✓ All projects built successfully{{NORMAL}}"
|
||||
echo -e "{{GREEN}}✓ All projects built successfully{{NORMAL}}"
|
||||
Reference in New Issue
Block a user