diff --git a/.github/workflows/external_trigger.yml b/.github/workflows/external_trigger.yml index 58e7fd9..69fdeb4 100755 --- a/.github/workflows/external_trigger.yml +++ b/.github/workflows/external_trigger.yml @@ -23,7 +23,7 @@ jobs: echo "> [!NOTE]" >> $GITHUB_STEP_SUMMARY echo "> External trigger running off of master branch. To disable this trigger, add \`nginx_master\` into the Github organizational variable \`SKIP_EXTERNAL_TRIGGER\`." >> $GITHUB_STEP_SUMMARY printf "\n## Retrieving external version\n\n" >> $GITHUB_STEP_SUMMARY - EXT_RELEASE=$(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz" | tar -xz -C /tmp \ + EXT_RELEASE=$(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz" | tar -xz -C /tmp \ && awk '/^P:'"nginx"'$/,/V:/' /tmp/APKINDEX | sed -n 2p | sed 's/^V://') echo "Type is \`alpine_repo\`" >> $GITHUB_STEP_SUMMARY if [ -z "${EXT_RELEASE}" ] || [ "${EXT_RELEASE}" == "null" ]; then @@ -49,13 +49,30 @@ jobs: --header "Accept: application/vnd.oci.image.index.v1+json" \ --header "Authorization: Bearer ${token}" \ "https://ghcr.io/v2/${image}/manifests/${tag}") - multidigest=$(jq -r ".manifests[] | select(.platform.architecture == \"amd64\").digest?" <<< "${multidigest}") - digest=$(curl -s \ - --header "Accept: application/vnd.docker.distribution.manifest.v2+json" \ - --header "Accept: application/vnd.oci.image.manifest.v1+json" \ - --header "Authorization: Bearer ${token}" \ - "https://ghcr.io/v2/${image}/manifests/${multidigest}" \ - | jq -r '.config.digest') + if jq -e '.layers // empty' <<< "${multidigest}" >/dev/null 2>&1; then + # If there's a layer element it's a single-arch manifest so just get that digest + digest=$(jq -r '.config.digest' <<< "${multidigest}") + else + # Otherwise it's multi-arch or has manifest annotations + if jq -e '.manifests[]?.annotations // empty' <<< "${multidigest}" >/dev/null 2>&1; then + # Check for manifest annotations and delete if found + multidigest=$(jq 'del(.manifests[] | select(.annotations))' <<< "${multidigest}") + fi + if [[ $(jq '.manifests | length' <<< "${multidigest}") -gt 1 ]]; then + # If there's still more than one digest, it's multi-arch + multidigest=$(jq -r ".manifests[] | select(.platform.architecture == \"amd64\").digest?" <<< "${multidigest}") + else + # Otherwise it's single arch + multidigest=$(jq -r ".manifests[].digest?" <<< "${multidigest}") + fi + if digest=$(curl -s \ + --header "Accept: application/vnd.docker.distribution.manifest.v2+json" \ + --header "Accept: application/vnd.oci.image.manifest.v1+json" \ + --header "Authorization: Bearer ${token}" \ + "https://ghcr.io/v2/${image}/manifests/${multidigest}"); then + digest=$(jq -r '.config.digest' <<< "${digest}"); + fi + fi image_info=$(curl -sL \ --header "Authorization: Bearer ${token}" \ "https://ghcr.io/v2/${image}/blobs/${digest}") @@ -79,7 +96,7 @@ jobs: if [ "${EXT_RELEASE}" == "${IMAGE_VERSION}" ]; then echo "Version \`${EXT_RELEASE}\` already pushed, exiting" >> $GITHUB_STEP_SUMMARY exit 0 - elif [[ $(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.20/main/aarch64/APKINDEX.tar.gz" | tar -xz -C /tmp && awk '/^P:'"nginx"'$/,/V:/' /tmp/APKINDEX | sed -n 2p | sed 's/^V://') != "${EXT_RELEASE}" ]]; then + elif [[ $(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.21/main/aarch64/APKINDEX.tar.gz" | tar -xz -C /tmp && awk '/^P:'"nginx"'$/,/V:/' /tmp/APKINDEX | sed -n 2p | sed 's/^V://') != "${EXT_RELEASE}" ]]; then echo "New version \`${EXT_RELEASE}\` found; but not all arch repos updated yet; exiting" >> $GITHUB_STEP_SUMMARY FAILURE_REASON="New version ${EXT_RELEASE} for nginx tag latest is detected, however not all arch repos are updated yet. Will try again later." curl -X POST -H "Content-Type: application/json" --data '{"avatar_url": "https://cdn.discordapp.com/avatars/354986384542662657/df91181b3f1cf0ef1592fbe18e0962d7.png","embeds": [{"color": 9802903, @@ -100,7 +117,7 @@ jobs: else printf "\n## Trigger new build\n\n" >> $GITHUB_STEP_SUMMARY echo "New version \`${EXT_RELEASE}\` found; old version was \`${IMAGE_VERSION}\`. Triggering new build" >> $GITHUB_STEP_SUMMARY - if "${artifacts_found}" == "true" ]]; then + if [[ "${artifacts_found}" == "true" ]]; then echo "All artifacts seem to be uploaded." >> $GITHUB_STEP_SUMMARY fi response=$(curl -iX POST \ diff --git a/.github/workflows/package_trigger_scheduler.yml b/.github/workflows/package_trigger_scheduler.yml index 549b510..7610971 100755 --- a/.github/workflows/package_trigger_scheduler.yml +++ b/.github/workflows/package_trigger_scheduler.yml @@ -27,9 +27,18 @@ jobs: fi printf "\n## Evaluating \`%s\`\n\n" ${br} >> $GITHUB_STEP_SUMMARY JENKINS_VARS=$(curl -sX GET https://raw.githubusercontent.com/linuxserver/docker-nginx/${br}/jenkins-vars.yml) - if [[ "${br}" == $(yq -r '.ls_branch' <<< "${JENKINS_VARS}") ]]; then + if ! curl -sfX GET https://raw.githubusercontent.com/linuxserver/docker-nginx/${br}/Jenkinsfile >/dev/null 2>&1; then + echo "> [!WARNING]" >> $GITHUB_STEP_SUMMARY + echo "> No Jenkinsfile found. Branch is either deprecated or is an early dev branch." >> $GITHUB_STEP_SUMMARY + skipped_branches="${skipped_branches}${br} " + elif [[ "${br}" == $(yq -r '.ls_branch' <<< "${JENKINS_VARS}") ]]; then echo "Branch appears to be live; checking workflow." >> $GITHUB_STEP_SUMMARY - if [[ $(yq -r '.skip_package_check' <<< "${JENKINS_VARS}") == "true" ]]; then + README_VARS=$(curl -sX GET https://raw.githubusercontent.com/linuxserver/docker-nginx/${br}/readme-vars.yml) + if [[ $(yq -r '.project_deprecation_status' <<< "${README_VARS}") == "true" ]]; then + echo "> [!WARNING]" >> $GITHUB_STEP_SUMMARY + echo "> Branch appears to be deprecated; skipping trigger." >> $GITHUB_STEP_SUMMARY + skipped_branches="${skipped_branches}${br} " + elif [[ $(yq -r '.skip_package_check' <<< "${JENKINS_VARS}") == "true" ]]; then echo "> [!WARNING]" >> $GITHUB_STEP_SUMMARY echo "> Skipping branch ${br} due to \`skip_package_check\` being set in \`jenkins-vars.yml\`." >> $GITHUB_STEP_SUMMARY skipped_branches="${skipped_branches}${br} " @@ -37,7 +46,7 @@ jobs: echo "> [!WARNING]" >> $GITHUB_STEP_SUMMARY echo "> Github organizational variable \`SKIP_PACKAGE_TRIGGER\` contains \`nginx_${br}\`; skipping trigger." >> $GITHUB_STEP_SUMMARY skipped_branches="${skipped_branches}${br} " - elif [ $(curl -s https://ci.linuxserver.io/job/Docker-Pipeline-Builders/job/docker-nginx/job/${br}/lastBuild/api/json | jq -r '.building') == "true" ]; then + elif [ $(curl -s https://ci.linuxserver.io/job/Docker-Pipeline-Builders/job/docker-nginx/job/${br}/lastBuild/api/json | jq -r '.building' 2>/dev/null) == "true" ]; then echo "> [!WARNING]" >> $GITHUB_STEP_SUMMARY echo "> There already seems to be an active build on Jenkins; skipping package trigger for ${br}" >> $GITHUB_STEP_SUMMARY skipped_branches="${skipped_branches}${br} " @@ -49,6 +58,11 @@ jobs: response=$(curl -iX POST \ https://ci.linuxserver.io/job/Docker-Pipeline-Builders/job/docker-nginx/job/${br}/buildWithParameters?PACKAGE_CHECK=true \ --user ${{ secrets.JENKINS_USER }}:${{ secrets.JENKINS_TOKEN }} | grep -i location | sed "s|^[L|l]ocation: \(.*\)|\1|") + if [[ -z "${response}" ]]; then + echo "> [!WARNING]" >> $GITHUB_STEP_SUMMARY + echo "> Jenkins build could not be triggered. Skipping branch." + continue + fi echo "Jenkins [job queue url](${response%$'\r'})" >> $GITHUB_STEP_SUMMARY echo "Sleeping 10 seconds until job starts" >> $GITHUB_STEP_SUMMARY sleep 10 @@ -56,11 +70,14 @@ jobs: buildurl="${buildurl%$'\r'}" echo "Jenkins job [build url](${buildurl})" >> $GITHUB_STEP_SUMMARY echo "Attempting to change the Jenkins job description" >> $GITHUB_STEP_SUMMARY - curl -iX POST \ + if ! curl -ifX POST \ "${buildurl}submitDescription" \ --user ${{ secrets.JENKINS_USER }}:${{ secrets.JENKINS_TOKEN }} \ --data-urlencode "description=GHA package trigger https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" \ - --data-urlencode "Submit=Submit" + --data-urlencode "Submit=Submit"; then + echo "> [!WARNING]" >> $GITHUB_STEP_SUMMARY + echo "> Unable to change the Jenkins job description." + fi sleep 20 fi else diff --git a/Dockerfile b/Dockerfile index d2a8ba3..bdc9472 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM ghcr.io/linuxserver/baseimage-alpine-nginx:3.20 +FROM ghcr.io/linuxserver/baseimage-alpine-nginx:3.21 # set version label ARG BUILD_DATE @@ -13,7 +13,7 @@ LABEL maintainer="nemchik" # install packages RUN \ if [ -z ${NGINX_VERSION+x} ]; then \ - NGINX_VERSION=$(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz" | tar -xz -C /tmp \ + NGINX_VERSION=$(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz" | tar -xz -C /tmp \ && awk '/^P:nginx$/,/V:/' /tmp/APKINDEX | sed -n 2p | sed 's/^V://'); \ fi && \ apk add --no-cache \ diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64 index b46f72c..0ac9dc8 100644 --- a/Dockerfile.aarch64 +++ b/Dockerfile.aarch64 @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM ghcr.io/linuxserver/baseimage-alpine-nginx:arm64v8-3.20 +FROM ghcr.io/linuxserver/baseimage-alpine-nginx:arm64v8-3.21 # set version label ARG BUILD_DATE @@ -13,7 +13,7 @@ LABEL maintainer="nemchik" # install packages RUN \ if [ -z ${NGINX_VERSION+x} ]; then \ - NGINX_VERSION=$(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz" | tar -xz -C /tmp \ + NGINX_VERSION=$(curl -sL "http://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz" | tar -xz -C /tmp \ && awk '/^P:nginx$/,/V:/' /tmp/APKINDEX | sed -n 2p | sed 's/^V://'); \ fi && \ apk add --no-cache \ diff --git a/Jenkinsfile b/Jenkinsfile index 6722cee..54fecba 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,8 +27,7 @@ pipeline { DEV_DOCKERHUB_IMAGE = 'lsiodev/nginx' PR_DOCKERHUB_IMAGE = 'lspipepr/nginx' DIST_IMAGE = 'alpine' - DIST_TAG = '3.20' - DIST_REPO = 'http://dl-cdn.alpinelinux.org/alpine/v3.20/main/' + DIST_REPO = 'http://dl-cdn.alpinelinux.org/alpine/v3.21/main/' DIST_REPO_PACKAGES = 'nginx' MULTIARCH='true' CI='true' @@ -579,7 +578,7 @@ pipeline { --label \"org.opencontainers.image.title=Nginx\" \ --label \"org.opencontainers.image.description=[Nginx](https://nginx.org/) is a simple webserver with php support. The config files reside in `/config` for easy user customization.\" \ --no-cache --pull -t ${IMAGE}:${META_TAG} --platform=linux/amd64 \ - --provenance=false --sbom=false --builder=container --load \ + --provenance=true --sbom=true --builder=container --load \ --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION=\"${VERSION_TAG}\" --build-arg BUILD_DATE=${GITHUB_DATE} ." sh '''#! /bin/bash set -e @@ -608,7 +607,9 @@ pipeline { for i in "${CACHE[@]}"; do docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} & done - wait + for p in $(jobs -p); do + wait "$p" || { echo "job $p failed" >&2; exit 1; } + done fi ''' } @@ -643,7 +644,7 @@ pipeline { --label \"org.opencontainers.image.title=Nginx\" \ --label \"org.opencontainers.image.description=[Nginx](https://nginx.org/) is a simple webserver with php support. The config files reside in `/config` for easy user customization.\" \ --no-cache --pull -t ${IMAGE}:amd64-${META_TAG} --platform=linux/amd64 \ - --provenance=false --sbom=false --builder=container --load \ + --provenance=true --sbom=true --builder=container --load \ --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION=\"${VERSION_TAG}\" --build-arg BUILD_DATE=${GITHUB_DATE} ." sh '''#! /bin/bash set -e @@ -672,7 +673,9 @@ pipeline { for i in "${CACHE[@]}"; do docker push ${i}:amd64-${COMMIT_SHA}-${BUILD_NUMBER} & done - wait + for p in $(jobs -p); do + wait "$p" || { echo "job $p failed" >&2; exit 1; } + done fi ''' } @@ -700,7 +703,7 @@ pipeline { --label \"org.opencontainers.image.title=Nginx\" \ --label \"org.opencontainers.image.description=[Nginx](https://nginx.org/) is a simple webserver with php support. The config files reside in `/config` for easy user customization.\" \ --no-cache --pull -f Dockerfile.aarch64 -t ${IMAGE}:arm64v8-${META_TAG} --platform=linux/arm64 \ - --provenance=false --sbom=false --builder=container --load \ + --provenance=true --sbom=true --builder=container --load \ --build-arg ${BUILD_VERSION_ARG}=${EXT_RELEASE} --build-arg VERSION=\"${VERSION_TAG}\" --build-arg BUILD_DATE=${GITHUB_DATE} ." sh '''#! /bin/bash set -e @@ -729,7 +732,9 @@ pipeline { for i in "${CACHE[@]}"; do docker push ${i}:arm64v8-${COMMIT_SHA}-${BUILD_NUMBER} & done - wait + for p in $(jobs -p); do + wait "$p" || { echo "job $p failed" >&2; exit 1; } + done fi ''' } @@ -972,7 +977,7 @@ pipeline { echo '{"tag_name":"'${META_TAG}'",\ "target_commitish": "master",\ "name": "'${META_TAG}'",\ - "body": "**CI Report:**\\n\\n'${CI_URL:-N/A}'\\n\\n**LinuxServer Changes:**\\n\\n'${LS_RELEASE_NOTES}'\\n\\n**Repo Changes:**\\n\\n' > start + "body": "**CI Report:**\\n\\n'${CI_URL:-N/A}'\\n\\n**LinuxServer Changes:**\\n\\n'${LS_RELEASE_NOTES}'\\n\\n**Remote Changes:**\\n\\n' > start printf '","draft": false,"prerelease": false}' >> releasebody.json paste -d'\\0' start releasebody.json > releasebody.json.done curl -H "Authorization: token ${GITHUB_TOKEN}" -X POST https://api.github.com/repos/${LS_USER}/${LS_REPO}/releases -d @releasebody.json.done''' diff --git a/README.md b/README.md index fca958b..6cc59f9 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,14 @@ The architectures supported by this image are: Add your web files to `/config/www` for hosting. Modify the nginx, php and site config files under `/config` as needed +## Read-Only Operation + +This image can be run with a read-only container filesystem. For details please [read the docs](https://docs.linuxserver.io/misc/read-only/). + +### Caveats + +* `/tmp` must be mounted to tmpfs + ## Usage To help you get started creating a container from this image you can either use docker-compose or the docker cli. @@ -116,6 +124,7 @@ Containers are configured using parameters passed at runtime (such as those abov | `-e PGID=1000` | for GroupID - see below for explanation | | `-e TZ=Etc/UTC` | specify a timezone to use, see this [list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List). | | `-v /config` | Persistent config files | +| `--read-only=true` | Run container with a read-only filesystem. Please [read the docs](https://docs.linuxserver.io/misc/read-only/). | ## Environment variables from files (Docker secrets) @@ -279,6 +288,7 @@ Once registered you can define the dockerfile to use with `-f Dockerfile.aarch64 ## Versions +* **17.12.24:** - Rebase to Alpine 3.21. * **31.05.24:** - Rebase to Alpine 3.20. Existing users should update their nginx confs to avoid http2 deprecation warnings. * **05.03.24:** - Rebase to Alpine 3.19 with php 8.3. * **25.05.23:** - Rebase to Alpine 3.18, deprecate armhf. diff --git a/jenkins-vars.yml b/jenkins-vars.yml index 5c5198a..e982b99 100644 --- a/jenkins-vars.yml +++ b/jenkins-vars.yml @@ -15,8 +15,7 @@ repo_vars: - DEV_DOCKERHUB_IMAGE = 'lsiodev/nginx' - PR_DOCKERHUB_IMAGE = 'lspipepr/nginx' - DIST_IMAGE = 'alpine' - - DIST_TAG = '3.20' - - DIST_REPO = 'http://dl-cdn.alpinelinux.org/alpine/v3.20/main/' + - DIST_REPO = 'http://dl-cdn.alpinelinux.org/alpine/v3.21/main/' - DIST_REPO_PACKAGES = 'nginx' - MULTIARCH='true' - CI='true' diff --git a/readme-vars.yml b/readme-vars.yml index 58bdbf6..a00945b 100644 --- a/readme-vars.yml +++ b/readme-vars.yml @@ -20,6 +20,9 @@ param_usage_include_ports: true param_ports: - {external_port: "80", internal_port: "80", port_desc: "http"} - {external_port: "443", internal_port: "443", port_desc: "https"} +readonly_supported: true +readonly_message: | + * `/tmp` must be mounted to tmpfs # application setup block app_setup_block_enabled: true app_setup_block: | @@ -80,6 +83,7 @@ init_diagram: | "nginx:latest" <- Base Images # changelog changelogs: + - {date: "17.12.24:", desc: "Rebase to Alpine 3.21."} - {date: "31.05.24:", desc: "Rebase to Alpine 3.20. Existing users should update their nginx confs to avoid http2 deprecation warnings."} - {date: "05.03.24:", desc: "Rebase to Alpine 3.19 with php 8.3."} - {date: "25.05.23:", desc: "Rebase to Alpine 3.18, deprecate armhf."}