diff --git a/.gitea/pwsh-ci/Dockerfile b/.gitea/pwsh-ci/Dockerfile new file mode 100644 index 0000000..4eff84a --- /dev/null +++ b/.gitea/pwsh-ci/Dockerfile @@ -0,0 +1,34 @@ +# The image the `pwsh` job of ci.yml runs in: Debian, PowerShell 7 and PSScriptAnalyzer, +# plus what the tool preflight and the suite need (Gitea #151, D37). Built by +# `.gitea/workflows/pwsh-ci-image.yml`, never by hand. +FROM debian:trixie-slim + +ARG PWSH_VERSION + +# `nodejs` is for act_runner, which executes JavaScript actions (checkout) inside the job +# container. `libicu76` is what PowerShell's .NET needs for culture data; `iconv` converts +# the UTF-16 checksum list the PowerShell release publishes. +RUN set -eu; \ + test -n "$PWSH_VERSION"; \ + apt-get update -qq; \ + apt-get install -y --no-install-recommends \ + ca-certificates curl git nodejs python3 python3-venv ripgrep libicu76; \ + base="https://github.com/PowerShell/PowerShell/releases/download/v${PWSH_VERSION}"; \ + tarball="powershell-${PWSH_VERSION}-linux-x64.tar.gz"; \ + curl -fsSL -o "/tmp/${tarball}" "${base}/${tarball}"; \ + curl -fsSL "${base}/hashes.sha256" | iconv -f UTF-16 -t UTF-8 | tr -d '\r' > /tmp/hashes.sha256; \ + expected="$(grep -F "*${tarball}" /tmp/hashes.sha256 | cut -d' ' -f1)"; \ + test -n "$expected"; \ + echo "${expected} /tmp/${tarball}" | sha256sum -c -; \ + mkdir -p /opt/microsoft/powershell/7; \ + tar -xzf "/tmp/${tarball}" -C /opt/microsoft/powershell/7; \ + chmod +x /opt/microsoft/powershell/7/pwsh; \ + ln -s /opt/microsoft/powershell/7/pwsh /usr/local/bin/pwsh; \ + rm -rf /tmp/* /var/lib/apt/lists/* + +RUN pwsh -NoProfile -Command \ + "Set-PSRepository PSGallery -InstallationPolicy Trusted; Install-Module PSScriptAnalyzer -Scope AllUsers -Force" + +LABEL org.opencontainers.image.title="chemenu-ci-pwsh" \ + org.opencontainers.image.description="PowerShell 7 and PSScriptAnalyzer for chemenu's pwsh CI job" \ + chemenu.pwsh-version="${PWSH_VERSION}" diff --git a/.gitea/workflows/pwsh-ci-image.yml b/.gitea/workflows/pwsh-ci-image.yml new file mode 100644 index 0000000..742b53b --- /dev/null +++ b/.gitea/workflows/pwsh-ci-image.yml @@ -0,0 +1,118 @@ +# Builds the image the `pwsh` job of ci.yml runs in: Debian, PowerShell 7 and PSScriptAnalyzer +# (Gitea #151, D37). It lives in this Gitea instance's registry as +# `gitea.nehmer.net/torben/chemenu-ci-pwsh`. +# +# The image follows the current PowerShell release, not a pin: users run whatever pwsh is +# current, so that is what the preflight has to be tested against. A change to the Dockerfile +# or to this file rebuilds it, a month turning over rebuilds it so the Debian layers do not age +# unnoticed, and a run triggered by hand can build an older release with `pwsh_version`. +# +# Tags: `:` always, `:latest` only when that version is the current release. +# +# Runner shape follows `sp-live-image.yml`: the `container-builder` label, a remote BuildKit +# on the runner host, and the registry login from 1Password. +# +# After the very first push the package has to be linked to this repository once, by hand, in +# the Gitea UI - a step no workflow can do. Until then the image builds and pulls fine; only +# the package page shows no repository. + +name: pwsh CI image + +on: + push: + branches: [main] + paths: + - '.gitea/pwsh-ci/**' + - '.gitea/workflows/pwsh-ci-image.yml' + schedule: + - cron: '30 4 1 * *' + workflow_dispatch: + inputs: + pwsh_version: + description: 'PowerShell release to build (default: the current one)' + required: false + +env: + REGISTRY: gitea.nehmer.net/torben + IMAGE_NAME: chemenu-ci-pwsh + +jobs: + build-and-push: + runs-on: container-builder + container: + image: debian:trixie-slim + steps: + - name: Install CI dependencies + # `nodejs` is for act_runner's JavaScript actions, `unzip` for + # 1password/load-secrets-action - see sp-live-image.yml. + run: | + set -eu + apt-get update -qq + apt-get install -y --no-install-recommends \ + git nodejs curl docker-cli docker-buildx unzip ca-certificates iproute2 gawk + + - uses: actions/checkout@v7 + + - name: Resolve the PowerShell release + id: pwsh + env: + REQUESTED: ${{ inputs.pwsh_version }} + run: | + set -eu + current="$(curl -fsSL https://api.github.com/repos/PowerShell/PowerShell/releases/latest \ + | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p' | head -n 1)" + test -n "$current" + wanted="${REQUESTED:-$current}" + { + echo "version=$wanted" + echo "current=$current" + } >> "$GITHUB_OUTPUT" + echo "wanted $wanted, current $current" + + - name: Load secrets from 1Password + uses: 1password/load-secrets-action@v2 + with: + export-env: true + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + REGISTRY_USER: op://CI-CD/gitea-package-token/username + REGISTRY_PAT: op://CI-CD/gitea-package-token/password + + - name: BuildKit setup (remote builder) + run: | + HOST_IP=$(ip route | awk '/default/ { print $3 }') + docker buildx create --name remote-builder --driver remote tcp://$HOST_IP:1234 --use --bootstrap + + - name: Log in to the container registry + run: | + echo "$REGISTRY_PAT" | docker login gitea.nehmer.net -u "$REGISTRY_USER" --password-stdin + + - name: Decide the tags + id: decide + env: + PWSH_VERSION: ${{ steps.pwsh.outputs.version }} + CURRENT: ${{ steps.pwsh.outputs.current }} + run: | + set -eu + tags="$REGISTRY/$IMAGE_NAME:$PWSH_VERSION" + if [ "$PWSH_VERSION" = "$CURRENT" ]; then + tags="$tags + $REGISTRY/$IMAGE_NAME:latest" + fi + { + echo "tags<> "$GITHUB_OUTPUT" + echo "tags: $tags" + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: .gitea/pwsh-ci + file: .gitea/pwsh-ci/Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ steps.decide.outputs.tags }} + build-args: | + PWSH_VERSION=${{ steps.pwsh.outputs.version }}