Skip to content
Open
81 changes: 81 additions & 0 deletions .github/workflows/build-chezmoi-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Build and Push Chezmoi Image

on:
push:
branches:
- main
paths:
- 'chezmoi/**'
- '.github/workflows/build-chezmoi-image.yml'
pull_request:
branches:
- main
paths:
- 'chezmoi/**'
- '.github/workflows/build-chezmoi-image.yml'
workflow_run:
workflows: ["Build and Push Base Image"]
types:
- completed
branches:
- main
workflow_dispatch:

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

jobs:
build-and-push:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

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

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

- name: Determine base image tag
id: base-tag
run: |
if [ "${{ github.event_name }}" = "workflow_run" ]; then
echo "tag=sha-$(echo ${{ github.event.workflow_run.head_sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
else
echo "tag=latest" >> $GITHUB_OUTPUT
fi
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ./chezmoi
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
BASE_IMAGE_TAG=${{ steps.base-tag.outputs.tag }}
cache-from: type=gha
cache-to: type=gha,mode=max
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

Missing trailing newline at end of file. According to POSIX standard and common best practices, text files should end with a newline character.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 679a0c4.

32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,34 @@
# toolbox
Various special purpose toolboxes as container images

## Available Toolboxes

### Base
Base toolbox image with common dependencies (ca-certificates, curl, git, zsh, mise).

### Devcontainer
Development container image with git, SSH, GPG, and locale support.
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

The description mentions "git" as available in the base image, but the Devcontainer description also lists "git" as one of its features. Since git is now inherited from the base image, this creates redundancy. Consider updating the description to focus on what Devcontainer adds beyond the base: "Development container image with SSH, GPG, and locale support."

Suggested change
Development container image with git, SSH, GPG, and locale support.
Development container image with SSH, GPG, and locale support.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 679a0c4.


### Chezmoi
Container image with chezmoi for dotfiles management. Supports optional initialization with a dotfiles repository.

#### Usage

Build without initializing a dotfiles repo:
```bash
docker build -t my-chezmoi ./chezmoi
```

Build and initialize with a dotfiles repo:
```bash
docker build --build-arg CHEZMOI_REPO=https://github.com/username/dotfiles.git -t my-chezmoi ./chezmoi
```

> [!WARNING]
> Using `CHEZMOI_REPO` will execute code from the dotfiles repository during the build process. Only use with trusted repositories.
Run the container:
```bash
docker run -it my-chezmoi zsh
Copy link
Owner

Choose a reason for hiding this comment

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

Make zsh the default command for a run or start

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in commit 679a0c4 by adding CMD ["/bin/zsh"] to the Dockerfile.

```

1 change: 1 addition & 0 deletions base/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ RUN apt-get update &&\
apt-get install --no-install-recommends -y \
ca-certificates=20250419\
curl=8.14.1-2+deb13u2 \
git=1:2.47.3-0+deb13u1 \
zsh=5.9-8+b14 &&\
apt-get clean -y &&\
rm -rf /var/lib/apt/lists/*
Expand Down
26 changes: 26 additions & 0 deletions chezmoi/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
ARG BASE_IMAGE_TAG=latest
FROM ghcr.io/jhulten/toolbox/base:${BASE_IMAGE_TAG}

LABEL org.opencontainers.image.title="Toolbox Chezmoi"
LABEL org.opencontainers.image.description="Container image with chezmoi for dotfiles management"
LABEL org.opencontainers.image.source="https://github.com/jhulten/toolbox"
LABEL org.opencontainers.image.licenses="MIT"

ENV DEBIAN_FRONTEND=noninteractive

USER root

# Install chezmoi using the official installation script
ARG CHEZMOI_VERSION=2.56.1
RUN sh -c "$(curl -fsLS get.chezmoi.io)" -- -b /usr/local/bin -t v${CHEZMOI_VERSION}
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

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

The install command uses curl get.chezmoi.io without an explicit scheme, which defaults to HTTP. Piping unverified HTTP content directly to sh enables a network attacker (e.g., MITM) to inject and execute arbitrary code during the image build. Fix by explicitly using https://get.chezmoi.io and adding integrity checks (e.g., download a specific release asset over https and verify its checksum/signature), for example:

RUN sh -c "$(curl -fsLS --proto '=https' https://get.chezmoi.io)" -- -b /usr/local/bin -t v${CHEZMOI_VERSION}
Suggested change
RUN sh -c "$(curl -fsLS get.chezmoi.io)" -- -b /usr/local/bin -t v${CHEZMOI_VERSION}
RUN sh -c "$(curl -fsLS --proto '=https' https://get.chezmoi.io)" -- -b /usr/local/bin -t v${CHEZMOI_VERSION}

Copilot uses AI. Check for mistakes.
Copy link
Owner

Choose a reason for hiding this comment

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

Instead of using the curl endpoint, please install chezmoi with mise (aqua:twpayne/chezmoi).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in commits 679a0c4 and f716f20. Now using mise use --global aqua:twpayne/chezmoi@${CHEZMOI_VERSION} as the toolsmith user.


USER toolsmith
WORKDIR /home/toolsmith

# Optional: Initialize chezmoi with a dotfiles repo
# WARNING: This will execute code from the dotfiles repository during build.
# Only use with trusted repositories.
ARG CHEZMOI_REPO=""
Copy link
Owner

Choose a reason for hiding this comment

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

How would we pass credentials to chezmoi to get dotfiles from a private repo?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For private repos, credentials can be passed by:

  1. Using --build-arg CHEZMOI_REPO=https://[email protected]/user/dotfiles.git with a GitHub personal access token
  2. Mounting SSH keys at build time with --ssh in Docker buildx (requires modifying the Dockerfile to use RUN --mount=type=ssh)
  3. Or, skip initialization during build and run chezmoi init manually after starting the container with mounted credentials

The current implementation supports option 1. Options 2-3 would require additional Dockerfile modifications if you'd like me to add them.

RUN if [ -n "$CHEZMOI_REPO" ]; then \
chezmoi init --apply "$CHEZMOI_REPO"; \
fi
1 change: 0 additions & 1 deletion devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ USER root

RUN apt-get update &&\
apt-get install --no-install-recommends -y \
git=1:2.47.3-0+deb13u1 \
openssh-client=1:10.0p1-7 \
gnupg=2.4.7-21 \
locales=2.41-12 &&\
Expand Down
Loading