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
88 changes: 76 additions & 12 deletions .devcontainer/builder/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,82 @@
# Everything a contributor needs is in the development shell (see flake.nix), so
# this image only has to provide Nix and a store that already holds that shell.
# Building it here rather than on first use is what keeps provisioning to about
# a minute, and it means the container cannot drift from the flake: there is no
# second list of tools to keep in step.

# TODO: Upgrade to Ubuntu 24.04 when https://bugs.launchpad.net/ubuntu/+source/curl/+bug/2073448 is addressed
FROM mcr.microsoft.com/vscode/devcontainers/base:ubuntu-22.04

# TARGETARCH is automatically set by BuildKit to the architecture (e.g. "amd64" or "arm64")
# Docs: https://docs.docker.com/reference/dockerfile/#automatic-platform-args-in-the-global-scope
ARG TARGETARCH
# Same release the CI jobs get from cachix/install-nix-action, see
# .github/actions/setup-nix.
# renovate: datasource=github-tags depName=NixOS/nix
ARG NIX_VERSION=2.35.1

# The Nix binary tarball is xz-compressed and the base image has no xz.
RUN apt-get update \
&& apt-get install --yes --no-install-recommends xz-utils \
&& rm -rf /var/lib/apt/lists/*

# Nix is installed single-user and owned by `vscode`: a container has no init
# system to run the multi-user daemon under, and `vscode` is the only user that
# ever builds anything here. Creating the store up front is what tells the
# installer that, instead of it asking sudo for a daemon install.
RUN install --directory --mode 0755 --owner vscode --group vscode /nix

# `sandbox` — a single-user Nix has no daemon, so it would have to set up the
# build sandbox itself, which an unprivileged process in a container cannot do.
# `extra-substituters` — the cache .github/workflows/nix.yaml fills. Without it
# the warm-up below builds every pinned tool from source.
RUN mkdir -p /etc/nix && printf '%s\n' \
'experimental-features = nix-command flakes' \
'sandbox = false' \
'extra-substituters = https://argo-workflows.cachix.org' \
'extra-trusted-public-keys = argo-workflows.cachix.org-1:6SFfGYWkzLxEQhKynPaXUtXFTHipK2Ib2kmKFz+K0BA=' \
> /etc/nix/nix.conf

# So that Nix is on PATH for every shell in the container, whether or not it
# reads the profile scripts the installer writes.
ENV PATH=/home/vscode/.nix-profile/bin:${PATH}

USER vscode

# The versioned installer URL is immutable and the script it serves verifies the
# SHA-256 of the tarball it downloads, so the version above is the whole pin.
RUN curl --fail --silent --show-error --location \
"https://releases.nixos.org/nix/nix-${NIX_VERSION}/install" \
| sh -s -- --no-daemon --no-channel-add

# Only the three files the flake reads. Copying the whole repo would rebuild the
# layer below on every commit, and the workspace is bind-mounted at run time
# anyway.
COPY --chown=vscode:vscode flake.nix flake.lock go.mod /tmp/flake/

# Use bash to allow us to source hack/k8s-versions.sh
SHELL ["/bin/bash", "-c"]
# Realise the development shell into the store. Evaluating the same three files
# from the workspace produces the same derivation, so `nix develop` there finds
# every path already built. The profile is a GC root, so the shell survives a
# `nix-collect-garbage` inside the container.
RUN mkdir -p /home/vscode/.local/state/nix/profiles \
&& nix develop --profile /home/vscode/.local/state/nix/profiles/argo-devshell \
/tmp/flake --command true \
&& rm -rf /tmp/flake

# install protocol buffer tools
RUN apt-get update && apt-get install -y protobuf-compiler clang-format
# Interactive shells start in the development shell. Ubuntu's .bashrc has
# already returned for non-interactive ones by this point.
#
# The shell `nix develop` starts sources this file again, and it does so before
# it exports IN_NIX_SHELL, so that is no use as the guard against recursing —
# hence a variable of our own, exported before the call. Not `exec`, so that
# someone who has just broken flake.nix gets a prompt to fix it from rather than
# a terminal that closes itself.
RUN printf '%s\n' \
'' \
'# Tools come from the Nix development shell (see flake.nix), not from the image.' \
'if [ -z "$ARGO_DEV_SHELL" ] && [ -f "$PWD/flake.nix" ]; then' \
' export ARGO_DEV_SHELL=1' \
' nix develop || echo "the development shell did not start: no project tools on PATH" >&2' \
'fi' \
>> /home/vscode/.bashrc

# install kubectl
COPY hack/k8s-versions.sh /tmp/
RUN . /tmp/k8s-versions.sh && \
wget -O /usr/local/bin/kubectl "https://dl.k8s.io/release/${K8S_VERSIONS[min]}/bin/linux/${TARGETARCH}/kubectl" && \
chmod +x /usr/local/bin/kubectl
# Back to the base image's user: the dev container CLI adds its features on top
# of this image and expects to start from the user the base image left behind.
USER root
15 changes: 0 additions & 15 deletions .devcontainer/builder/devcontainer-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,6 @@
"version": "2.17.0",
"resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c",
"integrity": "sha256:25b9f05705ffba7dbe503230ac76081419306f8c8bc88e0ce78c4ecd99a0c78c"
},
"ghcr.io/devcontainers/features/go:1": {
"version": "1.3.4",
"resolved": "ghcr.io/devcontainers/features/go@sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032",
"integrity": "sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "1.7.1",
"resolved": "ghcr.io/devcontainers/features/node@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6",
"integrity": "sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6"
},
"ghcr.io/devcontainers/features/python:1": {
"version": "1.8.0",
"resolved": "ghcr.io/devcontainers/features/python@sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511",
"integrity": "sha256:fbcad6955caeecc5ad3f7886baf652e25cba5225a6c4c2287c536de2e5607511"
}
}
}
13 changes: 5 additions & 8 deletions .devcontainer/builder/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@
"args": { "BUILDKIT_INLINE_CACHE": "0" }
},

// Language toolchains are not features here: they come from the Nix
// development shell the Dockerfile builds, at the versions flake.nix pins.
// Docker is the exception — it is a daemon, not a tool, so Nix does not
// manage it — and k3d needs one.
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.26.1"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "20"
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/python:1": {}
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
}
}
6 changes: 5 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
"--add-host=mysql:127.0.0.1",
"--add-host=azurite:127.0.0.1"
],
"postCreateCommand": ".devcontainer/pre-build.sh",
// k3d and kubectl come from the development shell, so this runs inside it.
// The image ships a warm store, so entering it is a lookup, not a build.
// Interactive shells enter it themselves, see the .bashrc line in
// .devcontainer/builder/Dockerfile.
"postCreateCommand": "nix develop --command .devcontainer/pre-build.sh",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/go/src/github.com/argoproj/argo-workflows,type=bind",
"workspaceFolder": "/home/vscode/go/src/github.com/argoproj/argo-workflows",
"remoteEnv": {
Expand Down
8 changes: 4 additions & 4 deletions .devcontainer/pre-build.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
#!/usr/bin/env bash
set -eux

# Run from the development shell (see .devcontainer/devcontainer.json), which is
# where k3d and kubectl come from — the image installs no tools of its own.

# Make sure go path is owned by vscode
sudo chown vscode:vscode /home/vscode/go || true
sudo chown vscode:vscode /home/vscode/go/src || true
sudo chown vscode:vscode /home/vscode/go/src/github.com || true

# create cluster using the minimum tested Kubernetes version (k3d-up.sh also
# applies the kubelet config that stops images being GC'd during test runs)
K8S_VERSION=min make k3d k3d-up

# install Tilt (used by `make start`) into $GOPATH/bin, which is on PATH
make tilt
K8S_VERSION=min make k3d-up

# Patch CoreDNS to have host.docker.internal inside the cluster available
kubectl get cm coredns -n kube-system -o yaml | sed "s/ NodeHosts: |/ NodeHosts: |\n `grep host.docker.internal /etc/hosts`/" | kubectl apply -f -
19 changes: 15 additions & 4 deletions .github/workflows/devcontainer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@ name: Dev Container

on:
push:
paths:
paths: # keep in step with the pull_request list below
- ".devcontainer/**"
- "hack/k8s-versions.sh"
# The image is a warm Nix store, so it is stale as soon as the shell it
# was built from changes — including the Go version, which flake.nix
# reads out of go.mod.
- "flake.nix"
- "flake.lock"
- "go.mod"
- ".github/workflows/devcontainer.yaml"
branches:
- main
pull_request:
paths:
- ".devcontainer/**"
- "hack/k8s-versions.sh"
- "flake.nix"
- "flake.lock"
- "go.mod"
- ".github/workflows/devcontainer.yaml"
branches:
- main
Expand All @@ -31,6 +38,10 @@ jobs:
packages: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# For the `devcontainer` CLI that `make devcontainer-build` runs. It has
# a devShell to itself — see flake.nix — because the docker engine it
# drags in has no business in the shell everyone else uses.
- uses: ./.github/actions/setup-nix
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
with:
# Workaround for segfaults under arm64:
Expand All @@ -45,7 +56,7 @@ jobs:
username: ${{ secrets.QUAYIO_USERNAME }}
password: ${{ secrets.QUAYIO_PASSWORD }}
- name: Build dev container image
run: make devcontainer-build
run: nix develop .#devcontainer --command make devcontainer-build
env:
TARGET_PLATFORM: linux/amd64,linux/arm64
DEVCONTAINER_PUSH: ${{ github.event_name == 'push' && 'true' || 'false' }}
15 changes: 13 additions & 2 deletions .github/workflows/nix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,19 @@ permissions:

jobs:
linux:
name: Build development shell (x86_64-linux)
runs-on: ubuntu-24.04
name: Build development shell (${{ matrix.system }})
# aarch64-linux is not only for contributors on arm64 machines: the dev
# container image is built for both architectures and bakes this shell in,
# so a cold cache there means QEMU compiling every pinned tool.
strategy:
fail-fast: false
matrix:
include:
- system: x86_64-linux
runner: ubuntu-24.04
- system: aarch64-linux
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/setup-nix
Expand Down
4 changes: 3 additions & 1 deletion docs/running-locally.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ exact toolchain from source.

Prebuilt [development container](https://containers.dev/) images are provided for both `amd64` and `arm64` containing all you need to develop Argo Workflows, without installing tools on your local machine. Provisioning a dev container is fully automated and typically takes ~1 minute.

The container is the development shell above with the Nix part already done for you: the image has Nix installed and the whole shell built into its store, and a terminal opened in the workspace enters `nix develop` for you. So the tools are the same versions as everywhere else, and `nix develop` after a `flake.nix` change behaves the same way — it just has less to download.

You can use the development container in a few different ways:

1. [Visual Studio Code](https://code.visualstudio.com/) with [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). Open your `argo-workflows` folder in VSCode and it should offer to use the development container automatically. VSCode will allow you to forward ports to allow your external browser to access the running components.
1. [`devcontainer` CLI](https://github.com/devcontainers/cli), which the development shell provides. In your `argo-workflows` folder, run `make devcontainer-up` to start the container. Then, use `devcontainer exec --workspace-folder . /bin/bash` to get a shell where you can build the code. You can use any editor outside the container to edit code; any changes will be mirrored inside the container. Unlike the VS Code extension, the CLI does not forward ports to your host. The dev stack binds its services (UI `8080`, server `2746`, metrics `9090`, Tilt UI `10350`) to `0.0.0.0`, so reach them via the container's IP — `docker inspect <container> --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'`, then e.g. `http://<ip>:8080`.
1. [GitHub Codespaces](https://github.com/codespaces). You can start editing as soon as VSCode is open, though you may want to wait for `pre-build.sh` to finish installing dependencies, building binaries, and setting up the cluster before running any commands in the terminal. Once you start running services (see next steps below), you can click on the "PORTS" tab in the VSCode terminal to see all forwarded ports. You can open the Web UI in a new tab from there.
1. [GitHub Codespaces](https://github.com/codespaces). You can start editing as soon as VSCode is open, though you may want to wait for `pre-build.sh` to finish setting up the cluster before running any commands in the terminal. Once you start running services (see next steps below), you can click on the "PORTS" tab in the VSCode terminal to see all forwarded ports. You can open the Web UI in a new tab from there.

Once you have entered the container, continue to [Developing Locally](#developing-locally).

Expand Down
10 changes: 10 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
"# renovate: datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)( versioning=(?<versioning>\\S+))?\\s+\\w+\\s*\\??=\\s*\"?(?<currentValue>[^\\s\"]+)\"?"
],
"extractVersionTemplate": "^v(?<version>.+)$"
},
{
"description": "Update build arguments annotated with `# renovate:` in the dev container Dockerfile",
"customType": "regex",
"managerFilePatterns": [
"/(^|/)\\.devcontainer/builder/Dockerfile$/"
],
"matchStrings": [
"# renovate: datasource=(?<datasource>\\S+) depName=(?<depName>\\S+)( versioning=(?<versioning>\\S+))?\\s+ARG \\w+=(?<currentValue>\\S+)"
]
}
],
"packageRules": [
Expand Down