Publish runner image to Gitea Container Registry
Some checks failed
Test blackout notifier / test (push) Failing after 0s
Hourly blackout check / check (push) Failing after 0s
Build runner image / build (push) Failing after 2s

This commit is contained in:
Meghdad
2026-07-17 20:22:52 +03:30
parent c23e8b98f7
commit 308ed80ff4
4 changed files with 146 additions and 32 deletions

View File

@@ -0,0 +1,73 @@
name: Build runner image
on:
push:
branches:
- main
paths:
- runner-image/**
- .gitea/workflows/build-runner-image.yml
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
container:
image: node:20-bookworm
volumes:
- /var/run/docker.sock:/var/run/docker.sock
steps:
- name: Check out repository
uses: https://gitea.com/actions/checkout@v4
- name: Install Docker client
run: |
set -eu
for attempt in 1 2 3; do
if apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
--no-install-recommends docker.io; then
break
fi
if [ "$attempt" -eq 3 ]; then
echo "Unable to install the Docker client after 3 attempts"
exit 1
fi
sleep "$((attempt * 5))"
done
rm -rf /var/lib/apt/lists/*
docker version --format 'Docker client {{.Client.Version}}, server {{.Server.Version}}'
- name: Build, verify, and publish image
env:
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -eu
registry="mahgit.ir"
image="$registry/meghdadfadaee/daily-blackout-check-runner"
revision="$(git rev-parse --short=12 HEAD)"
candidate="$image:$revision"
cleanup() {
docker logout "$registry" >/dev/null 2>&1 || true
}
trap cleanup EXIT
printf '%s' "$REGISTRY_TOKEN" | docker login "$registry" \
--username "$REGISTRY_USERNAME" \
--password-stdin
docker build \
--pull \
--label "org.opencontainers.image.revision=$revision" \
--label "org.opencontainers.image.source=https://mahgit.ir/MeghdadFadaee/daily-blackout-check" \
--tag "$candidate" \
runner-image
docker run --rm "$candidate" python -c "import dotenv, jdatetime, requests"
docker run --rm "$candidate" pytest --version
docker run --rm "$candidate" ruff --version
docker push "$candidate"
docker tag "$candidate" "$image:latest"
docker push "$image:latest"

View File

@@ -9,7 +9,10 @@ jobs:
check:
runs-on: ubuntu-latest
container:
image: daily-blackout-check-runner:latest
image: mahgit.ir/meghdadfadaee/daily-blackout-check-runner:latest
credentials:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_TOKEN }}
env:
PYTHONPATH: src
steps:

View File

@@ -11,7 +11,10 @@ jobs:
test:
runs-on: ubuntu-latest
container:
image: daily-blackout-check-runner:latest
image: mahgit.ir/meghdadfadaee/daily-blackout-check-runner:latest
credentials:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_TOKEN }}
env:
PYTHONPATH: src
steps:

View File

@@ -62,37 +62,77 @@ Shell and Gitea-provided variables take precedence over `.env`.
## Gitea deployment
This repository targets Gitea 1.25.x and a Docker-based runner advertising the
`ubuntu-latest` label. Both workflows run inside the locally built
`daily-blackout-check-runner:latest` image. It contains Node for the checkout
action plus Python, Git, timezone data, application dependencies, pytest, and
Ruff. Scheduled jobs therefore do not install Python or download Python packages.
`ubuntu-latest` label. The test and hourly workflows pull this private image from
the Gitea Container Registry:
Build the image on the Docker host used by `act_runner`:
```sh
cd /path/to/daily-blackout-check
docker build \
--tag daily-blackout-check-runner:latest \
runner-image
docker image inspect daily-blackout-check-runner:latest >/dev/null
```text
mahgit.ir/meghdadfadaee/daily-blackout-check-runner:latest
```
Only `runner-image/` is sent as the Docker build context, so local secrets and
outage state never enter the build context or image.
It contains Node for the checkout action plus Python, Git, timezone data,
application dependencies, pytest, and Ruff. Scheduled jobs therefore do not
install Python or download Python packages.
The image is local rather than registry-hosted, matching the deployment pattern
used by `mahak-api-docs`. Build it on every runner host that can claim this job.
Rebuild it whenever `runner-image/Dockerfile`, `runner-image/requirements.lock`,
or dependency declarations in `pyproject.toml` change. The image build requires
Docker Hub and PyPI access; normal workflow runs only need the Gitea instance,
### Registry credentials
Create a Gitea personal access token for `MeghdadFadaee` with package permission
set to **Read and Write**. Add these repository Actions secrets:
| Secret | Value |
| --- | --- |
| `REGISTRY_USERNAME` | Gitea username that owns the package |
| `REGISTRY_TOKEN` | Personal access token with package Read and Write permission |
| `BARGHEMAN_TOKEN` | SAAPA bearer token |
| `EITAAYAR_TOKEN` | EitaaYar bot token |
| `CHAT_ID` | Destination chat ID |
| `BILL_IDS` | Comma-separated bill IDs |
The registry token is used to publish the image and as `container.credentials`
when Gitea Runner pulls the private image before starting a job.
### Runner configuration
The image-build workflow needs access to the Docker daemon used by `act_runner`.
In the runner's generated `config.yaml`, update the existing `container` section:
```yaml
container:
force_pull: true
valid_volumes:
- /var/run/docker.sock
```
The runner container itself must already mount the same socket, as in the normal
Docker-based `act_runner` setup. Restart the runner after changing its config.
Docker socket access is equivalent to control of the Docker host. Use a trusted,
preferably repository-level runner. The build workflow runs only for changes on
`main` under `runner-image/` or its own workflow file; it never runs for pull
requests.
### Image publishing flow
The **Build runner image** workflow:
1. Starts in `node:20-bookworm` and installs the Docker client.
2. Builds using only `runner-image/` as context, so `.env` and outage state are
never included.
3. Runs import, pytest, and Ruff smoke checks inside the candidate image.
4. Pushes an immutable tag using the first 12 characters of the Git commit.
5. Updates `latest` only after the candidate passes verification.
It runs automatically when `runner-image/**` changes on `main` and can also be
started manually. Image builds require Debian mirrors, Docker Hub, PyPI, Gitea,
and access to the Docker socket. Normal test and hourly runs only need Gitea,
`gitea.com`, SAAPA, and EitaaYar.
1. Build `daily-blackout-check-runner:latest` on the runner's Docker host.
2. Enable Actions in the repository settings.
3. Confirm an online Docker runner advertises `ubuntu-latest`.
4. Add these repository Actions secrets:
`BARGHEMAN_TOKEN`, `EITAAYAR_TOKEN`, `CHAT_ID`, and `BILL_IDS`.
5. Run the test workflow manually.
Deployment order:
1. Enable Packages and Actions for the repository/owner.
2. Configure the runner socket permission and restart it.
3. Add all repository Actions secrets listed above.
4. Run **Build runner image** manually and confirm both registry tags appear.
5. Run **Test blackout notifier** manually.
6. Run **Hourly blackout check** manually once and inspect the notification and
resulting state commit.
7. Leave the `@hourly` schedule enabled.
@@ -104,11 +144,6 @@ Its checkout credential must retain the default ability to push to the current
repository. Scheduled jobs should not be manually overlapped; in the rare event
of a push race, the job fails and unsaved notices may be repeated on the next run.
If a workflow tries to pull the local image instead of using it, ensure the image
exists in the same Docker daemon used by `act_runner` and set
`container.force_pull: false` in the runner's generated `config.yaml`, then
restart the runner.
## Development
```sh