diff --git a/.github/workflows/atlantis-base.yml b/.github/workflows/atlantis-base.yml index a93a0a0fbb..6c505d8d8d 100644 --- a/.github/workflows/atlantis-base.yml +++ b/.github/workflows/atlantis-base.yml @@ -3,13 +3,13 @@ name: atlantis-base on: push: paths: - - 'docker-base/**' + - 'docker-base/Dockerfile.*' - '.github/workflows/atlantis-base.yml' branches: - "main" pull_request: paths: - - 'docker-base/**' + - 'docker-base/Dockerfile.*' - '.github/workflows/atlantis-base.yml' workflow_dispatch: @@ -23,7 +23,14 @@ concurrency: jobs: build: + strategy: + matrix: + image_type: [alpine, debian] runs-on: ubuntu-22.04 + env: + DOCKERFILE: "Dockerfile.${{ matrix.image_type }}" + IMAGE_BASE: ghcr.io/${{ github.repository_owner }}/atlantis-base + IMAGE_SUFFIX: ${{ !contains(matrix.image_type, 'alpine') && '-alpine' || '' }} steps: - uses: actions/checkout@v3 @@ -42,16 +49,28 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - run: echo "TODAY=$(date +"%Y.%m.%d")" >> $GITHUB_ENV - - name: Build and push atlantis-base:${{env.TODAY}} image + - name: Populate release version + run: echo "RELEASE_VERSION=$(date +"%Y.%m.%d")" >> $GITHUB_ENV + + - name: Build and push atlantis-base:${{ env.RELEASE_VERSION }}${{ env.IMAGE_SUFFIX }} image uses: docker/build-push-action@v3 with: cache-from: type=gha cache-to: type=gha,mode=max context: docker-base + build-args: ATLANTIS_BASE_TAG_TYPE=${{ matrix.image_type }} + file: docker-base/${{ env.DOCKERFILE }} platforms: linux/arm64/v8,linux/amd64,linux/arm/v7 push: ${{ github.event_name != 'pull_request' }} + # release version is the YYYYMMDD date of the build i.e. 20301210 + # release version also has the image type appended i.e. 20301210-alpine + # release tag is latest i.e. latest + # release tag also has the image type appended i.e. latest-alpine + # if it's Dec 10, 2030 and alpine, it will do 20301210, 20301210-alpine, latest, latest-alpine + # if it's Dec 10, 2030 and debian, it will do 20301210-debian, latest-debian tags: | - ghcr.io/${{ github.repository_owner }}/atlantis-base:${{env.TODAY}} - ghcr.io/${{ github.repository_owner }}/atlantis-base:latest + ${{ env.IMAGE_BASE }}:${{ env.RELEASE_VERSION }}${{ env.IMAGE_SUFFIX }} + ${{ env.IMAGE_BASE }}:${{ env.RELEASE_VERSION }}-${{ matrix.image_type }} + ${{ env.IMAGE_BASE }}:latest${{ env.IMAGE_SUFFIX }} + ${{ env.IMAGE_BASE }}:latest-${{ matrix.image_type }} diff --git a/.github/workflows/atlantis-image.yml b/.github/workflows/atlantis-image.yml index 309f14cd4b..3b40007447 100644 --- a/.github/workflows/atlantis-image.yml +++ b/.github/workflows/atlantis-image.yml @@ -19,12 +19,18 @@ concurrency: jobs: build: + strategy: + matrix: + image_type: [alpine, debian] runs-on: ubuntu-22.04 env: RELEASE_TYPE: ${{ contains(github.ref, 'pre') && 'pre' || 'stable' }} RELEASE_TAG: ${{ contains(github.ref, 'pre') && 'prerelease-latest' || 'latest' }} + IMAGE_BASE: ghcr.io/${{ github.repository_owner }}/atlantis + IMAGE_SUFFIX: ${{ !contains(matrix.image_type, 'alpine') && '-alpine' || '' }} steps: - uses: actions/checkout@v3 + - uses: actions/setup-go@v3 with: go-version: 1.19 @@ -46,7 +52,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} # Publish dev image to container registry - - name: Build and push atlantis:dev image + - name: Build and push atlantis:dev${{ env.IMAGE_SUFFIX }} image if: ${{ contains(fromJson('["push", "pull_request"]'), github.event_name) }} uses: docker/build-push-action@v3 with: @@ -56,10 +62,10 @@ jobs: platforms: linux/arm64/v8,linux/amd64,linux/arm/v7 push: ${{ github.event_name != 'pull_request' }} tags: | - ghcr.io/${{ github.repository_owner }}/atlantis:dev + ghcr.io/${{ github.repository_owner }}/atlantis:dev${{ env.IMAGE_SUFFIX }} # Publish release to container registry - - name: populate release version + - name: Populate release version if: | contains(fromJson('["push", "pull_request"]'), github.event_name) && startsWith(github.ref, 'refs/tags/') @@ -74,8 +80,17 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max context: . + build-args: ATLANTIS_BASE_TAG_TYPE=${{ matrix.image_type }} platforms: linux/arm64/v8,linux/amd64,linux/arm/v7 push: ${{ github.event_name != 'pull_request' }} + # release version is the name of the tag i.e. v0.10.0 + # release version also has the image type appended i.e. v0.10.0-alpine + # release tag is either pre-release or latest i.e. latest + # release tag also has the image type appended i.e. latest-alpine + # if it's v0.10.0 and alpine, it will do v0.10.0, v0.10.0-alpine, latest, latest-alpine + # if it's v0.10.0 and debian, it will do v0.10.0-debian, latest-debian tags: | - ghcr.io/${{ github.repository_owner }}/atlantis:${{ env.RELEASE_VERSION }} - ghcr.io/${{ github.repository_owner }}/atlantis:${{ env.RELEASE_TAG }} + ${{ env.IMAGE_BASE }}:${{ env.RELEASE_VERSION }}${{ env.IMAGE_SUFFIX }} + ${{ env.IMAGE_BASE }}:${{ env.RELEASE_VERSION }}-${{ matrix.image_type }} + ${{ env.IMAGE_BASE }}:${{ env.RELEASE_TAG }}${{ env.IMAGE_SUFFIX }} + ${{ env.IMAGE_BASE }}:${{ env.RELEASE_TAG }}-${{ matrix.image_type }} diff --git a/Dockerfile b/Dockerfile index f5828aa5ed..268da87a09 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,9 @@ +ARG ATLANTIS_BASE=ghcr.io/runatlantis/atlantis-base +ARG ATLANTIS_BASE_TAG_DATE=2022.12.11 +ARG ATLANTIS_BASE_TAG_TYPE=alpine + # Stage 1: build artifact + FROM golang:1.19.4-alpine AS builder WORKDIR /app @@ -9,7 +14,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \ # Stage 2 # The runatlantis/atlantis-base is created by docker-base/Dockerfile -FROM ghcr.io/runatlantis/atlantis-base:2022.12.11 AS base +FROM ${ATLANTIS_BASE}:${ATLANTIS_BASE_TAG_DATE}-${ATLANTIS_BASE_TAG_TYPE} AS base # Get the architecture the image is being built for ARG TARGETPLATFORM diff --git a/docker-base/Dockerfile b/docker-base/Dockerfile.alpine similarity index 100% rename from docker-base/Dockerfile rename to docker-base/Dockerfile.alpine diff --git a/docker-base/Dockerfile.debian b/docker-base/Dockerfile.debian new file mode 100644 index 0000000000..a5e8d5a589 --- /dev/null +++ b/docker-base/Dockerfile.debian @@ -0,0 +1,77 @@ +# This Dockerfile builds our base image with gosu, dumb-init and the atlantis +# user. We split this from the main Dockerfile because this base doesn't change +# and also because it kept breaking the build due to flakiness. +FROM debian:bullseye-20221205-slim + +# We use gosu to step down from root and run as the atlantis user so we need +# to create that user and group. +# We add the atlantis user to the root group and make its home directory +# owned by root so that OpenShift users can use /home/atlantis as their +# data dir because OpenShift runs containers as a random uid that's part of +# the root group. +RUN useradd --create-home --user-group --shell /bin/bash atlantis && \ + adduser atlantis root && \ + chown atlantis:root /home/atlantis/ && \ + chmod g=u /home/atlantis/ && \ + chmod g=u /etc/passwd + +# Install gosu and git-lfs. +ENV GOSU_VERSION=1.14 +ENV GIT_LFS_VERSION=3.1.2 + +# Automatically populated with the architecture the image is being built for. +ARG TARGETPLATFORM + +# Install packages needed for running Atlantis. +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates=20210119 \ + curl=7.74.0-1.3+deb11u3 \ + git=1:2.30.2-1 \ + unzip=6.0-26+deb11u1 \ + bash=5.1-2+deb11u1 \ + openssh-server=1:8.4p1-5+deb11u1 \ + libcap2=1:2.44-1 \ + dumb-init=1.2.5-1 \ + # Install packages needed for building dependencies. + && apt-get install -y --no-install-recommends \ + gnupg=2.2.27-2+deb11u2 \ + openssl=1.1.1n-0+deb11u3 && \ + mkdir -p /tmp/build && \ + cd /tmp/build && \ + # git-lfs + case ${TARGETPLATFORM} in \ + "linux/amd64") GIT_LFS_ARCH=amd64 ;; \ + "linux/arm64") GIT_LFS_ARCH=arm64 ;; \ + "linux/arm/v7") GIT_LFS_ARCH=arm ;; \ + esac && \ + curl -L -s --output git-lfs.tar.gz "https://github.com/git-lfs/git-lfs/releases/download/v${GIT_LFS_VERSION}/git-lfs-linux-${GIT_LFS_ARCH}-v${GIT_LFS_VERSION}.tar.gz" && \ + tar -xf git-lfs.tar.gz && \ + chmod +x git-lfs && \ + mv git-lfs /usr/bin/git-lfs && \ + git-lfs --version && \ + # gosu + case ${TARGETPLATFORM} in \ + "linux/amd64") GOSU_ARCH=amd64 ;; \ + "linux/arm64") GOSU_ARCH=arm64 ;; \ + "linux/arm/v7") GOSU_ARCH=armhf ;; \ + esac && \ + curl -L -s --output gosu "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${GOSU_ARCH}" && \ + curl -L -s --output gosu.asc "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${GOSU_ARCH}.asc" && \ + for server in $(shuf -e ipv4.pool.sks-keyservers.net \ + hkp://p80.pool.sks-keyservers.net:80 \ + keyserver.ubuntu.com \ + hkp://keyserver.ubuntu.com:80 \ + pgp.mit.edu) ; do \ + gpg --keyserver "$server" --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && break || : ; \ + done && \ + gpg --batch --verify gosu.asc gosu && \ + chmod +x gosu && \ + cp gosu /bin && \ + gosu --version && \ + # Cleanup + cd /tmp && \ + rm -rf /tmp/build && \ + gpgconf --kill dirmngr && \ + gpgconf --kill gpg-agent && \ + rm -rf /root/.gnupg diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 5936a09a19..3d1e29656c 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -5,7 +5,7 @@ set -e # If the user is trying to run atlantis directly with some arguments, then # pass them to atlantis. -if [ "${1:0:1}" = '-' ]; then +if [ "$(echo "${1}" | cut -c1)" ]; then set -- atlantis "$@" fi @@ -23,7 +23,7 @@ if atlantis help "$1" 2>&1 | grep -q "atlantis $1"; then fi # If the current uid running does not have a user create one in /etc/passwd -if ! whoami &> /dev/null; then +if ! whoami > /dev/null 2>&1; then if [ -w /etc/passwd ]; then echo "${USER_NAME:-default}:x:$(id -u):0:${USER_NAME:-default} user:/home/atlantis:/sbin/nologin" >> /etc/passwd fi @@ -32,11 +32,11 @@ fi # If we're running as root and we're trying to execute atlantis then we use # gosu to step down from root and run as the atlantis user. # In OpenShift, containers are run as a random users so we don't need to use gosu. -if [[ $(id -u) == 0 ]] && [[ "$1" = 'atlantis' ]]; then +if [ "$(id -u)" = 0 ] && [ "$1" = 'atlantis' ]; then # If requested, set the capability to bind to privileged ports before # we drop to the non-root user. Note that this doesn't work with all # storage drivers (it won't work with AUFS). - if [ ! -z ${ATLANTIS_ALLOW_PRIVILEGED_PORTS+x} ]; then + if [ -n "${ATLANTIS_ALLOW_PRIVILEGED_PORTS+x}" ]; then setcap "cap_net_bind_service=+ep" /bin/atlantis fi