Skip to content
Open
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
35 changes: 35 additions & 0 deletions src/current/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Git
.git
.gitignore

# Jekyll build outputs
_site
.jekyll-cache
.jekyll-metadata
.sass-cache

# Ruby vendor dependencies (will be installed in image)
vendor

# Node modules
node_modules

# Logs
*.log
build_*.log

# OS files
.DS_Store
Thumbs.db

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

# Algolia state (generated during indexing)
algolia_state

# Temporary files
tmp
66 changes: 66 additions & 0 deletions src/current/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Hermetic Jekyll Documentation Build Image
#
# This Dockerfile creates a consistent build environment for the CockroachDB
# documentation site. It pins all Ruby, Python, and Node.js dependencies to
# ensure reproducible builds across all developers and CI environments.
#
# For build and publish instructions, see ci/README.md

FROM ruby:3.4-slim

# Version labels
LABEL org.opencontainers.image.title="CockroachDB Docs Builder"
LABEL org.opencontainers.image.description="Hermetic build environment for CockroachDB documentation"
LABEL org.opencontainers.image.source="https://github.com/cockroachdb/docs"
LABEL ruby.version="3.4.0"
LABEL bundler.version="4.0.0"
LABEL jekyll.version="4.3.4"

# Install build and runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
git \
python3 \
python3-pip \
curl \
libxml2-dev \
libxslt1-dev \
&& rm -rf /var/lib/apt/lists/*

# Install Node.js 20 LTS for Jest tests
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /docs

# Install specific bundler version (must match Gemfile.lock BUNDLED WITH)
RUN gem install bundler:4.0.0 --no-document

# Copy Gemfile and local gem for dependency installation
# The jekyll-algolia-dev directory contains a local gem that must be present
COPY Gemfile Gemfile.lock ./
COPY jekyll-algolia-dev ./jekyll-algolia-dev/

# Configure bundler and install gems to /usr/local/bundle (default location)
RUN bundle config set --local jobs 4 \
&& bundle config set --local without development:test \
&& bundle install

# Install Python dependencies for Algolia indexing
RUN pip3 install --break-system-packages --no-cache-dir \
pyyaml \
"algoliasearch>=3.0,<4.0" \
beautifulsoup4 \
lxml \
tqdm

# Set environment variables
ENV JEKYLL_ENV=development
ENV BUNDLE_FROZEN=true

# Expose Jekyll server port
EXPOSE 4000

# Default command - serve the documentation locally
CMD ["bundle", "exec", "jekyll", "serve", "--host", "0.0.0.0", "--port", "4000"]
4 changes: 4 additions & 0 deletions src/current/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ source "https://rubygems.org"
# If you add to this file, it is recommended to run `make vendor`
# (`gem install bundler && bundle install`) before running your next local build.
# It may fail without at least running `bundle install`.
#
# If you modify this file, you'll need to rebuild the docs-builder Docker
# image to ensure CI and local builds use the updated dependencies.
# See ci/README.md for build and publish instructions.
gem "jekyll", "4.3.4"
gem "liquid-c", "~> 4.0.0"
gem "redcarpet", "~> 3.6"
Expand Down
67 changes: 67 additions & 0 deletions src/current/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,70 @@ clean-site:

clean-cache:
rm -rf .jekyll-cache

# =============================================================================
# Docker-based builds
# =============================================================================
# These targets use the hermetic docs-builder Docker image for consistent builds
# across all environments. See ci/README.md for more details.

DOCKER_REGISTRY := us-docker.pkg.dev/release-notes-automation-stag/docs-builder
DOCKER_IMAGE := docs-builder
DOCKER_TAG := latest
# Run as current user to avoid permission issues with mounted volumes
DOCKER_USER := $(shell id -u):$(shell id -g)

# Build the Docker image locally
.PHONY: docker-build
docker-build:
docker build -t $(DOCKER_IMAGE):local .

# Serve documentation using Docker (with live reload)
.PHONY: docker-serve
docker-serve:
docker run -it --rm \
--dns 8.8.8.8 \
--user $(DOCKER_USER) \
-p 4000:4000 \
-v "$(CURDIR)":/docs \
-e JEKYLL_ENV=development \
-e HOME=/tmp \
$(DOCKER_IMAGE):local \
bundle exec jekyll serve --host 0.0.0.0 --port 4000 --incremental --trace \
--config _config_base.yml,_config_cockroachdb.yml,_config_cockroachdb_local.yml

# Build documentation using Docker (no serve)
.PHONY: docker-build-site
docker-build-site:
docker run -it --rm \
--dns 8.8.8.8 \
--user $(DOCKER_USER) \
-v "$(CURDIR)":/docs \
-e JEKYLL_ENV=production \
-e HOME=/tmp \
$(DOCKER_IMAGE):local \
bundle exec jekyll build --trace \
--config _config_base.yml,_config_cockroachdb.yml

# Interactive shell in the Docker container
.PHONY: docker-shell
docker-shell:
docker run -it --rm \
--dns 8.8.8.8 \
--user $(DOCKER_USER) \
-v "$(CURDIR)":/docs \
-e HOME=/tmp \
$(DOCKER_IMAGE):local \
/bin/bash

# Pull the latest pre-built image from GCP Artifact Registry
.PHONY: docker-pull
docker-pull:
docker pull $(DOCKER_REGISTRY)/$(DOCKER_IMAGE):$(DOCKER_TAG)
docker tag $(DOCKER_REGISTRY)/$(DOCKER_IMAGE):$(DOCKER_TAG) $(DOCKER_IMAGE):local

# Push image to GCP Artifact Registry (requires gcloud auth)
.PHONY: docker-push
docker-push:
docker tag $(DOCKER_IMAGE):local $(DOCKER_REGISTRY)/$(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE):$(DOCKER_TAG)
7 changes: 7 additions & 0 deletions src/current/_plugins/versions/release_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ def generate(site)
parent_dir = File.expand_path('..', site.source)

# Step 2: Construct the paths to versions.csv and releases.yml
# Try parent/current/_data first (standard layout), fall back to site.source/_data (Docker)
versions_path = File.join(parent_dir, "current/_data/versions.csv")
releases_path = File.join(parent_dir, "current/_data/releases.yml")

# Fall back to site.source if parent path doesn't exist (e.g., in Docker container)
unless File.exist?(versions_path)
versions_path = File.join(site.source, "_data/versions.csv")
releases_path = File.join(site.source, "_data/releases.yml")
end

# Load versions and releases data
versions_data = CSV.read(versions_path, headers: true)
releases_data = YAML.load_file(releases_path)
Expand Down
195 changes: 195 additions & 0 deletions src/current/ci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# Documentation Build Docker Image

This directory contains configuration for building and publishing the hermetic Docker image used for CockroachDB documentation builds.

## Quick Start (Local Development)

```bash
# 1. Build the Docker image (first time only)
make docker-build

# 2. Serve docs locally with live reload
make docker-serve

# 3. Open http://localhost:4000/docs/ in your browser
```

That's it! No Ruby, Bundler, or gem installation required on your machine.

**Other useful commands:**
- `make docker-build-site` - Build without serving
- `make docker-shell` - Interactive shell for debugging
- `make docker-pull` - Pull pre-built image from GCP (instead of building locally)

## Overview

The `docs-builder` Docker image provides a consistent build environment with pinned versions of:

| Component | Version |
|-----------|---------|
| Base Image | ruby:3.4-slim (Debian Trixie) |
| Ruby | 3.4.0 |
| Bundler | 4.0.0 |
| Jekyll | 4.3.4 |
| Python | 3.13+ |
| Node.js | 20 LTS |

## Prerequisites

- Docker installed locally
- For publishing: `gcloud` CLI configured with appropriate permissions

## Building the Image Locally

```bash
# From the repository root
docker build -t docs-builder:local .

# Or use the Makefile target
make docker-build
```

## Running Locally with Docker

### Serve documentation with live reload

```bash
make docker-serve
# Then open http://localhost:4000
```

### Interactive shell in the container

```bash
make docker-shell
```

### Build without serving

```bash
docker run -it --rm \
-v "$(pwd)":/docs \
docs-builder:local \
bundle exec jekyll build --trace \
--config _config_base.yml,_config_cockroachdb.yml
```

## Publishing to GCP Artifact Registry

### Manual Publishing

1. Authenticate with GCP:
```bash
gcloud auth login
gcloud auth configure-docker us-docker.pkg.dev
```

2. Build and push the image:
```bash
# Set the image tag (use date-based versioning)
export IMAGE_TAG=$(date +%Y-%m-%d)
export REGISTRY=us-docker.pkg.dev/release-notes-automation-stag/docs-builder

# Build for multi-architecture
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag ${REGISTRY}/docs-builder:${IMAGE_TAG} \
--tag ${REGISTRY}/docs-builder:latest \
--push \
.
```

### Automated Publishing with Cloud Build

Trigger a Cloud Build to build and publish the image:

```bash
gcloud builds submit --config ci/cloudbuild.yaml .
```

## Image Tagging Convention

| Tag | Description |
|-----|-------------|
| `latest` | Most recent build |
| `YYYY-MM-DD` | Date-based version (e.g., `2025-01-15`) |
| `ruby3.4-bundler4.0` | Version-based for explicit compatibility |

## When to Rebuild the Image

Rebuild and publish a new image when:

1. `Gemfile` or `Gemfile.lock` changes
2. Python dependencies change in build scripts
3. Ruby or Node.js version needs updating
4. Security updates are required for base image

## Environment Variables

The following environment variables can be passed to the container:

| Variable | Description | Default |
|----------|-------------|---------|
| `JEKYLL_ENV` | Build environment (`development`, `production`, `preview`) | `development` |
| `ALGOLIA_API_KEY` | Algolia write API key for indexing | - |
| `PROD_ALGOLIA_API_KEY` | Production Algolia API key | - |

## Volume Mounts

| Mount Point | Purpose |
|-------------|---------|
| `/docs` | Mount the documentation source directory here |

## Exposed Ports

| Port | Service |
|------|---------|
| 4000 | Jekyll development server |

## Troubleshooting

### Permission errors with mounted volumes

If you encounter permission errors (e.g., with `.jekyll-cache`, `_site`, `.jekyll-metadata`), run the container as your current user:

```bash
docker run -it --rm \
-v "$(pwd)":/docs \
-u "$(id -u):$(id -g)" \
-e HOME=/tmp \
docs-builder:local \
bundle exec jekyll build
```

If files were created by root in a previous run, remove them first:

```bash
sudo rm -rf .jekyll-cache _site .jekyll-metadata
```

### DNS resolution errors

If you see errors like "Failed to open TCP connection" or "getaddrinfo: Temporary failure in name resolution", add explicit DNS:

```bash
docker run -it --rm \
--dns 8.8.8.8 \
-v "$(pwd)":/docs \
docs-builder:local \
bundle exec jekyll build
```

### Native gem compilation issues

The image uses `ruby:3.4-slim` which includes build tools for native extensions. If you encounter issues:

1. Ensure you're using the multi-arch image matching your platform
2. Try pulling a fresh image: `docker pull ${REGISTRY}/docs-builder:latest`

### Bundle path issues

The image pre-installs gems to `/usr/local/bundle`. When mounting your local directory, ensure you don't have a conflicting local `vendor/bundle` directory.

To work around this, you can either:
- Clear your local vendor directory: `rm -rf vendor`
- Or use a different bundle path in the container
Loading
Loading