Slim the image, publish on version tags, drop deployment specifics
The first publish failed partway through the push with 413 Payload Too Large: one layer was bigger than the proxy in front of the registry would accept. Three changes, only one of which is that fix. Keep deployment out of a public repo. The registry, image name and credentials now come from repository variables and secrets rather than being written down here, and the docs describe how to run the thing rather than where one particular instance runs. PUBLIC_ORIGIN defaults to localhost. The 413 is a proxy limit, so the fix is pointing REGISTRY at a host the runner reaches directly; the workflow explains itself if that host is plain HTTP and the builder's daemon has not been told to allow it. Publish on version tags. A tag like 1.2.3 publishes :1.2.3, :1.2, :1 and :latest; a prerelease publishes only its exact version and leaves :latest alone. Pushes to main publish :main and :sha-<short> and no longer move :latest, so what is deployed moves when a release says so. Shrink the image from over 1.2GB to 353MB. The Playwright base image carries Firefox and WebKit, which this never launches. Installing just the browser it does launch onto a slim Node base drops two thirds of the weight, which is worth having on a Raspberry Pi even though it does not get any single layer under a proxy limit. That last change surfaced something worth naming: a headless launch resolves to Playwright's headless shell, not the full browser, so that is what every test so far has actually been running. The image now installs exactly that binary and pool.ts names the channel, so the two cannot drift apart. Verified in the container: Bluesky, Instagram, X and Threads all resolve identically on the slim image. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01BGkRmLfiWuJHx6tQ12EELY
This commit is contained in:
@@ -1,15 +1,36 @@
|
||||
name: Publish
|
||||
|
||||
# Builds the application image and pushes it to the Gitea container registry
|
||||
# as git.unsupervised.ca/unsupervised/antisocial.
|
||||
# Builds the application image and pushes it to a container registry.
|
||||
#
|
||||
# Runs on main, and on demand. Pull requests build without pushing, so a
|
||||
# broken Dockerfile is caught before it can move a published tag.
|
||||
# push to main -> :main and :sha-<short>
|
||||
# tag 1.2.3 -> :1.2.3, :1.2, :1 and :latest
|
||||
# pull request -> builds without pushing, so a broken Dockerfile is
|
||||
# caught before it can move a published tag
|
||||
#
|
||||
# Configure with repository variables and secrets:
|
||||
#
|
||||
# vars.REGISTRY required, e.g. registry.example.com
|
||||
# vars.IMAGE_NAME optional, defaults to this repository's owner/name
|
||||
# vars.REGISTRY_USER optional, defaults to the actor running the workflow
|
||||
# secrets.REGISTRY_TOKEN required to push
|
||||
#
|
||||
# Point REGISTRY at a host the runner reaches directly, without an intermediate
|
||||
# proxy that caps request bodies: a browser image has layers well over 100MB,
|
||||
# and such a proxy rejects them mid-push with `413 Payload Too Large`.
|
||||
#
|
||||
# If that host serves plain HTTP, the builder's Docker daemon also needs it in
|
||||
# `insecure-registries` — that is daemon configuration, not something a
|
||||
# workflow can set.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+'
|
||||
- '[0-9]+.[0-9]+.[0-9]+-*'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-*'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'Dockerfile'
|
||||
@@ -18,10 +39,6 @@ on:
|
||||
- '.gitea/workflows/publish.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: git.unsupervised.ca
|
||||
IMAGE: unsupervised/antisocial
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and push
|
||||
@@ -29,55 +46,97 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Images are built natively, so each carries the architecture of the
|
||||
# runner that built it. Every runner in the pool is arm64, which is
|
||||
# also what Kallone is. A runner of a different architecture joining
|
||||
# would overwrite these tags with its own arch, at which point this
|
||||
# needs buildx and a multi-arch manifest.
|
||||
- name: Check Docker is available
|
||||
- name: Check the runner can build images
|
||||
run: |
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
echo "No usable Docker daemon in the job container." >&2
|
||||
echo "act_runner needs container.docker_host set, or left empty to autodetect." >&2
|
||||
exit 1
|
||||
fi
|
||||
docker version --format 'client {{.Client.Version}} / server {{.Server.Version}} / arch {{.Server.Arch}}'
|
||||
|
||||
# Images are built natively, so each carries the architecture of the
|
||||
# runner that built it. A runner of a different architecture joining the
|
||||
# pool would overwrite these tags with its own arch, at which point this
|
||||
# needs buildx and a manifest list.
|
||||
- name: Work out the tags
|
||||
id: tags
|
||||
run: echo "sha=sha-$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||
id: meta
|
||||
env:
|
||||
REGISTRY: ${{ vars.REGISTRY }}
|
||||
IMAGE_NAME: ${{ vars.IMAGE_NAME }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# REGISTRY_TOKEN is an organisation secret: a personal access token
|
||||
# with the package scope, Read and Write. Gitea's Actions task token is
|
||||
# rejected by the container registry (go-gitea/gitea#23642), so this
|
||||
# cannot fall back to secrets.GITHUB_TOKEN. The login user must own the
|
||||
# token; set the REGISTRY_USER variable if it is not github.actor.
|
||||
if [ -z "${REGISTRY}" ]; then
|
||||
echo "The REGISTRY repository variable is not set." >&2
|
||||
echo "Set it to the registry host to publish to, e.g. registry.example.com" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
image="${REGISTRY}/$(echo "${IMAGE_NAME:-${{ github.repository }}}" | tr '[:upper:]' '[:lower:]')"
|
||||
tags=""
|
||||
|
||||
if [ "${{ github.ref_type }}" = "tag" ]; then
|
||||
version="${{ github.ref_name }}"
|
||||
version="${version#v}"
|
||||
tags="${version}"
|
||||
|
||||
# Only a final release moves the rolling aliases; a prerelease is
|
||||
# published under its own exact version and nothing else.
|
||||
case "${version}" in
|
||||
*-*) ;;
|
||||
*)
|
||||
major="${version%%.*}"
|
||||
minor="${version%.*}"
|
||||
tags="${tags} ${minor} ${major} latest"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
tags="main sha-$(git rev-parse --short HEAD)"
|
||||
fi
|
||||
|
||||
args=""
|
||||
for tag in ${tags}; do args="${args} --tag ${image}:${tag}"; done
|
||||
|
||||
{
|
||||
echo "image=${image}"
|
||||
echo "tags=${tags}"
|
||||
echo "args=${args}"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "Publishing ${image} as:${tags// /, :}"
|
||||
|
||||
# The Actions task token is rejected by some registries, so pushing uses
|
||||
# a token that belongs to a real user.
|
||||
- name: Log in to the container registry
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
if [ -z "${{ secrets.REGISTRY_TOKEN }}" ]; then
|
||||
echo "REGISTRY_TOKEN is not set. The Actions task token cannot push packages." >&2
|
||||
echo "REGISTRY_TOKEN is not set." >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! echo "${{ secrets.REGISTRY_TOKEN }}" \
|
||||
| docker login "${{ vars.REGISTRY }}" -u "${{ vars.REGISTRY_USER || github.actor }}" --password-stdin
|
||||
then
|
||||
echo >&2
|
||||
echo "If that failed with 'server gave HTTP response to HTTPS client', the" >&2
|
||||
echo "registry is plain HTTP and the builder's Docker daemon has to be told" >&2
|
||||
echo "to allow it: add ${{ vars.REGISTRY }} to insecure-registries in the" >&2
|
||||
echo "daemon config on the runner. The workflow cannot configure that." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" \
|
||||
| docker login "${REGISTRY}" -u "${{ vars.REGISTRY_USER || github.actor }}" --password-stdin
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
docker build \
|
||||
--pull \
|
||||
--tag "${REGISTRY}/${IMAGE}:${{ steps.tags.outputs.sha }}" \
|
||||
--tag "${REGISTRY}/${IMAGE}:latest" \
|
||||
--file Dockerfile \
|
||||
.
|
||||
run: docker build --pull ${{ steps.meta.outputs.args }} --file Dockerfile .
|
||||
|
||||
- name: Push
|
||||
if: github.event_name != 'pull_request'
|
||||
run: |
|
||||
docker push "${REGISTRY}/${IMAGE}:${{ steps.tags.outputs.sha }}"
|
||||
docker push "${REGISTRY}/${IMAGE}:latest"
|
||||
echo "Published ${REGISTRY}/${IMAGE}:${{ steps.tags.outputs.sha }}"
|
||||
set -euo pipefail
|
||||
for tag in ${{ steps.meta.outputs.tags }}; do
|
||||
docker push "${{ steps.meta.outputs.image }}:${tag}"
|
||||
done
|
||||
echo "Published ${{ steps.meta.outputs.image }} as: ${{ steps.meta.outputs.tags }}"
|
||||
|
||||
- name: Log out
|
||||
if: always() && github.event_name != 'pull_request'
|
||||
run: docker logout "${REGISTRY}" || true
|
||||
run: docker logout "${{ vars.REGISTRY }}" || true
|
||||
|
||||
Reference in New Issue
Block a user