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
86 changes: 86 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Build artifacts
dist/
**/dist/

# Git
.git/
.gitignore

# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Environment and config files
.env
.env.*
*.log

# CI/CD
.github/
.hermit/
bin/

# Temporary files
tmp/
temp/
*.tmp

# Test coverage
coverage/
*.lcov

# Documentation
docs/
*.md
!README.md

# Development scripts
scripts/

# macOS
.DS_Store
.AppleDouble
.LSOverride

# Windows
Thumbs.db
ehthumbs.db

# Linux
.directory
.Trash-*

# Archives
*.tar
*.tar.gz
*.tar.bz2
*.zip
*.7z
*.rar

# Backup files
*.bak
*.backup
*.old

# Docker files (don't need to copy these into context)
docker/
Dockerfile
.dockerignore
docker-compose.yml

# Development tools
Justfile
lefthook.yml
renovate.json
.goreleaser.yaml
.golangci.yml
CODEOWNERS
CONTRIBUTING.md
GOVERNANCE.md
AGENTS.md
LICENSE
89 changes: 89 additions & 0 deletions .github/workflows/publish-docker.yml
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested pushing to ghcr from this branch

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Publish Docker Image

on:
workflow_run:
workflows: ["CI"]
types:
- completed
branches:
- main
- docker
push:
tags:
- 'v*.*.*'
- 'v*.*.*-*' # For pre-releases like v1.2.3-beta
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
docker:
runs-on: ubuntu-latest
# Only run if CI passed (when triggered by workflow_run) or on tag push or manual dispatch
if: |
github.event_name == 'workflow_dispatch' ||
github.event_name == 'push' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6
with:
fetch-depth: 0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # pin@v3.11.1

- name: Log in to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # pin@v3.5.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract version metadata
id: meta
run: |
VERSION="${GITHUB_REF#refs/tags/}"
if [[ "$VERSION" == refs/heads/* ]]; then
VERSION="${VERSION#refs/heads/}"
fi
if [[ "$VERSION" == "main" ]]; then
VERSION="$(git describe --tags --always --dirty 2>/dev/null || echo dev)"
fi
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"

GIT_COMMIT="$(git rev-parse HEAD)"
echo "git_commit=${GIT_COMMIT}" >> "${GITHUB_OUTPUT}"

- name: Extract Docker metadata
id: docker_meta
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # pin@v5.8.0
with:
images: ghcr.io/${{ github.repository_owner }}/cachew
tags: |
# For main branch: latest, main, and sha
type=ref,event=branch
type=raw,value=latest,enable={{is_default_branch}}
# For tags: v1.2.3 -> 1.2.3, 1.2, 1
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
# For pre-release tags: keep the full version
type=raw,value={{tag}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}

- name: Build and push Docker image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # pin@v6.18.0
with:
context: .
file: ./docker/Dockerfile
push: true
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
build-args: |
VERSION=${{ steps.meta.outputs.version }}
GIT_COMMIT=${{ steps.meta.outputs.git_commit }}
58 changes: 58 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Contributing to Cachew

Thank you for your interest in contributing to Cachew! This guide will help you get started with local development, building, and testing.

## Local Development

**Run natively (fastest for development):**
```bash
just run # Build and run on localhost:8080
```

**Run in Docker:**
```bash
just docker run # Build and run in container
just docker run debug # Run with debug logging
```

## Building and Testing

```bash
just build # Build for current platform
just build-linux # Build for Linux
just build-all # Build all platforms
just test # Run tests
just lint # Lint code
just fmt # Format code
```

## Docker

```bash
just docker build # Build single-arch Docker image for local use
just docker build-multi # Build multi-arch image (amd64 + arm64)
just docker run # Run in container
just docker run debug # Run with debug logging
just docker clean # Clean up Docker images
```

## Using the Cache

The `cachew` CLI client interacts with the `cachewd` server:

```bash
# Upload to cache
cachew put my-key myfile.txt --ttl 24h

# Download from cache
cachew get my-key -o myfile.txt

# Check if cached
cachew stat my-key

# Snapshot a directory
cachew snapshot deps-cache ./node_modules --ttl 7d

# Restore a directory
cachew restore deps-cache ./node_modules
```
63 changes: 63 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
set positional-arguments := true
set shell := ["bash", "-c"]

# Import modules
mod docker

# Configuration

VERSION := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
GIT_COMMIT := `git rev-parse HEAD 2>/dev/null || echo "unknown"`
TAG := `git rev-parse --short HEAD 2>/dev/null || echo "dev"`
RELEASE := "dist"

_help:
@just -l

Expand All @@ -18,3 +28,56 @@ fmt:
just --unstable --fmt
golangci-lint fmt
go mod tidy

# ============================================================================
# Build
# ============================================================================

# Build for current platform
build:
@mkdir -p {{ RELEASE }}
@go build -trimpath -o {{ RELEASE }}/cachewd \
-ldflags "-s -w -X main.version={{ VERSION }} -X main.gitCommit={{ GIT_COMMIT }}" \
./cmd/cachewd
@echo "✓ Built {{ RELEASE }}/cachewd"

# Build for Linux (current arch)
build-linux:
#!/usr/bin/env bash
set -e
mkdir -p {{ RELEASE }}
ARCH=$(uname -m)
[[ "$ARCH" == "x86_64" ]] && ARCH="amd64"
[[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]] && ARCH="arm64"
echo "Building for linux/${ARCH}..."
GOOS=linux GOARCH=${ARCH} go build -trimpath \
-o {{ RELEASE }}/cachewd-linux-${ARCH} \
-ldflags "-s -w -X main.version={{ VERSION }} -X main.gitCommit={{ GIT_COMMIT }}" \
./cmd/cachewd
echo "✓ Built {{ RELEASE }}/cachewd-linux-${ARCH}"

# Build all platforms
build-all:
@mkdir -p {{ RELEASE }}
@echo "Building all platforms..."
@GOOS=darwin GOARCH=arm64 go build -trimpath -o {{ RELEASE }}/cachewd-darwin-arm64 -ldflags "-s -w -X main.version={{ VERSION }} -X main.gitCommit={{ GIT_COMMIT }}" ./cmd/cachewd
@GOOS=darwin GOARCH=amd64 go build -trimpath -o {{ RELEASE }}/cachewd-darwin-amd64 -ldflags "-s -w -X main.version={{ VERSION }} -X main.gitCommit={{ GIT_COMMIT }}" ./cmd/cachewd
@GOOS=linux GOARCH=arm64 go build -trimpath -o {{ RELEASE }}/cachewd-linux-arm64 -ldflags "-s -w -X main.version={{ VERSION }} -X main.gitCommit={{ GIT_COMMIT }}" ./cmd/cachewd
@GOOS=linux GOARCH=amd64 go build -trimpath -o {{ RELEASE }}/cachewd-linux-amd64 -ldflags "-s -w -X main.version={{ VERSION }} -X main.gitCommit={{ GIT_COMMIT }}" ./cmd/cachewd
@echo "✓ Built all platforms"

# ============================================================================
# Run
# ============================================================================

# Run natively
run: build
@echo "→ Starting cachew at http://localhost:8080"
@mkdir -p state
@{{ RELEASE }}/cachewd --config cachew.hcl

# Clean up build artifacts
clean:
@echo "Cleaning..."
@rm -rf {{ RELEASE }}
@echo "✓ Cleaned"
57 changes: 57 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Build stage
FROM golang:1.25.5-alpine AS builder

ARG VERSION=dev
ARG GIT_COMMIT=unknown
ARG TARGETOS
ARG TARGETARCH

WORKDIR /build

# Install build dependencies
RUN apk add --no-cache git ca-certificates tzdata

# Copy go mod files first for better caching
COPY go.mod go.sum ./
RUN go mod download

# Copy source code
COPY . .

# Build the binary
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -trimpath -o cachewd \
-ldflags "-s -w -X main.version=${VERSION} -X main.gitCommit=${GIT_COMMIT}" \
./cmd/cachewd

# Runtime stage
FROM alpine:3.21

SHELL ["/bin/sh", "-o", "pipefail", "-c"]

# Install runtime dependencies for git operations and TLS
RUN apk add --no-cache ca-certificates git tzdata && \
addgroup -g 1000 cachew && \
adduser -D -u 1000 -G cachew cachew

# Set working directory (config uses relative paths like ./state/cache)
WORKDIR /app

# Copy binary from builder
COPY --from=builder /build/cachewd /usr/local/bin/cachewd

# Copy default configuration file
COPY cachew.hcl /app/cachew.hcl

# Create state directory with proper permissions
RUN mkdir -p /app/state/cache && \
chown -R cachew:cachew /app

# Switch to non-root user
USER cachew

# Bind to all interfaces in Docker (can be overridden with CACHEW_BIND env var)
ENV CACHEW_BIND=0.0.0.0:8080

ENTRYPOINT ["/usr/local/bin/cachewd"]
CMD ["--config", "/app/cachew.hcl"]
Loading