Skip to content

feat: add git to Docker image #6034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Feb 6, 2023
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
53 changes: 53 additions & 0 deletions .github/workflows/docker-base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: docker-base

on:
push:
branches:
- main
paths:
- Dockerfile.base
- Dockerfile

schedule:
# Run every week at 09:43 on Monday, Wednesday and Friday. We build this
# frequently to ensure that packages are up-to-date.
- cron: "43 9 * * 1,3,5"

workflow_dispatch:

# Avoid running multiple jobs for the same commit.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-docker-base

jobs:
build:
runs-on: ubuntu-latest
if: github.repository_owner == 'coder'
steps:
- uses: actions/checkout@v3

- name: Docker login
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create empty base-build-context directory
run: mkdir base-build-context

- name: Install depot.dev CLI
uses: depot/setup-action@v1

# This uses OIDC authentication, so no auth variables are required.
- name: Build base Docker image via depot.dev
uses: depot/build-push-action@v1
with:
project: wl5hnrrkns
context: base-build-context
file: Dockerfile.base
pull: true
no-cache: true
push: true
tags: |
ghcr.io/coder/coder-base:latest
65 changes: 50 additions & 15 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,17 @@ jobs:
set -euo pipefail
wget -O /tmp/nfpm.deb https://github.com/goreleaser/nfpm/releases/download/v2.18.1/nfpm_amd64.deb
sudo dpkg -i /tmp/nfpm.deb
rm /tmp/nfpm.deb

- name: Install rcodesign
run: |
set -euo pipefail

# Install a prebuilt binary of rcodesign for linux amd64. Once the
# following PR is merged and released upstream, we can download
# directly from GitHub releases instead:
# https://github.com/indygreg/PyOxidizer/pull/635
wget -O /tmp/rcodesign https://cdn.discordapp.com/attachments/283356472258199552/1016767245717872700/rcodesign
sudo install --mode 755 /tmp/rcodesign /usr/local/bin/rcodesign
wget -O /tmp/rcodesign.tar.gz https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-x86_64-unknown-linux-musl.tar.gz
sudo tar -xzf /tmp/rcodesign.tar.gz \
-C /usr/bin \
--strip-components=1 \
apple-codesign-0.22.0-x86_64-unknown-linux-musl/rcodesign
rm /tmp/rcodesign.tar.gz

- name: Setup Apple Developer certificate and API key
run: |
Expand Down Expand Up @@ -160,6 +160,39 @@ jobs:
- name: Delete Apple Developer certificate and API key
run: rm -f /tmp/{apple_cert.p12,apple_cert_password.txt,apple_apikey.p8}

- name: Determine base image tag
id: image-base-tag
run: |
set -euo pipefail
if [[ "${CODER_RELEASE:-}" != *t* ]] || [[ "${CODER_DRY_RUN:-}" == *t* ]]; then
# Empty value means use the default and avoid building a fresh one.
echo "tag=" >> $GITHUB_OUTPUT
else
echo "tag=$(CODER_IMAGE_BASE=ghcr.io/coder/coder-base ./scripts/image_tag.sh)" >> $GITHUB_OUTPUT
fi

- name: Create empty base-build-context directory
if: steps.image-base-tag.outputs.tag != ''
run: mkdir base-build-context

- name: Install depot.dev CLI
if: steps.image-base-tag.outputs.tag != ''
uses: depot/setup-action@v1

# This uses OIDC authentication, so no auth variables are required.
- name: Build base Docker image via depot.dev
if: steps.image-base-tag.outputs.tag != ''
uses: depot/build-push-action@v1
with:
project: wl5hnrrkns
context: base-build-context
file: Dockerfile.base
pull: true
no-cache: true
push: true
tags: |
${{ steps.image-base-tag.outputs.tag }}

- name: Build Linux Docker images
run: |
set -euxo pipefail
Expand Down Expand Up @@ -188,6 +221,8 @@ jobs:
--target "$(./scripts/image_tag.sh --version latest)" \
$(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag)
fi
env:
CODER_BASE_IMAGE_TAG: ${{ steps.image-base-tag.outputs.tag }}

- name: ls build
run: ls -lh build
Expand Down Expand Up @@ -252,6 +287,14 @@ jobs:
./build/*.rpm
retention-days: 7

- name: Start Packer builds
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.CDRCI_GITHUB_TOKEN }}
repository: coder/packages
event-type: coder-release
client-payload: '{"coder_version": "${{ steps.version.outputs.version }}"}'

publish-winget:
name: Publish to winget-pkgs
runs-on: windows-latest
Expand Down Expand Up @@ -333,11 +376,3 @@ jobs:
# For gh CLI. We need a real token since we're commenting on a PR in a
# different repo.
GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }}

- name: Start Packer builds
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.CDRCI_GITHUB_TOKEN }}
repository: coder/packages
event-type: coder-release
client-payload: '{"coder_version": "${{ needs.release.outputs.version }}"}'
16 changes: 14 additions & 2 deletions .github/workflows/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,20 @@ jobs:
id: build
run: |
set -euo pipefail
image_job="build/coder_$(./scripts/version.sh)_linux_amd64.tag"
DOCKER_IMAGE_NO_PREREQUISITES=true make -j "$image_job"
version="$(./scripts/version.sh)"
image_job="build/coder_${version}_linux_amd64.tag"
# This environment variable force make to not build packages and
# archives (which the Docker image depends on due to technical reasons
# related to concurrent FS writes).
export DOCKER_IMAGE_NO_PREREQUISITES=true
# This environment variables forces scripts/build_docker.sh to build
# the base image tag locally instead of using the cached version from
# the registry.
export CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")"
make -j "$image_job"
echo "image=$(cat "$image_job")" >> $GITHUB_OUTPUT
- name: Run Trivy vulnerability scanner
Expand Down
16 changes: 5 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
# cross-compiled, it cannot have ANY "RUN" commands. All binaries are built
# using the go toolchain on the host and then copied into the build context by
# scripts/build_docker.sh.
FROM alpine:latest
#
# If you need RUN commands (e.g. to install tools via apk), add them to
# Dockerfile.base instead, which supports "RUN" commands.
ARG BASE_IMAGE
FROM $BASE_IMAGE

# LABEL doesn't add any real layers so it's fine (and easier) to do it here than
# in the build script.
Expand All @@ -14,17 +18,7 @@ LABEL \
org.opencontainers.image.source="https://github.com/coder/coder" \
org.opencontainers.image.version="$CODER_VERSION"

# Create coder group and user. We cannot use `addgroup` and `adduser` because
# they won't work if we're building the image for a different architecture.
COPY --chown=0:0 --chmod=644 group passwd /etc/
COPY --chown=1000:1000 --chmod=700 empty-dir /home/coder

# The coder binary is injected by scripts/build_docker.sh.
COPY --chown=1000:1000 --chmod=755 coder /opt/coder

USER 1000:1000
ENV HOME=/home/coder
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt
WORKDIR /home/coder

ENTRYPOINT [ "/opt/coder", "server" ]
27 changes: 27 additions & 0 deletions Dockerfile.base
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This is the base image used for Coder images. It's a multi-arch image that is
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need a base? Can we do all builds in depot? then we don't need the forking logic.

Copy link
Member Author

Choose a reason for hiding this comment

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

Because then every developer needs to have depot access and install depot CLI and authenticate it just to build docker images. The forking logic makes it easier overall as most people won't have to worry about dealing with the build process anyways.

I also don't think it adds that much overhead and it's clearly documented at the top of each Dockerfile what they're for.

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense!

# built in depot.dev for all supported architectures. Since it's built on real
# hardware and not cross-compiled, it can have "RUN" commands.
FROM alpine:latest

# We use a single RUN command to reduce the number of layers in the image.
RUN apk add --no-cache \
curl \
wget \
bash \
git \
openssh-client && \
addgroup \
-g 1000 \
coder && \
adduser \
-D \
-s /bin/bash \
-h /home/coder \
-u 1000 \
-G coder \
coder

USER 1000:1000
ENV HOME=/home/coder
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt
WORKDIR /home/coder
6 changes: 6 additions & 0 deletions dogfood/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ RUN apt-get update --quiet && apt-get install --yes \
# Configure FIPS-compliant policies
update-crypto-policies --set FIPS

# Install the docker buildx component.
RUN DOCKER_BUILDX_VERSION=$(curl -s "https://api.github.com/repos/docker/buildx/releases/latest" | grep '"tag_name":' | sed -E 's/.*"(v[^"]+)".*/\1/') && \
mkdir -p /usr/local/lib/docker/cli-plugins && \
curl -Lo /usr/local/lib/docker/cli-plugins/docker-buildx "https://github.com/docker/buildx/releases/download/${DOCKER_BUILDX_VERSION}/buildx-${DOCKER_BUILDX_VERSION}.linux-amd64" && \
chmod a+x /usr/local/lib/docker/cli-plugins/docker-buildx

# See https://github.com/cli/cli/issues/6175#issuecomment-1235984381 for proof
# the apt repository is unreliable
RUN curl -L https://github.com/cli/cli/releases/download/v2.14.7/gh_2.14.7_linux_amd64.deb -o gh.deb && \
Expand Down
56 changes: 38 additions & 18 deletions scripts/build_docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
# This script builds a Docker image of Coder containing the given binary, for
# the given architecture. Only linux binaries are supported at this time.
#
# Usage: ./build_docker.sh --arch amd64 [--version 1.2.3] [--target image_tag] [--push] path/to/coder
# Usage: ./build_docker.sh --arch amd64 [--version 1.2.3] [--target image_tag] [--build-base image_tag] [--push] path/to/coder
#
# The --arch parameter is required and accepts a Golang arch specification. It
# will be automatically mapped to a suitable architecture that Docker accepts
# before being passed to `docker buildx build`.
#
# The --build-base parameter is optional and specifies to build the base image
# in Dockerfile.base instead of pulling a copy from the registry. The string
# value is the tag to use for the built image (not pushed). This also consumes
# $CODER_IMAGE_BUILD_BASE_TAG for easily forcing a fresh build in CI.
#
# The default base image can be controlled via $CODER_BASE_IMAGE_TAG.
#
# The image will be built and tagged against the image tag returned by
# ./image_tag.sh unless a --target parameter is supplied.
#
Expand All @@ -22,12 +29,15 @@ set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"

DEFAULT_BASE="${CODER_BASE_IMAGE_TAG:-ghcr.io/coder/coder-base:latest}"

arch=""
image_tag=""
build_base="${CODER_IMAGE_BUILD_BASE_TAG:-}"
version=""
push=0

args="$(getopt -o "" -l arch:,target:,version:,push -- "$@")"
args="$(getopt -o "" -l arch:,target:,build-base:,version:,push -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
Expand All @@ -43,6 +53,10 @@ while true; do
version="$2"
shift 2
;;
--build-base)
build_base="$2"
shift
;;
--push)
push=1
shift
Expand Down Expand Up @@ -98,31 +112,37 @@ fi
cdroot
temp_dir="$(TMPDIR="$(dirname "$input_file")" mktemp -d)"
ln "$input_file" "$temp_dir/coder"
ln Dockerfile.base "$temp_dir/"
ln Dockerfile "$temp_dir/"

cd "$temp_dir"

log "--- Building Docker image for $arch ($image_tag)"

# Pull the base image, copy the /etc/group and /etc/passwd files out of it, and
# add the coder group and user. We have to do this in a separate step instead of
# using the RUN directive in the Dockerfile because you can't use RUN if you're
# building the image for a different architecture than the host.
docker pull --platform "$arch" alpine:latest 1>&2

temp_container_id="$(docker create --platform "$arch" alpine:latest)"
docker cp "$temp_container_id":/etc/group ./group 1>&2
docker cp "$temp_container_id":/etc/passwd ./passwd 1>&2
docker rm "$temp_container_id" 1>&2
export DOCKER_BUILDKIT=1

base_image="$DEFAULT_BASE"
if [[ "$build_base" != "" ]]; then
log "--- Building base Docker image for $arch ($build_base)"
docker build \
--platform "$arch" \
--tag "$build_base" \
--no-cache \
-f Dockerfile.base \
. 1>&2

base_image="$build_base"
else
docker pull --platform "$arch" "$base_image" 1>&2
fi

echo "coder:x:1000:coder" >>./group
echo "coder:x:1000:1000::/:/bin/sh" >>./passwd
mkdir ./empty-dir
log "--- Building Docker image for $arch ($image_tag)"

docker buildx build \
docker build \
--platform "$arch" \
--build-arg "BASE_IMAGE=$base_image" \
--build-arg "CODER_VERSION=$version" \
--no-cache \
--tag "$image_tag" \
-f Dockerfile \
. 1>&2

cdroot
Expand Down
5 changes: 3 additions & 2 deletions scripts/notarize_darwin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ rcodesign encode-app-store-connect-api-key \
# The notarization process is very fragile and heavily dependent on Apple's
# notarization server not returning server errors, so we retry this step twice
# with a delay of 30 seconds between attempts.
NOTARY_SUBMIT_ATTEMPTS=2
rc=0
for i in $(seq 1 2); do
for i in $(seq 1 $NOTARY_SUBMIT_ATTEMPTS); do
# -v is quite verbose, the default output is pretty good on it's own. Adding
# -v makes it dump the credentials used for uploading to Apple's S3 bucket.
rcodesign notary-submit \
Expand All @@ -58,7 +59,7 @@ for i in $(seq 1 2); do
1>&2 && rc=0 && break || rc=$?

log "rcodesign exit code: $rc"
if [[ $i -lt 5 ]]; then
if [[ $i -lt $NOTARY_SUBMIT_ATTEMPTS ]]; then
log
log "Retrying notarization in 30 seconds"
log
Expand Down