From 308ed80ff4e2fa4f0ded10f494cb07d17da67b4d Mon Sep 17 00:00:00 2001 From: Meghdad <61308304+MeghdadFadaee@users.noreply.github.com> Date: Fri, 17 Jul 2026 20:22:52 +0330 Subject: [PATCH] Publish runner image to Gitea Container Registry --- .gitea/workflows/build-runner-image.yml | 73 +++++++++++++++++++ .gitea/workflows/check.yml | 5 +- .gitea/workflows/test.yml | 5 +- README.md | 95 +++++++++++++++++-------- 4 files changed, 146 insertions(+), 32 deletions(-) create mode 100644 .gitea/workflows/build-runner-image.yml diff --git a/.gitea/workflows/build-runner-image.yml b/.gitea/workflows/build-runner-image.yml new file mode 100644 index 0000000..a27ce06 --- /dev/null +++ b/.gitea/workflows/build-runner-image.yml @@ -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" diff --git a/.gitea/workflows/check.yml b/.gitea/workflows/check.yml index c1f0605..393a95b 100644 --- a/.gitea/workflows/check.yml +++ b/.gitea/workflows/check.yml @@ -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: diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 07d3ee1..a5e4fba 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -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: diff --git a/README.md b/README.md index 2b2e0db..5a60e0d 100644 --- a/README.md +++ b/README.md @@ -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