Skip to content

perf(container): reuse base CA and timezone data #303

perf(container): reuse base CA and timezone data

perf(container): reuse base CA and timezone data #303

Workflow file for this run

name: Build and Push Docker Image
on:
push:
branches: [master]
tags: ["v*"]
pull_request:
branches: [master]
workflow_dispatch:
concurrency:
# Serialize every publishing event because both master and version tags can
# update registry state. PR checks remain independently supersedable.
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || 'publish' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
DOCKERHUB_IMAGE: aethersailor/rule-bot
GHCR_IMAGE: ghcr.io/aethersailor/rule-bot
MAX_COMPRESSED_PLATFORM_SIZE: 40000000
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: "3.14"
cache: pip
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Run static checks
run: |
python -m compileall -q src tests
ruff check src tests
pip check
- name: Run unit tests
run: python -m unittest discover -s tests -v
- name: Audit Python dependencies
run: pip-audit -r requirements.txt
build-and-push:
needs: validate
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Verify publish source
if: github.event_name != 'pull_request'
shell: bash
env:
EXPECTED_SHA: ${{ github.sha }}
run: |
set -euo pipefail
case "$GITHUB_REF" in
refs/heads/master)
remote_sha="$(git ls-remote origin refs/heads/master | awk '{print $1}')"
test -n "$remote_sha"
if [[ "$remote_sha" != "$EXPECTED_SHA" ]]; then
echo "refusing to publish stale source: expected $EXPECTED_SHA, master is $remote_sha" >&2
exit 1
fi
;;
refs/tags/v*)
if [[ ! "$GITHUB_REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "release tags must use vMAJOR.MINOR.PATCH" >&2
exit 1
fi
remote_sha="$(git ls-remote origin "refs/tags/${GITHUB_REF_NAME}^{}" | awk '{print $1}')"
if [[ -z "$remote_sha" ]]; then
remote_sha="$(git ls-remote origin "refs/tags/${GITHUB_REF_NAME}" | awk '{print $1}')"
fi
if [[ "$remote_sha" != "$EXPECTED_SHA" ]]; then
echo "refusing to publish mismatched tag: expected $EXPECTED_SHA, tag resolves to $remote_sha" >&2
exit 1
fi
;;
*)
echo "publishing is limited to master and vMAJOR.MINOR.PATCH tags" >&2
exit 1
;;
esac
- name: Extract image metadata
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
with:
images: |
${{ env.DOCKERHUB_IMAGE }}
${{ env.GHCR_IMAGE }}
flavor: latest=false
tags: |
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }}
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
labels: |
org.opencontainers.image.title=Rule Bot
org.opencontainers.image.description=Telegram bot for rule management
org.opencontainers.image.vendor=AetherSailor
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
- name: Build and optionally publish multi-arch image
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: mode=max
sbom: true
- name: Smoke test both published architectures
if: github.event_name != 'pull_request'
shell: bash
env:
PUBLISHED_TAGS: ${{ steps.meta.outputs.tags }}
run: |
set -euo pipefail
image="$(sed -n '1p' <<<"$PUBLISHED_TAGS")"
test -n "$image"
for platform in linux/amd64 linux/arm64; do
docker run --rm --pull always --platform "$platform" \
--entrypoint python "$image" \
-c 'import aiohttp, github, maxminddb, telegram; import src.main'
done
- name: Verify published images
if: github.event_name != 'pull_request'
shell: bash
env:
EXPECTED_SHA: ${{ github.sha }}
PUBLISHED_TAGS: ${{ steps.meta.outputs.tags }}
run: |
set -euo pipefail
expected_index_digest=""
while IFS= read -r image; do
[[ -n "$image" ]] || continue
published=false
for attempt in {1..12}; do
manifest="$(docker buildx imagetools inspect "$image" \
--format '{{json .Manifest}}' 2>/dev/null || true)"
if jq -e '
([.manifests[]?.platform | select(.os == "linux") | .architecture] | index("amd64")) != null
and
([.manifests[]?.platform | select(.os == "linux") | .architecture] | index("arm64")) != null
' <<<"$manifest" >/dev/null 2>&1; then
verified=true
for arch in amd64 arm64; do
repository="${image%:*}"
digest="$(jq -er --arg arch "$arch" '
.manifests[]
| select(.platform.os == "linux" and .platform.architecture == $arch)
| .digest
' <<<"$manifest")"
image_config="$(docker buildx imagetools inspect \
"${repository}@${digest}" --format '{{json .Image}}')"
revision="$(jq -r '.config.Labels["org.opencontainers.image.revision"] // empty' \
<<<"$image_config")"
if [[ "$revision" != "$EXPECTED_SHA" ]]; then
verified=false
break
fi
if jq -e '
any(.history[]?; (.created_by // "") | contains("COPY /wheels"))
' <<<"$image_config" >/dev/null; then
echo "$repository@$digest contains a copied wheelhouse layer" >&2
exit 1
fi
platform_manifest="$(docker buildx imagetools inspect \
"${repository}@${digest}" --raw)"
compressed_size="$(jq -er '[.layers[].size] | add' \
<<<"$platform_manifest")"
if (( compressed_size > MAX_COMPRESSED_PLATFORM_SIZE )); then
echo "$repository@$digest is $compressed_size bytes; limit is $MAX_COMPRESSED_PLATFORM_SIZE" >&2
exit 1
fi
echo "$repository@$digest compressed size: $compressed_size bytes"
done
if [[ "$verified" == true ]]; then
raw_manifest="$(docker buildx imagetools inspect "$image" --raw)"
index_digest="sha256:$(printf '%s' "$raw_manifest" | sha256sum | awk '{print $1}')"
if [[ -z "$expected_index_digest" ]]; then
expected_index_digest="$index_digest"
elif [[ "$index_digest" != "$expected_index_digest" ]]; then
echo "registry digest mismatch for $image: $index_digest != $expected_index_digest" >&2
exit 1
fi
published=true
break
fi
fi
sleep 10
done
if [[ "$published" != true ]]; then
echo "$image does not contain verified amd64/arm64 images for $EXPECTED_SHA" >&2
exit 1
fi
done <<<"$PUBLISHED_TAGS"
test -n "$expected_index_digest"
- name: Remove obsolete Docker Hub tags
if: github.event_name != 'pull_request'
shell: bash
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
run: |
set -euo pipefail
payload="$(jq -n \
--arg username "$DOCKERHUB_USERNAME" \
--arg password "$DOCKERHUB_TOKEN" \
'{username: $username, password: $password}')"
jwt="$(curl --fail --silent --show-error \
-H 'Content-Type: application/json' \
-d "$payload" \
https://hub.docker.com/v2/users/login/ | jq -er '.token // .access_token')"
tags_url='https://hub.docker.com/v2/repositories/aethersailor/rule-bot/tags?page_size=100'
tags="$(curl --fail --silent --show-error \
-H "Authorization: JWT $jwt" \
"$tags_url" | jq -r '.results[].name')"
while IFS= read -r tag; do
if [[ -n "$tag" && "$tag" != "latest" \
&& ! "$tag" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
curl --fail --silent --show-error -X DELETE \
-H "Authorization: JWT $jwt" \
"https://hub.docker.com/v2/repositories/aethersailor/rule-bot/tags/$tag/"
fi
done <<<"$tags"
for attempt in {1..12}; do
remaining="$(curl --fail --silent --show-error \
-H 'Cache-Control: no-cache' \
-H "Authorization: JWT $jwt" \
"$tags_url")"
if jq -e '[.results[].name | select(
. != "latest" and (test("^[0-9]+\\.[0-9]+\\.[0-9]+$") | not)
)] | length == 0' \
<<<"$remaining" >/dev/null; then
break
fi
sleep 10
done
if ! jq -e '[.results[].name | select(
. != "latest" and (test("^[0-9]+\\.[0-9]+\\.[0-9]+$") | not)
)] | length == 0' <<<"$remaining" >/dev/null; then
echo "Docker Hub still contains obsolete tags" >&2
exit 1
fi
- name: Remove unreferenced GHCR versions
if: github.event_name != 'pull_request'
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
package="rule-bot"
versions_api="/users/${GITHUB_REPOSITORY_OWNER}/packages/container/${package}/versions?per_page=100"
versions="$(gh api --paginate --slurp "$versions_api" | jq 'add')"
tagged_roots="$(mktemp)"
protected="$(mktemp)"
jq -r '.[] | select((.metadata.container.tags // []) | length > 0) | .name' \
<<<"$versions" >"$tagged_roots"
cp "$tagged_roots" "$protected"
while IFS= read -r root_digest; do
[[ -n "$root_digest" ]] || continue
docker buildx imagetools inspect "${GHCR_IMAGE}@${root_digest}" \
--format '{{json .Manifest}}' \
| jq -r '.manifests[]?.digest' >>"$protected"
done <"$tagged_roots"
sort -u -o "$protected" "$protected"
mapfile -t obsolete_ids < <(jq -r --rawfile protected "$protected" '
($protected | split("\n") | map(select(length > 0))) as $protected_digests
| .[]
| select(((.metadata.container.tags // []) | length) == 0)
| .name as $digest
| select(($protected_digests | index($digest)) == null)
| .id
' <<<"$versions")
for version_id in "${obsolete_ids[@]}"; do
gh api --method DELETE \
"/users/${GITHUB_REPOSITORY_OWNER}/packages/container/${package}/versions/${version_id}"
done
for attempt in {1..12}; do
remaining_ids="$(gh api --paginate --slurp "$versions_api" \
| jq -r 'add[] | .id')"
stale=false
for version_id in "${obsolete_ids[@]}"; do
if grep -Fxq "$version_id" <<<"$remaining_ids"; then
stale=true
break
fi
done
if [[ "$stale" == false ]]; then
exit 0
fi
sleep 10
done
echo "GHCR still contains unreferenced package versions" >&2
exit 1