Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Build artifacts
target/

# Version control
.git/
.gitignore

# Documentation (not needed for build)
*.md

# CI/CD
.github/

# IDE
.idea/
.vscode/
*.swp
*.swo

# Misc
.DS_Store
142 changes: 142 additions & 0 deletions .github/workflows/container.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: container

on:
pull_request:
workflow_run:
workflows: [Rust]
types: [completed]
branches: [main]
push:
tags: ['v*']

permissions:
contents: read
packages: write

concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.head_ref || github.ref }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build:
if: |
github.event_name == 'pull_request' ||
github.event_name == 'push' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
platform: linux/amd64
arch: amd64
- runner: ubuntu-24.04-arm
platform: linux/arm64
arch: arm64
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha || '' }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: >-
type=image,
name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},
push-by-digest=true,
name-canonical=true,
push=${{ github.event_name != 'pull_request' }}
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,scope=${{ matrix.arch }},mode=max

- name: Export digest
if: github.event_name != 'pull_request'
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"

- name: Upload digest
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.arch }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1

merge:
runs-on: ubuntu-latest
timeout-minutes: 10
if: github.event_name != 'pull_request'
needs: build
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=edge,branch=main
type=sha,prefix=sha-
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}

- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)

- name: Inspect image
run: |
docker buildx imagetools inspect \
$(jq -cr '.tags[0]' <<< "$DOCKER_METADATA_OUTPUT_JSON")
67 changes: 67 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# syntax=docker/dockerfile:1

# =============================================================================
# Stage 1: Chef - Install cargo-chef for dependency caching
# =============================================================================
FROM rust:bookworm AS chef
RUN cargo install --locked cargo-chef@0.1.77
WORKDIR /app

# =============================================================================
# Stage 2: Planner - Generate dependency recipe
# =============================================================================
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

# =============================================================================
# Stage 3: Builder - Build all workspace binaries
# =============================================================================
FROM chef AS builder

# libclang-dev: required by bindgen (build-dep of process crate)
RUN apt-get update && apt-get install -y --no-install-recommends \
libclang-dev \
&& rm -rf /var/lib/apt/lists/*

# Build dependencies first (cached layer)
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json

# Build all workspace binaries
COPY . .
RUN cargo build --release

# Collect binaries into staging directory
RUN mkdir -p /app/staging/bin && \
find target/release -maxdepth 1 -type f -executable \
! -name '*.d' ! -name 'build-script-*' \
-exec cp {} /app/staging/bin/ \;

# =============================================================================
# Stage 4: Runtime - Minimal image with binaries
# =============================================================================
FROM ubuntu:24.04 AS runtime

LABEL org.opencontainers.image.source="https://github.com/rustcoreutils/posixutils-rs"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.description="Rust-native POSIX utilities (130+ commands)"
LABEL org.opencontainers.image.title="posixutils-rs"

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

RUN useradd --create-home --shell /bin/bash posixutils

COPY --from=builder /app/staging/bin/ /usr/local/bin/

# argv[0]-based symlinks (ex->vi, uncompress/zcat->compress)
RUN ln -sf vi /usr/local/bin/ex && \
ln -sf compress /usr/local/bin/uncompress && \
ln -sf compress /usr/local/bin/zcat

USER posixutils
WORKDIR /home/posixutils

CMD ["sh"]
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
services:
posixutils:
build: .
stdin_open: true
tty: true
volumes:
- .:/workspace:ro
working_dir: /workspace
Loading