From 662053d5927e8cf63654309c0e156f8809154459 Mon Sep 17 00:00:00 2001 From: mr-forust Date: Sat, 27 Jun 2026 23:06:45 +0200 Subject: [PATCH] Combine CI and publish workflows --- .gitea/workflows/ci.yaml | 161 +++++++++++++++++++++++++++++++++ .gitea/workflows/publish.yaml | 64 ------------- .github/workflows/ci.yaml | 161 +++++++++++++++++++++++++++++++++ .github/workflows/publish.yaml | 64 ------------- compose.yaml | 14 ++- k8s/telegram-scraper.yaml | 8 +- 6 files changed, 337 insertions(+), 135 deletions(-) create mode 100644 .gitea/workflows/ci.yaml delete mode 100644 .gitea/workflows/publish.yaml create mode 100644 .github/workflows/ci.yaml delete mode 100644 .github/workflows/publish.yaml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..69e4375 --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,161 @@ +name: ci + +on: + push: + branches: + - "**" + pull_request: + workflow_dispatch: + +env: + REGISTRY: gcr.forust.xyz + IMAGE_NAME: forust/telegram-scraper + +jobs: + lint: + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Check formatting with Prettier + shell: bash + run: | + mapfile -t prettier_files < <( + git ls-files \ + | grep -E '\.(md|json|ya?ml|html|css)$' \ + | grep -Ev '^(\.docs/|\.zed/|errorpages/html/|homepages/(forust_files|xdfnx_files)/)' + ) + + if [ "${#prettier_files[@]}" -eq 0 ]; then + echo "No Prettier-managed files found." + exit 0 + fi + + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + node:22-alpine \ + sh -lc 'npx --yes prettier@3 --check --ignore-unknown "$@"' sh "${prettier_files[@]}" + + - name: Lint Python with Ruff + shell: bash + run: | + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + ghcr.io/astral-sh/ruff:latest \ + check . + + - name: Lint YAML syntax + shell: bash + run: | + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + cytopia/yamllint:latest \ + -c .yamllint . + + - name: Lint Dockerfiles + shell: bash + run: | + mapfile -t dockerfiles < <( + git ls-files ':(glob)**/Dockerfile' ':(glob)**/Dockerfile.*' + ) + + if [ "${#dockerfiles[@]}" -eq 0 ]; then + echo "No Dockerfiles found." + exit 0 + fi + + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + --entrypoint hadolint \ + hadolint/hadolint:latest-debian \ + -c .hadolint.yaml "${dockerfiles[@]}" + + validate: + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Lint YAML syntax + shell: bash + run: | + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + cytopia/yamllint:latest \ + -c .yamllint . + + - name: Validate Kubernetes manifests + shell: bash + run: | + mapfile -t manifests < <( + git ls-files ':(glob)**/k8s/**/*.yaml' ':(glob)**/k8s/**/*.yml' \ + | grep -Ev '(^|/)(kustomization\.ya?ml|.*\.example\.ya?ml|.*values\.ya?ml|patch-.*\.ya?ml)$' + ) + + if [ "${#manifests[@]}" -eq 0 ]; then + echo "No Kubernetes manifests found." + exit 0 + fi + + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + ghcr.io/yannh/kubeconform:latest \ + -strict \ + -ignore-missing-schemas \ + -summary \ + "${manifests[@]}" + + publish: + needs: [lint, validate] + if: github.event_name != 'pull_request' + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Read project version + id: version + shell: bash + run: | + version="$(python3 -c 'from pathlib import Path; import tomllib; print(tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"])')" + echo "version=$version" >> "$GITHUB_OUTPUT" + + - name: Log in to registry + shell: bash + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \ + -u "${{ secrets.REGISTRY_USERNAME }}" \ + --password-stdin + + - name: Build and push image + shell: bash + run: | + image="${REGISTRY}/${IMAGE_NAME}" + tags=("latest" "${{ steps.version.outputs.version }}") + + case "${GITHUB_REF_NAME}" in + main) + tags+=("main" "prod") + ;; + dev) + tags+=("dev") + ;; + esac + + build_args=() + for tag in "${tags[@]}"; do + build_args+=(-t "${image}:${tag}") + done + + docker build "${build_args[@]}" . + + for tag in "${tags[@]}"; do + docker push "${image}:${tag}" + done diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml deleted file mode 100644 index a504ab4..0000000 --- a/.gitea/workflows/publish.yaml +++ /dev/null @@ -1,64 +0,0 @@ -name: publish - -on: - push: - branches: - - "**" - workflow_dispatch: - -env: - REGISTRY: gcr.forust.xyz - IMAGE_NAME: forust/telegram-scraper - -jobs: - docker: - runs-on: [self-hosted, linux, arch, homelab] - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Read project version - id: version - shell: bash - run: | - version="$(python3 - <<'PY' -from pathlib import Path -import tomllib - -print(tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]) -PY -)" - echo "version=$version" >> "$GITHUB_OUTPUT" - - - name: Log in to registry - shell: bash - run: | - echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \ - -u "${{ secrets.REGISTRY_USERNAME }}" \ - --password-stdin - - - name: Build and push image - shell: bash - run: | - image="${REGISTRY}/${IMAGE_NAME}" - tags=("latest" "${{ steps.version.outputs.version }}") - - case "${GITHUB_REF_NAME}" in - main) - tags+=("main" "prod") - ;; - dev) - tags+=("dev") - ;; - esac - - build_args=() - for tag in "${tags[@]}"; do - build_args+=(-t "${image}:${tag}") - done - - docker build "${build_args[@]}" . - - for tag in "${tags[@]}"; do - docker push "${image}:${tag}" - done diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..69e4375 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,161 @@ +name: ci + +on: + push: + branches: + - "**" + pull_request: + workflow_dispatch: + +env: + REGISTRY: gcr.forust.xyz + IMAGE_NAME: forust/telegram-scraper + +jobs: + lint: + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Check formatting with Prettier + shell: bash + run: | + mapfile -t prettier_files < <( + git ls-files \ + | grep -E '\.(md|json|ya?ml|html|css)$' \ + | grep -Ev '^(\.docs/|\.zed/|errorpages/html/|homepages/(forust_files|xdfnx_files)/)' + ) + + if [ "${#prettier_files[@]}" -eq 0 ]; then + echo "No Prettier-managed files found." + exit 0 + fi + + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + node:22-alpine \ + sh -lc 'npx --yes prettier@3 --check --ignore-unknown "$@"' sh "${prettier_files[@]}" + + - name: Lint Python with Ruff + shell: bash + run: | + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + ghcr.io/astral-sh/ruff:latest \ + check . + + - name: Lint YAML syntax + shell: bash + run: | + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + cytopia/yamllint:latest \ + -c .yamllint . + + - name: Lint Dockerfiles + shell: bash + run: | + mapfile -t dockerfiles < <( + git ls-files ':(glob)**/Dockerfile' ':(glob)**/Dockerfile.*' + ) + + if [ "${#dockerfiles[@]}" -eq 0 ]; then + echo "No Dockerfiles found." + exit 0 + fi + + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + --entrypoint hadolint \ + hadolint/hadolint:latest-debian \ + -c .hadolint.yaml "${dockerfiles[@]}" + + validate: + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Lint YAML syntax + shell: bash + run: | + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + cytopia/yamllint:latest \ + -c .yamllint . + + - name: Validate Kubernetes manifests + shell: bash + run: | + mapfile -t manifests < <( + git ls-files ':(glob)**/k8s/**/*.yaml' ':(glob)**/k8s/**/*.yml' \ + | grep -Ev '(^|/)(kustomization\.ya?ml|.*\.example\.ya?ml|.*values\.ya?ml|patch-.*\.ya?ml)$' + ) + + if [ "${#manifests[@]}" -eq 0 ]; then + echo "No Kubernetes manifests found." + exit 0 + fi + + docker run --rm \ + -v "$PWD:/work" \ + -w /work \ + ghcr.io/yannh/kubeconform:latest \ + -strict \ + -ignore-missing-schemas \ + -summary \ + "${manifests[@]}" + + publish: + needs: [lint, validate] + if: github.event_name != 'pull_request' + runs-on: [self-hosted, linux, arch, homelab] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Read project version + id: version + shell: bash + run: | + version="$(python3 -c 'from pathlib import Path; import tomllib; print(tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"])')" + echo "version=$version" >> "$GITHUB_OUTPUT" + + - name: Log in to registry + shell: bash + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \ + -u "${{ secrets.REGISTRY_USERNAME }}" \ + --password-stdin + + - name: Build and push image + shell: bash + run: | + image="${REGISTRY}/${IMAGE_NAME}" + tags=("latest" "${{ steps.version.outputs.version }}") + + case "${GITHUB_REF_NAME}" in + main) + tags+=("main" "prod") + ;; + dev) + tags+=("dev") + ;; + esac + + build_args=() + for tag in "${tags[@]}"; do + build_args+=(-t "${image}:${tag}") + done + + docker build "${build_args[@]}" . + + for tag in "${tags[@]}"; do + docker push "${image}:${tag}" + done diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml deleted file mode 100644 index a504ab4..0000000 --- a/.github/workflows/publish.yaml +++ /dev/null @@ -1,64 +0,0 @@ -name: publish - -on: - push: - branches: - - "**" - workflow_dispatch: - -env: - REGISTRY: gcr.forust.xyz - IMAGE_NAME: forust/telegram-scraper - -jobs: - docker: - runs-on: [self-hosted, linux, arch, homelab] - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Read project version - id: version - shell: bash - run: | - version="$(python3 - <<'PY' -from pathlib import Path -import tomllib - -print(tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]) -PY -)" - echo "version=$version" >> "$GITHUB_OUTPUT" - - - name: Log in to registry - shell: bash - run: | - echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \ - -u "${{ secrets.REGISTRY_USERNAME }}" \ - --password-stdin - - - name: Build and push image - shell: bash - run: | - image="${REGISTRY}/${IMAGE_NAME}" - tags=("latest" "${{ steps.version.outputs.version }}") - - case "${GITHUB_REF_NAME}" in - main) - tags+=("main" "prod") - ;; - dev) - tags+=("dev") - ;; - esac - - build_args=() - for tag in "${tags[@]}"; do - build_args+=(-t "${image}:${tag}") - done - - docker build "${build_args[@]}" . - - for tag in "${tags[@]}"; do - docker push "${image}:${tag}" - done diff --git a/compose.yaml b/compose.yaml index e36b7ba..5575e39 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,13 +5,19 @@ services: dockerfile: Dockerfile container_name: telegram-scraper image: gcr.forust.xyz/forust/telegram-scraper:latest - user: 1000:1000 - restart: unless-stopped tty: true + user: "1000:1000" + restart: unless-stopped ports: - - "7887:8080" + - 7887:8080 healthcheck: - test: ["CMD", "python", "-c", "import http.client,json;c=http.client.HTTPConnection('localhost',8080);c.request('GET','/health');r=c.getresponse();exit(0)if json.loads(r.read()).get('ok')else exit(1)"] + test: + [ + CMD, + python, + -c, + import http.client,json;c=http.client.HTTPConnection('localhost',8080);c.request('GET','/health');r=c.getresponse();exit(0)if json.loads(r.read()).get('ok')else exit(1), + ] interval: 30s timeout: 10s retries: 3 diff --git a/k8s/telegram-scraper.yaml b/k8s/telegram-scraper.yaml index 3bfa56d..90bbe84 100644 --- a/k8s/telegram-scraper.yaml +++ b/k8s/telegram-scraper.yaml @@ -34,8 +34,8 @@ spec: containers: - name: telegram-scraper image: gcr.forust.xyz/forust/telegram-scraper:latest - tty: true stdin: true + tty: true ports: - containerPort: 8080 livenessProbe: @@ -59,14 +59,16 @@ spec: - metadata: name: telegram-scraper-data spec: - accessModes: ["ReadWriteOnce"] + accessModes: + - ReadWriteOnce resources: requests: storage: 20Gi - metadata: name: telegram-scraper-session spec: - accessModes: ["ReadWriteOnce"] + accessModes: + - ReadWriteOnce resources: requests: storage: 5Mi