diff --git a/.docker/app/Dockerfile.35 b/.docker/app/Dockerfile.35 index b3b078b..1ec56f3 100644 --- a/.docker/app/Dockerfile.35 +++ b/.docker/app/Dockerfile.35 @@ -1,21 +1,24 @@ -ARG NEXTCLOUD_BASE_IMAGE=nextcloud:34-fpm - +ARG NEXTCLOUD_BASE_IMAGE FROM ${NEXTCLOUD_BASE_IMAGE} -ARG NEXTCLOUD_DAILY_URL=https://download.nextcloud.com/server/daily/latest-master.tar.bz2 +ARG NEXTCLOUD_DAILY_URL +ARG EXPECTED_NEXTCLOUD_MAJOR= +ARG PHP_EXTENSION_INSTALLER_VERSION=2.12.0 +ARG PHP_EXTENSION_INSTALLER_SHA256=3f49c71fa66c79b8b2b96bc0ce92885dafd7946f3b5a46f545b390d6f1617e2c RUN set -eux; \ - curl -fsSL "${NEXTCLOUD_DAILY_URL}" -o /tmp/nextcloud.tar.bz2; \ - curl -fsSL "${NEXTCLOUD_DAILY_URL}.sha512" -o /tmp/nextcloud.tar.bz2.sha512; \ + archive_name="$(basename "${NEXTCLOUD_DAILY_URL}")"; \ + curl -fsSL "${NEXTCLOUD_DAILY_URL}" -o "/tmp/${archive_name}"; \ + curl -fsSL "${NEXTCLOUD_DAILY_URL}.sha512" -o "/tmp/${archive_name}.sha512"; \ cd /tmp; \ - expected_sha512="$(awk '$2 == "latest-master.tar.bz2" { print $1 }' nextcloud.tar.bz2.sha512)"; \ + expected_sha512="$(awk -v archive="${archive_name}" '$2 == archive { print $1 }' "${archive_name}.sha512")"; \ test -n "${expected_sha512}"; \ - echo "${expected_sha512} nextcloud.tar.bz2" | sha512sum -c -; \ + echo "${expected_sha512} ${archive_name}" | sha512sum -c -; \ rm -rf /usr/src/nextcloud; \ - tar -xjf nextcloud.tar.bz2 -C /usr/src/; \ + tar -xjf "${archive_name}" -C /usr/src/; \ nextcloud_major="$(php -r 'require "/usr/src/nextcloud/version.php"; echo $OC_Version[0];')"; \ - test "${nextcloud_major}" = 35; \ - rm -f /tmp/nextcloud.tar.bz2 /tmp/nextcloud.tar.bz2.sha512; \ + if [ -n "${EXPECTED_NEXTCLOUD_MAJOR}" ]; then test "${nextcloud_major}" = "${EXPECTED_NEXTCLOUD_MAJOR}"; fi; \ + rm -f "/tmp/${archive_name}" "/tmp/${archive_name}.sha512"; \ rm -rf /usr/src/nextcloud/updater; \ mkdir -p /usr/src/nextcloud/data /usr/src/nextcloud/custom_apps; \ chmod +x /usr/src/nextcloud/occ @@ -33,8 +36,12 @@ ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US:en ENV LC_ALL=en_US.UTF-8 -ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ -RUN chmod uga+x /usr/local/bin/install-php-extensions && sync \ - && install-php-extensions bz2 imagick +RUN set -eux; \ + curl -fsSL \ + "https://github.com/mlocati/docker-php-extension-installer/releases/download/${PHP_EXTENSION_INSTALLER_VERSION}/install-php-extensions" \ + -o /usr/local/bin/install-php-extensions; \ + echo "${PHP_EXTENSION_INSTALLER_SHA256} /usr/local/bin/install-php-extensions" | sha256sum -c -; \ + chmod uga+x /usr/local/bin/install-php-extensions; \ + install-php-extensions bz2 imagick COPY config/php.ini /usr/local/etc/php/conf.d/ diff --git a/.github/workflows/nextcloud-35-development.yml b/.github/workflows/nextcloud-35-development.yml index 388676e..fc3bc15 100644 --- a/.github/workflows/nextcloud-35-development.yml +++ b/.github/workflows/nextcloud-35-development.yml @@ -1,52 +1,187 @@ -name: Build Nextcloud 35 Development Image +name: Build Nextcloud Development Images on: workflow_dispatch: + schedule: + - cron: '17 3 * * *' + pull_request: + branches: + - main + paths: + - '.docker/app/Dockerfile.35' + - '.docker/app/config/**' + - '.github/workflows/nextcloud-35-development.yml' + - 'scripts/scan-images.sh' + - 'trivy.yaml' push: branches: - main paths: - - .docker/app/Dockerfile.35 - - .docker/app/config/** - - .github/workflows/nextcloud-35-development.yml + - '.docker/app/Dockerfile.35' + - '.docker/app/config/**' + - '.github/workflows/nextcloud-35-development.yml' + - 'scripts/scan-images.sh' + - 'trivy.yaml' concurrency: - group: nextcloud-35-development + group: nextcloud-development-${{ github.event_name }}-${{ github.ref }} cancel-in-progress: true env: REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }}-app jobs: build: - name: Build and push app:35 + name: Build and scan ${{ matrix.channel }} runs-on: ubuntu-latest permissions: contents: read packages: write + strategy: + fail-fast: false + matrix: + include: + - channel: stable35 + base_tag: 35-fpm + daily_url: https://download.nextcloud.com/server/daily/latest-stable35.tar.bz2 + expected_major: '35' + primary_tag: '35' + alias_tag: stable35 + - channel: main + base_tag: stable-fpm + daily_url: https://download.nextcloud.com/server/daily/latest-master.tar.bz2 + expected_major: '' + primary_tag: main + alias_tag: '' + steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up QEMU + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Resolve upstream base image digest + id: base + shell: bash + env: + BASE_TAG: ${{ matrix.base_tag }} + run: | + set -euo pipefail + token="$(curl -fsSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/nextcloud:pull" | jq -r '.token')" + test -n "${token}" + + headers="$(mktemp)" + curl -fsSLI \ + -H "Authorization: Bearer ${token}" \ + -H 'Accept: application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' \ + -D "${headers}" \ + -o /dev/null \ + "https://registry-1.docker.io/v2/library/nextcloud/manifests/${BASE_TAG}" + + digest="$(awk 'BEGIN { IGNORECASE=1 } /^docker-content-digest:/ { gsub("\r", "", $2); print $2; exit }' "${headers}")" + if [[ ! "${digest}" =~ ^sha256:[0-9a-f]{64}$ ]]; then + echo "Could not resolve immutable digest for nextcloud:${BASE_TAG}" >&2 + exit 1 + fi + + echo "image=nextcloud@${digest}" >> "${GITHUB_OUTPUT}" + echo "Resolved nextcloud:${BASE_TAG} to ${digest}" + + - name: Build development app image (linux/amd64) + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + with: + context: .docker/app + file: .docker/app/Dockerfile.35 + platforms: linux/amd64 + load: true + build-args: | + NEXTCLOUD_BASE_IMAGE=${{ steps.base.outputs.image }} + NEXTCLOUD_DAILY_URL=${{ matrix.daily_url }} + EXPECTED_NEXTCLOUD_MAJOR=${{ matrix.expected_major }} + tags: scan/development-${{ matrix.channel }}:amd64 + cache-from: type=gha,scope=development-${{ matrix.channel }}-amd64 + cache-to: type=gha,mode=max,scope=development-${{ matrix.channel }}-amd64 + + - name: Build development app image (linux/arm64) + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + with: + context: .docker/app + file: .docker/app/Dockerfile.35 + platforms: linux/arm64 + load: true + build-args: | + NEXTCLOUD_BASE_IMAGE=${{ steps.base.outputs.image }} + NEXTCLOUD_DAILY_URL=${{ matrix.daily_url }} + EXPECTED_NEXTCLOUD_MAJOR=${{ matrix.expected_major }} + tags: scan/development-${{ matrix.channel }}:arm64 + cache-from: type=gha,scope=development-${{ matrix.channel }}-arm64 + cache-to: type=gha,mode=max,scope=development-${{ matrix.channel }}-arm64 + + - name: Install Trivy + uses: aquasecurity/setup-trivy@81e514348e19b6112ce2a7e3ecbafe19c1e1f567 # v0.3.1 + with: + version: v0.74.0 + cache: true + + - name: Scan development images + shell: bash + env: + CHANNEL: ${{ matrix.channel }} + run: | + set -euo pipefail + TRIVY_REPORT_DIR="trivy-results/${CHANNEL}" bash scripts/scan-images.sh "app@linux/amd64=scan/development-${CHANNEL}:amd64" "app@linux/arm64=scan/development-${CHANNEL}:arm64" + + - name: Normalize repository name + if: github.event_name != 'pull_request' + id: repo_name + shell: bash + run: echo "value=${GITHUB_REPOSITORY,,}" >> "${GITHUB_OUTPUT}" - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 + if: github.event_name != 'pull_request' + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push Nextcloud 35 development image - uses: docker/build-push-action@v6 - with: - context: .docker/app - file: .docker/app/Dockerfile.35 - platforms: linux/amd64 - push: true - tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:35 - cache-from: type=gha,scope=nextcloud-35-development - cache-to: type=gha,mode=max,scope=nextcloud-35-development + - name: Push scanned architecture images + if: github.event_name != 'pull_request' + shell: bash + env: + APP_IMAGE: ${{ env.REGISTRY }}/${{ steps.repo_name.outputs.value }}-app + CHANNEL: ${{ matrix.channel }} + run: | + set -euo pipefail + image_version="development-${CHANNEL}-${GITHUB_SHA}" + + docker tag "scan/development-${CHANNEL}:amd64" "${APP_IMAGE}:${image_version}-amd64" + docker tag "scan/development-${CHANNEL}:arm64" "${APP_IMAGE}:${image_version}-arm64" + + docker push "${APP_IMAGE}:${image_version}-amd64" + docker push "${APP_IMAGE}:${image_version}-arm64" + + - name: Publish multi-platform development tags + if: github.event_name != 'pull_request' + shell: bash + env: + APP_IMAGE: ${{ env.REGISTRY }}/${{ steps.repo_name.outputs.value }}-app + CHANNEL: ${{ matrix.channel }} + PRIMARY_TAG: ${{ matrix.primary_tag }} + ALIAS_TAG: ${{ matrix.alias_tag }} + run: | + set -euo pipefail + image_version="development-${CHANNEL}-${GITHUB_SHA}" + tag_args=(--tag "${APP_IMAGE}:${PRIMARY_TAG}") + if [ -n "${ALIAS_TAG}" ]; then + tag_args+=(--tag "${APP_IMAGE}:${ALIAS_TAG}") + fi + + docker buildx imagetools create "${tag_args[@]}" "${APP_IMAGE}:${image_version}-amd64" "${APP_IMAGE}:${image_version}-arm64" \ No newline at end of file diff --git a/README.md b/README.md index 9d3ff8c..dcc8e49 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,12 @@ docker compose build --pull docker compose up -d ``` +## Development images + +The development workflow maintains rolling app images for Nextcloud stable35 and the upstream main branch. It builds amd64 and arm64 images, runs the same Trivy policy used by this repository, and only publishes images after the scan succeeds. + +The stable35 channel publishes tags `35` and `stable35`. The rolling upstream channel publishes `main`. These tags are separate from the normal runtime `latest` image. + ## Vulnerability scanning Published `app` and `web` images are scanned for vulnerabilities. Contributors @@ -275,4 +281,4 @@ app_1 | 2020-04-28T19:49:38.577733913Z Upgrading nextcloud from 18.0.3.0 .. ## Talk -For setting up Nextcloud Talk with all services, see [here](https://github.com/LibreCodeCoop/nextcloud-docker-talk). +For setting up Nextcloud Talk with all services, see [here](https://github.com/LibreCodeCoop/nextcloud-docker-talk). \ No newline at end of file