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
139 changes: 0 additions & 139 deletions .ci/Dockerfile

This file was deleted.

56 changes: 30 additions & 26 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,24 @@ See the [Podman installation guide](https://podman.io/docs/installation).
No IDE, no extra tooling — just build and run the container directly.

```bash
# Build the image once (from the repository root)
docker build -t darktable-dev -f .devcontainer/Dockerfile .
# Pull the pre-built CI image
docker pull ghcr.io/darktable-org/darktable-build:latest

# Verify the build compiles (same environment as CI)
# Verify the build compiles cleanly (same environment as CI)
docker run --rm --user "$(id -u):$(id -g)" \
-v "$PWD":/workspace -w /workspace \
darktable-dev \
ghcr.io/darktable-org/darktable-build:latest \
bash -lc './build.sh --prefix /tmp/dt --build-type Release'

# Build an AppImage for GUI testing on the host
docker run --rm --user "$(id -u):$(id -g)" \
-v "$PWD":/workspace -w /workspace \
-e APPIMAGE_EXTRACT_AND_RUN=1 \
darktable-dev \
ghcr.io/darktable-org/darktable-build:latest \
bash -lc './tools/appimage-build-script.sh'
```

The AppImage appears in `build/Darktable-*.AppImage` and can be run on the host.
The AppImage appears in `build/Darktable-*.AppImage` and can be run directly on the host.

> Replace `docker` with `podman` if you use Podman.

Expand Down Expand Up @@ -95,10 +95,12 @@ Then:
# Start the container
devcontainer up --workspace-folder .

# Open a shell
# Open a shell inside it
devcontainer exec --workspace-folder . bash
```

Then build as usual (see [Building](#building)).

> **VS Code and JetBrains bundle their own devcontainer implementation** — you
> only need to install the CLI separately when using other editors or working
> purely in a terminal.
Expand Down Expand Up @@ -150,31 +152,32 @@ Using `--configdir` avoids touching your production darktable configuration.
cd build && ctest
```

## CI environment and pre-built images
## CI environment

The [Dockerfile](Dockerfile) is the single source of truth for the build
environment. Inspect it for the exact base image, compiler versions, and
package list.
environment. The `.github/workflows/build-docker.yml` workflow implements a
**build → test → push** sequence: it builds a candidate image from the
Dockerfile, runs a smoke-test build of darktable inside it, and only pushes
to GHCR if the build succeeds. Linux CI jobs always pull the last tested
`:latest` image.

### Pre-built images on GHCR

`.github/workflows/build-docker.yml` automatically builds the image and
publishes it to the GitHub Container Registry (GHCR) whenever the `Dockerfile`
changes on the `master` branch. The pre-built image is available at:
The `:latest` tag on `ghcr.io/darktable-org/darktable-build` is updated
whenever `.devcontainer/Dockerfile` changes on `master`, after the candidate
image passes a smoke-test build of darktable. Each release is also tagged
`YYYY-MM-DD-SHORTSHA` for pinned auditing.

```
ghcr.io/darktable-org/darktable-build:latest
```
`workflow_dispatch` on `build-docker.yml` lets maintainers trigger a manual
rebuild — useful when the upstream `ubuntu:26.04` base image gains security
patches without any change to the Dockerfile.

Using the pre-built image skips the local build step:
### Customising the build environment

```bash
docker pull ghcr.io/darktable-org/darktable-build:latest
docker run --rm --user "$(id -u):$(id -g)" \
-v "$PWD":/workspace -w /workspace \
ghcr.io/darktable-org/darktable-build:latest \
bash -lc './build.sh --prefix /tmp/dt --build-type Release'
```
To add or remove packages, edit `.devcontainer/Dockerfile` and submit it as a
normal PR. When the change merges to `master`, `build-docker.yml` runs
automatically, builds and smoke-tests the new image, and pushes it to GHCR
only if the build succeeds.

## Troubleshooting

Expand All @@ -200,7 +203,7 @@ Always set `APPIMAGE_EXTRACT_AND_RUN=1` — FUSE is not available inside contain
git submodule update --init --recursive
```

### Git says the repository has dubious ownership inside the container
### Git says the mounted repository has dubious ownership inside the container

Pass `--user "$(id -u):$(id -g)"` to `docker run` (as shown in the examples
above), or mark the path as safe inside the container:
Expand Down Expand Up @@ -228,5 +231,6 @@ CLI: `devcontainer up --workspace-folder . --remove-existing-container`
├── devcontainer.json # IDE/tooling configuration
└── README.md # This file
.github/workflows/
└── build-docker.yml # Publishes the image to GHCR on Dockerfile changes
├── ci.yml # Linux jobs run against the published :latest image
└── build-docker.yml # Build → test → push :latest (on Dockerfile changes or workflow_dispatch)
```
5 changes: 1 addition & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
{
"name": "Darktable Development",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"image": "ghcr.io/darktable-org/darktable-build:latest",
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt=seccomp=unconfined"
Expand Down
59 changes: 59 additions & 0 deletions .github/scripts/test-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Run all Linux CI matrix configurations against a candidate Docker image.
# Used by build-docker.yml; can also be called locally to validate a new image.
# Usage: test-image.sh <image> <src-dir>
set -euo pipefail

IMAGE="${1:?Usage: $0 <image> <src-dir>}"
SRC_DIR="${2:?Usage: $0 <image> <src-dir>}"

run_config() {
local name="$1"; shift
local build_dir="${SRC_DIR}/build/${name}"
local install_dir="${SRC_DIR}/install/${name}"
mkdir -p "${build_dir}" "${install_dir}"
printf '\n=== Testing configuration: %s ===\n\n' "${name}"
docker run --rm \
--tmpfs /tmp:exec \
-v "${SRC_DIR}:${SRC_DIR}" \
-e SRC_DIR="${SRC_DIR}" \
-e BUILD_DIR="${build_dir}" \
-e INSTALL_PREFIX="${install_dir}" \
-e GENERATOR=Ninja \
"$@" \
"${IMAGE}" \
"${SRC_DIR}/.ci/ci-script.sh"
printf '\n=== Configuration %s passed ===\n' "${name}"
printf 'Cleaning up build and install directories for %s\n' "${name}"
docker run --rm \
-v "${SRC_DIR}:${SRC_DIR}" \
"${IMAGE}" \
bash -c 'rm -rf -- "$1" "$2"' _ "${build_dir}" "${install_dir}"
}

# Mirror all Linux matrix configurations from .github/workflows/ci.yml
run_config GNU16_Release \
-e CC=gcc-16 -e CXX=g++-16 \
-e CMAKE_BUILD_TYPE=Release \
-e TARGET=skiptest \
-e ECO="-DDONT_USE_INTERNAL_LIBRAW=ON"

run_config LLVM22_Release \
-e CC=clang-22 -e CXX=clang++-22 \
-e CMAKE_BUILD_TYPE=Release \
-e TARGET=skiptest \
-e ECO="-DDONT_USE_INTERNAL_LIBRAW=ON"

run_config GNU16_Debug \
-e CC=gcc-16 -e CXX=g++-16 \
-e CMAKE_BUILD_TYPE=Debug \
-e TARGET=skiptest \
-e ECO="-DDONT_USE_INTERNAL_LIBRAW=OFF"

run_config GNU16_Release_tests \
-e CC=gcc-16 -e CXX=g++-16 \
-e CMAKE_BUILD_TYPE=Release \
-e TARGET=build \
-e ECO="-DDONT_USE_INTERNAL_LIBRAW=ON"

printf '\n=== All configurations passed ===\n'
54 changes: 42 additions & 12 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,32 @@ on:
- master
paths:
- '.devcontainer/Dockerfile'
pull_request:
branches:
- master
paths:
- '.devcontainer/Dockerfile'
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
build-and-push:
build-test-push:
name: Build, test and push CI Docker image
if: github.repository == 'darktable-org/darktable' || github.event_name == 'workflow_dispatch'
name: Build and push darktable-build Docker image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
with:
submodules: false
fetch-depth: 1

- name: Get build submodules
run: |
git submodule init
git config submodule.src/tests/integration.update none
git submodule update

- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
Expand All @@ -27,12 +40,29 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@v7
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build candidate image
uses: docker/build-push-action@v6
with:
context: .
file: .devcontainer/Dockerfile
push: true
tags: |
ghcr.io/darktable-org/darktable-build:latest
ghcr.io/darktable-org/darktable-build:${{ github.sha }}
load: true
push: false
tags: darktable-build:candidate
cache-from: type=registry,ref=ghcr.io/darktable-org/darktable-build:latest

- name: Run CI matrix checks with candidate image
run: .github/scripts/test-image.sh darktable-build:candidate "$GITHUB_WORKSPACE"

- name: Tag and push tested image to GHCR
if: github.event_name != 'pull_request'
# Retag the already-tested local image — no rebuild, so what CI uses is exactly what was tested.
run: |
SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-8)
DATE_TAG="$(date -u +%Y-%m-%d)-${SHORT_SHA}"
docker tag darktable-build:candidate "ghcr.io/darktable-org/darktable-build:latest"
docker tag darktable-build:candidate "ghcr.io/darktable-org/darktable-build:${DATE_TAG}"
docker push "ghcr.io/darktable-org/darktable-build:latest"
docker push "ghcr.io/darktable-org/darktable-build:${DATE_TAG}"
Loading
Loading