refactor: remove EDU ownership from homelab
This commit is contained in:
1 parent
fb400eea6e
commit
1d93588e06
25 files changed
+56
-2706
No files matched your search
@@ -0,0 +1,55 @@
|
|||||||
|
# EDU ownership handoff
|
||||||
|
|
||||||
|
## Review findings
|
||||||
|
|
||||||
|
The current `main` branch can verify and roll back changed workloads across the cluster. This is unsafe when an external repository owns an application. Open PR #99 already changes this behavior to use selected workload references and immutable releases. This change is based on PR #99 branch `codex/ci-visible-checks` and keeps its protected check names:
|
||||||
|
|
||||||
|
- `ci / Compose*`
|
||||||
|
- `ci / Workflows*`
|
||||||
|
- `ci / Shell*`
|
||||||
|
- `ci / Formatting*`
|
||||||
|
- `ci / Python and tests*`
|
||||||
|
- `ci / YAML*`
|
||||||
|
- `ci / Dockerfiles*`
|
||||||
|
- `ci / Kubernetes*`
|
||||||
|
|
||||||
|
Open PR #100 adds service metrics. It is independent and is not required for this handoff.
|
||||||
|
|
||||||
|
Live Gitea 28.0.0 supports dynamic job outputs, matrices, and `max-parallel`. The runner has 1 CPU, 1.7 GiB RAM, and 6 GiB free disk. Its capacity details could not be verified because the diagnostic required a sudo password. Runner concurrency capacity remains unverified.
|
||||||
|
|
||||||
|
The workstation checkout `/srv/edu-master` exists, is clean at `7ed537f`, and uses the SSH remote `gitssh.forust.xyz:2221`. In Kubernetes context `Default`, namespace `edu-master`, the `session-keeper` and `webinar-checker` workloads are Ready. Their health and live endpoints return 200. Their running image digests match the digests in the old homelab configuration. The Redis PVC `redis-data-pvc` is bound to PV `pvc-a4f2a79a-363a-4c12-ae91-92cdfc2a0d2e`; its reclaim policy is `Delete`. Never delete, recreate, or apply this PVC.
|
||||||
|
|
||||||
|
The EDU repository still needs its live `playwright-service` manifest and reconciled auto-reloader annotations. Those changes, plus backup and monitoring verification improvements, are in the separate EDU branch `fix/handoff-runtime`.
|
||||||
|
|
||||||
|
## Implementation decisions
|
||||||
|
|
||||||
|
The homelab change removes the complete `edu_master` subtree, EDU build and deploy selection, image pinning, rollback and verification cases, route probes, related tests, and Renovate references. It removes the external EDU image bypass from generic image scans. Application source and release ownership: [forust/edu-master](https://git.forust.xyz/forust/edu-master).
|
||||||
|
|
||||||
|
The image plan compares fingerprints for all three homelab images with the last successful CI release. Each matrix job builds its image or reuses the matching immutable digest. The matrix runs one job at a time and continues after a job failure so each image has a visible result. The final build job waits for all image jobs, checks the commit, inputs, and digests, applies the full-SHA tags, and writes the existing release artifact format. This preserves the deploy gate. A manifest-only change still runs three reuse jobs and the final tag stage. Pull requests do not publish images.
|
||||||
|
|
||||||
|
Retain the protected check names from PR #99. Keep CI and deploy separate. `AUTODEPLOY=false` is configured explicitly. The EDU main-push deploy policy is independent of this setting.
|
||||||
|
|
||||||
|
## Dependencies and rollout order
|
||||||
|
|
||||||
|
1. Merge PR #99 first, because this change uses its selected-workload and immutable-release behavior. PR #100 is not a dependency.
|
||||||
|
2. Complete the EDU runtime reconciliation on the EDU feature branch. Do not merge EDU into `main` yet. Configure and verify the EDU deployment secrets and trusted SSH host key outside Git.
|
||||||
|
3. Confirm the EDU release can pass its CI and immutable-SHA deploy gate. Keep the existing namespace, Secret, Redis data, Fernet key, and runtime credentials.
|
||||||
|
4. Stop or drain pending homelab runs that can deploy EDU. Remove the EDU active marker from the authoritative homelab source before any later homelab deploy. Confirm that the removal path does not prune resources.
|
||||||
|
5. Merge the homelab removal. Then merge EDU PR #1 into `main` to start a successful main-push release and deployment.
|
||||||
|
6. Verify that homelab no longer selects EDU and that EDU is the sole owner. Check rollout health, `/health`, `/live`, Redis AUTH, session TTL, delivery backlog, and monitoring. Record the release SHA, image digests, downtime, and any remaining limits in the relevant PR.
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
For an EDU release failure, use the EDU release snapshot and reapply the last recorded immutable digests. Inspect workload and application health after recovery. Do not restore old Redis data unless recovery requires it.
|
||||||
|
|
||||||
|
To reverse the ownership handoff, stop EDU deployment triggers and runs first. Restore the reviewed homelab configuration and active marker only after EDU is inactive. Reapply the recorded image digests and verify workload health. Never enable both deployment paths at the same time. The PVC and its PV must remain intact.
|
||||||
|
|
||||||
|
## Verification status
|
||||||
|
|
||||||
|
Verified: Gitea version and matrix support; current runner CPU, memory, and free disk readings; clean EDU checkout and SSH remote; Kubernetes context and namespace; workload readiness and health endpoints; matching live image digests; and Redis PVC binding and reclaim policy.
|
||||||
|
|
||||||
|
Not verified: runner capacity limits, merged PR state, post-merge image release, EDU deployment, or final handoff acceptance. The merge and live deployment steps remain pending. Do not report the handoff as complete until the live checks above pass.
|
||||||
|
|
||||||
|
Live pre-handoff checks also passed: Redis AUTH (`NOAUTH` without credentials and `PONG` with them), positive session TTL, zero delivery backlog, and nine EDU vmalert rules with matching expressions and healthy evaluation. The private snapshot is `/home/forust/.local/state/edu-master-deploy/handoff-20261007T080838Z` on workstation. It includes the Redis RDB, Secret, manifests, source files, and checkout commits. Workloads have not been redeployed.
|
||||||
|
|
||||||
|
The Redis RDB checksum passed with twelve keys. The unauthorized Redis pod test passed and the pod was removed. VictoriaMetrics reported the EDU scrape target UP. EDU Actions has the dedicated path and port secrets and `EDU_KUBE_CONTEXT=Default`. Existing deployment and registry credentials were retained.
|
||||||
@@ -67,8 +67,7 @@ The deploy user's existing Docker registry authentication remains necessary.
|
|||||||
|
|
||||||
CI publishes `release-<full SHA>` as a Gitea artifact with all three owned image
|
CI publishes `release-<full SHA>` as a Gitea artifact with all three owned image
|
||||||
digests and build input fingerprints. Unchanged images are reused only from a
|
digests and build input fingerprints. Unchanged images are reused only from a
|
||||||
successful main CI artifact, never from `:prod`. EDU images remain pinned to the
|
successful main CI artifact, never from `:prod`. Expired artifacts cause CI to
|
||||||
digests released by their application repository. Expired artifacts cause CI to
|
|
||||||
rebuild images; they block deployment until CI is rerun.
|
rebuild images; they block deployment until CI is rerun.
|
||||||
|
|
||||||
Run deploy from main with `deploy_ref=main` or a checked SHA:
|
Run deploy from main with `deploy_ref=main` or a checked SHA:
|
||||||
|
|||||||
@@ -94,7 +94,6 @@ replacements.txt
|
|||||||
.idea
|
.idea
|
||||||
|
|
||||||
# Temp files
|
# Temp files
|
||||||
edu_master/temp/
|
|
||||||
temp/*
|
temp/*
|
||||||
# Local-only tooling scratch space (pinned CI tools, verification scripts)
|
# Local-only tooling scratch space (pinned CI tools, verification scripts)
|
||||||
tmp/
|
tmp/
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
EDU_LOGIN=your_edu_login_here
|
|
||||||
EDU_PASSWORD=your_edu_password_here
|
|
||||||
EDU_URL_LOGIN=https://edu.edu.vn.ua/user/login
|
|
||||||
EDU_URL_VERIFY=https://edu.edu.vn.ua/course/userlist
|
|
||||||
PHPSESSID_INTERVAL=10
|
|
||||||
USER_AGENT="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
|
|
||||||
WEBINAR_URL=https://edu.edu.vn.ua/webinar/useractive
|
|
||||||
WEBINAR_CHECK_INTERVAL=60
|
|
||||||
REDIS_HOST=redis
|
|
||||||
REDIS_PORT=6379
|
|
||||||
PLAYWRIGHT_WS=ws://playwright-service:3000/ws
|
|
||||||
TZ=Europe/Kyiv
|
|
||||||
WEBINAR_TELEGRAM_TOKEN=your_telegram_bot_token_here
|
|
||||||
WEBINAR_ADMIN_ID=123456789
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
1.56.0
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# EDU deployment ownership
|
|
||||||
|
|
||||||
Application source and release builds: `forust/edu-master`.
|
|
||||||
The homelab pipeline deploys `edu_master/k8s` and preserves explicit image digests.
|
|
||||||
The application copies in this directory are legacy and are not build inputs.
|
|
||||||
Do not publish EDU `prod` images from homelab or resolve releases from moving tags.
|
|
||||||
|
|
||||||
For an EDU release, validate both images, select their digests in the keeper and
|
|
||||||
checker manifests, and run the existing homelab validation/apply/verification
|
|
||||||
helpers against this service. Keep the existing Secret and Redis PVC.
|
|
||||||
Coordinate Redis authentication changes with both clients and all init/probes;
|
|
||||||
keep a pre-rollout Redis backup and both previous compatible image references.
|
|
||||||
The current HTTP checker does not depend on Playwright; check other consumers
|
|
||||||
before removing the separate browser service.
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
services:
|
|
||||||
redis:
|
|
||||||
image: redis:8.10.2-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- redis-data:/data
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "redis-cli", "ping"]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
playwright-service:
|
|
||||||
image: mcr.microsoft.com/playwright:v1.56.0-jammy
|
|
||||||
restart: unless-stopped
|
|
||||||
command: npx -y playwright@1.56.0 run-server --port 3000 --path /ws
|
|
||||||
|
|
||||||
session-keeper:
|
|
||||||
build: ./phpsessid-bot
|
|
||||||
image: gcr.forust.xyz/forust/session-keeper:prod
|
|
||||||
pull_policy: build
|
|
||||||
env_file: .env
|
|
||||||
restart: unless-stopped
|
|
||||||
depends_on:
|
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "redis-cli -h redis EXISTS EDU_PHPSESSID | grep -q 1"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 10
|
|
||||||
start_period: 60s
|
|
||||||
|
|
||||||
webinar-checker:
|
|
||||||
build: ./webinar-checker
|
|
||||||
image: gcr.forust.xyz/forust/webinar-checker:prod
|
|
||||||
pull_policy: build
|
|
||||||
env_file: .env
|
|
||||||
restart: unless-stopped
|
|
||||||
depends_on:
|
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
session-keeper:
|
|
||||||
condition: service_healthy
|
|
||||||
playwright-service:
|
|
||||||
condition: service_started
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
redis-data:
|
|
||||||
Whitespace-only changes.
@@ -1,115 +0,0 @@
|
|||||||
apiVersion: monitoring.coreos.com/v1
|
|
||||||
kind: PrometheusRule
|
|
||||||
metadata:
|
|
||||||
name: edu-master-webinar
|
|
||||||
namespace: edu-master
|
|
||||||
labels:
|
|
||||||
release: prometheus-stack
|
|
||||||
spec:
|
|
||||||
groups:
|
|
||||||
- name: edu_master.webinar
|
|
||||||
rules:
|
|
||||||
# No successful webinar check for 5m (~2-3 missed 2-min checks).
|
|
||||||
# Catches: playwright hangs/timeouts, version skew, site changes, hung job.
|
|
||||||
# The last_success > 0 guard is mandatory: checker.py initialises
|
|
||||||
# last_success to 0, so without it `time() - 0` equals the current epoch
|
|
||||||
# and humanizeDuration renders ~20722d on every pod restart. Keep the
|
|
||||||
# duration expression on the left so $value stays the real gap.
|
|
||||||
- alert: WebinarCheckerNoSuccessfulCheck
|
|
||||||
expr: |
|
|
||||||
((time() - webinar_check_last_success_timestamp_seconds) > 300)
|
|
||||||
and (webinar_check_last_success_timestamp_seconds > 0)
|
|
||||||
and (webinar_check_last_run_timestamp_seconds > 0)
|
|
||||||
for: 2m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Webinar checker has no successful check for 5m"
|
|
||||||
description: "edu-master/webinar-checker: last successful webinar check was {{ $value | humanizeDuration }} ago. Checks are failing or hanging (see consecutive failures alert). Notifications about new webinars are NOT being sent."
|
|
||||||
|
|
||||||
# Checks are running but none has ever succeeded since pod start.
|
|
||||||
# Split out from the rule above so a zeroed gauge never feeds
|
|
||||||
# humanizeDuration.
|
|
||||||
- alert: WebinarCheckerNeverSucceeded
|
|
||||||
expr: |
|
|
||||||
(webinar_check_last_success_timestamp_seconds == 0)
|
|
||||||
and (webinar_check_last_run_timestamp_seconds > 0)
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Webinar checker has never completed a successful check"
|
|
||||||
description: 'edu-master/webinar-checker: checks have been running for 10m but not one has ever succeeded since the pod started, so every check is failing. Check pod logs (Loki: {namespace="edu-master", container="webinar-checker"}).'
|
|
||||||
|
|
||||||
# Fast path: 3 consecutive failures (~6+ min at 2-min interval).
|
|
||||||
- alert: WebinarCheckerConsecutiveFailures
|
|
||||||
expr: |
|
|
||||||
webinar_check_consecutive_failures >= 3
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Webinar checker failing consecutively"
|
|
||||||
description: 'edu-master/webinar-checker: {{ $value }} consecutive webinar check failures (timeout / http error / page error). Check pod logs (Loki: {namespace="edu-master", container="webinar-checker"}).'
|
|
||||||
|
|
||||||
- alert: WebinarCheckerNeverStarted
|
|
||||||
expr: |
|
|
||||||
(time() - edu_process_start > 120)
|
|
||||||
and (webinar_check_last_run_timestamp_seconds == 0)
|
|
||||||
for: 2m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Webinar checker job has not started"
|
|
||||||
description: "The process exposes metrics but its webinar job has never started."
|
|
||||||
|
|
||||||
- alert: WebinarDeliveryPending
|
|
||||||
expr: edu_delivery_pending > 0
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
annotations:
|
|
||||||
summary: "Webinar notifications await delivery"
|
|
||||||
description: "Telegram delivery has pending recipients. Check delivery failures and retry status."
|
|
||||||
|
|
||||||
- alert: EduRedisUnavailable
|
|
||||||
expr: edu_redis_connected == 0
|
|
||||||
for: 2m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "EDU checker cannot reach Redis"
|
|
||||||
description: "Redis health checks are failing; checker commands and delivery may be unavailable."
|
|
||||||
|
|
||||||
# Metrics endpoint not scraped for 10m: pod down, metrics server dead, or ServiceMonitor broken.
|
|
||||||
- alert: WebinarCheckerScrapeDown
|
|
||||||
expr: |
|
|
||||||
absent(webinar_check_last_run_timestamp_seconds) == 1
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Webinar checker metrics missing"
|
|
||||||
description: "edu-master/webinar-checker: no metrics series for 10m. Pod may be down, metrics server dead, or ServiceMonitor/Service broken. Webinar checks are unobserved."
|
|
||||||
|
|
||||||
# EDU session lost: session-keeper down or credentials expired. Without PHPSESSID every check is skipped.
|
|
||||||
- alert: EduPhpsessidMissing
|
|
||||||
expr: |
|
|
||||||
edu_phpsessid_present == 0
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "EDU_PHPSESSID missing"
|
|
||||||
description: "edu-master: EDU_PHPSESSID absent from redis for 10m. Webinar/diari/schedule checks are all skipped. Check session-keeper logs and EDU credentials."
|
|
||||||
|
|
||||||
# Hard deps: checker deployment unavailable.
|
|
||||||
- alert: WebinarCheckerDeploymentDown
|
|
||||||
expr: |
|
|
||||||
kube_deployment_status_replicas_unavailable{deployment="webinar-checker", namespace="edu-master"} > 0
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Webinar checker deployment unavailable"
|
|
||||||
description: "edu-master/webinar-checker deployment has {{ $value }} unavailable replica(s) for 10m."
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: edu-master
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: playwright-service
|
|
||||||
namespace: edu-master
|
|
||||||
labels:
|
|
||||||
app: edu-master-playwright
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: edu-master-playwright
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: edu-master-playwright
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: playwright
|
|
||||||
# renovate: datasource=docker depName=mcr.microsoft.com/playwright versioning=docker
|
|
||||||
image: mcr.microsoft.com/playwright:v1.56.0-jammy
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
# p95 412M, max 478M over 7 days, no limit before. Request is set at p95
|
|
||||||
# so the pod is not an eviction candidate; the limit stays above 2x the
|
|
||||||
# request because browser page lifetimes are unpredictable.
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: "200m"
|
|
||||||
memory: "416Mi"
|
|
||||||
limits:
|
|
||||||
memory: "1Gi"
|
|
||||||
command:
|
|
||||||
- npx
|
|
||||||
- -y
|
|
||||||
- playwright@1.56.0
|
|
||||||
- run-server
|
|
||||||
- --port
|
|
||||||
- "3000"
|
|
||||||
- --path
|
|
||||||
- /ws
|
|
||||||
ports:
|
|
||||||
- containerPort: 3000
|
|
||||||
readinessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 3000
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 3
|
|
||||||
livenessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 3000
|
|
||||||
initialDelaySeconds: 15
|
|
||||||
periodSeconds: 20
|
|
||||||
timeoutSeconds: 3
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: playwright-service
|
|
||||||
namespace: edu-master
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: edu-master-playwright
|
|
||||||
ports:
|
|
||||||
- name: ws
|
|
||||||
port: 3000
|
|
||||||
targetPort: 3000
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: NetworkPolicy
|
|
||||||
metadata:
|
|
||||||
name: redis-clients-only
|
|
||||||
namespace: edu-master
|
|
||||||
spec:
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app: edu-master-redis
|
|
||||||
policyTypes:
|
|
||||||
- Ingress
|
|
||||||
ingress:
|
|
||||||
- from:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app: edu-master-session-keeper
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app: edu-master-webinar-checker
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 6379
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: StatefulSet
|
|
||||||
metadata:
|
|
||||||
name: redis
|
|
||||||
namespace: edu-master
|
|
||||||
labels:
|
|
||||||
app: edu-master-redis
|
|
||||||
spec:
|
|
||||||
serviceName: redis
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: edu-master-redis
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: edu-master-redis
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: redis
|
|
||||||
image: redis:8.10.2-alpine
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
env:
|
|
||||||
- name: REDIS_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: edu-master-secrets
|
|
||||||
key: REDIS_PASSWORD
|
|
||||||
- name: REDISCLI_AUTH
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: edu-master-secrets
|
|
||||||
key: REDIS_PASSWORD
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -ec
|
|
||||||
- |
|
|
||||||
case "$REDIS_PASSWORD" in *[!0-9a-fA-F]*|'') echo 'REDIS_PASSWORD must be 64 hex characters' >&2; exit 1;; esac
|
|
||||||
[ "${#REDIS_PASSWORD}" -eq 64 ] || { echo 'REDIS_PASSWORD must be 64 hex characters' >&2; exit 1; }
|
|
||||||
umask 077
|
|
||||||
printf 'requirepass "%s"\n' "$REDIS_PASSWORD" > /tmp/redis-auth.conf
|
|
||||||
chown redis:redis /tmp/redis-auth.conf
|
|
||||||
exec docker-entrypoint.sh redis-server /tmp/redis-auth.conf
|
|
||||||
ports:
|
|
||||||
- containerPort: 6379
|
|
||||||
volumeMounts:
|
|
||||||
- name: redis-data
|
|
||||||
mountPath: /data
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 25m
|
|
||||||
memory: 32Mi
|
|
||||||
limits:
|
|
||||||
cpu: 250m
|
|
||||||
memory: 128Mi
|
|
||||||
readinessProbe:
|
|
||||||
exec:
|
|
||||||
command: ["redis-cli", "ping"]
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 5
|
|
||||||
timeoutSeconds: 3
|
|
||||||
livenessProbe:
|
|
||||||
exec:
|
|
||||||
command: ["redis-cli", "ping"]
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 3
|
|
||||||
volumes:
|
|
||||||
- name: redis-data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: redis-data-pvc
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: redis-data-pvc
|
|
||||||
namespace: edu-master
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 1Gi
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: redis
|
|
||||||
namespace: edu-master
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: edu-master-redis
|
|
||||||
ports:
|
|
||||||
- name: redis
|
|
||||||
port: 6379
|
|
||||||
targetPort: 6379
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# One-time Job to migrate redis state from docker compose to k8s (maintenance window).
|
|
||||||
# The .example file is not applied by the deploy pipeline (mask *.example.yaml).
|
|
||||||
#
|
|
||||||
# Runbook:
|
|
||||||
# 1. docker compose -f <repo>/edu_master/compose.yaml stop # SIGTERM -> redis will flush dump.rdb
|
|
||||||
# 2. docker run --rm -v edu_master_redis-data:/data \
|
|
||||||
# -v /tmp/edu-master-backup:/backup \
|
|
||||||
# redis:alpine sh -c "cp /data/dump.rdb /backup/ && ls -la /backup"
|
|
||||||
# 3. kubectl apply -f edu_master/k8s/namespace.yaml
|
|
||||||
# 4. kubectl apply -f <only the PVC from redis.yaml> # seed must come BEFORE redis pod starts
|
|
||||||
# 5. kubectl apply -f edu_master/k8s/restore-seed-job.yaml.example
|
|
||||||
# kubectl wait --for=condition=complete job/redis-restore-seed -n edu-master --timeout=120s
|
|
||||||
# 6. kubectl delete job redis-restore-seed -n edu-master
|
|
||||||
# 7. kubectl apply -f edu_master/k8s/ -R # apply remaining manifests
|
|
||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: redis-restore-seed
|
|
||||||
namespace: edu-master
|
|
||||||
spec:
|
|
||||||
backoffLimit: 2
|
|
||||||
ttlSecondsAfterFinished: 3600
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
restartPolicy: Never
|
|
||||||
containers:
|
|
||||||
- name: seed
|
|
||||||
image: redis:alpine
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -ec
|
|
||||||
- |
|
|
||||||
ls -la /backup
|
|
||||||
cp /backup/dump.rdb /data/dump.rdb
|
|
||||||
chmod 644 /data/dump.rdb
|
|
||||||
ls -la /data
|
|
||||||
volumeMounts:
|
|
||||||
- name: redis-data
|
|
||||||
mountPath: /data
|
|
||||||
- name: backup
|
|
||||||
mountPath: /backup
|
|
||||||
readOnly: true
|
|
||||||
volumes:
|
|
||||||
- name: redis-data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: redis-data-pvc
|
|
||||||
- name: backup
|
|
||||||
hostPath:
|
|
||||||
path: /tmp/edu-master-backup
|
|
||||||
type: DirectoryOrCreate
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: edu-master-secrets
|
|
||||||
namespace: edu-master
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
# Session keeper credentials
|
|
||||||
KEEPER_LOGIN: ""
|
|
||||||
KEEPER_PASSWORD: ""
|
|
||||||
KEEPER_INTERVAL: "10"
|
|
||||||
# EDU links
|
|
||||||
EDU_URL_BASE: "https://edu.edu.vn.ua"
|
|
||||||
EDU_URL_LOGIN: "/user/login"
|
|
||||||
EDU_URL_COURSES: "/course/userlist"
|
|
||||||
EDU_URL_WEBINAR: "/webinar/useractive"
|
|
||||||
# Playwright
|
|
||||||
USER_AGENT: ""
|
|
||||||
PLAYWRIGHT_WS: "ws://playwright-service:3000/ws"
|
|
||||||
# Webinar-checker
|
|
||||||
WEBINAR_TELEGRAM_TOKEN: ""
|
|
||||||
WEBINAR_ADMIN_ID: ""
|
|
||||||
WEBINAR_CHECK_INTERVAL: "60"
|
|
||||||
# Prometheus metrics endpoint (scraped via ServiceMonitor, alerts in k8s/alerts.yaml)
|
|
||||||
METRICS_PORT: "8000"
|
|
||||||
# Database
|
|
||||||
REDIS_HOST: "redis"
|
|
||||||
REDIS_PORT: "6379"
|
|
||||||
TZ: "Europe/Kyiv"
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: webinar-checker
|
|
||||||
namespace: edu-master
|
|
||||||
labels:
|
|
||||||
app: edu-master-webinar-checker
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: edu-master-webinar-checker
|
|
||||||
ports:
|
|
||||||
- name: metrics
|
|
||||||
port: 8000
|
|
||||||
targetPort: metrics
|
|
||||||
protocol: TCP
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
apiVersion: monitoring.coreos.com/v1
|
|
||||||
kind: ServiceMonitor
|
|
||||||
metadata:
|
|
||||||
name: webinar-checker
|
|
||||||
namespace: edu-master
|
|
||||||
labels:
|
|
||||||
release: prometheus-stack
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: edu-master-webinar-checker
|
|
||||||
endpoints:
|
|
||||||
- port: metrics
|
|
||||||
path: /metrics
|
|
||||||
interval: 30s
|
|
||||||
scrapeTimeout: 10s
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
reloader.stakater.com/auto: "true"
|
|
||||||
name: session-keeper
|
|
||||||
namespace: edu-master
|
|
||||||
labels:
|
|
||||||
app: edu-master-session-keeper
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: edu-master-session-keeper
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
edu.forust.xyz/source-commit: "90829d6c8080b9928f9da23587678e640939e10a"
|
|
||||||
labels:
|
|
||||||
app: edu-master-session-keeper
|
|
||||||
spec:
|
|
||||||
initContainers:
|
|
||||||
- name: wait-redis
|
|
||||||
image: redis:8.10.2-alpine
|
|
||||||
env:
|
|
||||||
- name: REDISCLI_AUTH
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: edu-master-secrets
|
|
||||||
key: REDIS_PASSWORD
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -ec
|
|
||||||
- |
|
|
||||||
i=0
|
|
||||||
until redis-cli -h redis ping | grep -q PONG; do
|
|
||||||
i=$((i+1))
|
|
||||||
[ "$i" -ge 300 ] && echo "TIMEOUT: redis not ready" && exit 1
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
echo "redis is ready"
|
|
||||||
containers:
|
|
||||||
- name: session-keeper
|
|
||||||
image: gcr.forust.xyz/forust/session-keeper@sha256:49285e87cc5bc4cf4ffe190813d87927916c2df8a206daac0aeb7d227c636450
|
|
||||||
envFrom:
|
|
||||||
- secretRef:
|
|
||||||
name: edu-master-secrets
|
|
||||||
env:
|
|
||||||
- name: REDISCLI_AUTH
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: edu-master-secrets
|
|
||||||
key: REDIS_PASSWORD
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 25m
|
|
||||||
memory: 32Mi
|
|
||||||
limits:
|
|
||||||
cpu: 250m
|
|
||||||
memory: 128Mi
|
|
||||||
readinessProbe:
|
|
||||||
exec:
|
|
||||||
command: ["/bin/sh", "-ec", "redis-cli -h redis EXISTS EDU_PHPSESSID | grep -q 1"]
|
|
||||||
initialDelaySeconds: 15
|
|
||||||
periodSeconds: 30
|
|
||||||
timeoutSeconds: 5
|
|
||||||
failureThreshold: 10
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
reloader.stakater.com/auto: "true"
|
|
||||||
name: webinar-checker
|
|
||||||
namespace: edu-master
|
|
||||||
labels:
|
|
||||||
app: edu-master-webinar-checker
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: edu-master-webinar-checker
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
edu.forust.xyz/source-commit: "90829d6c8080b9928f9da23587678e640939e10a"
|
|
||||||
labels:
|
|
||||||
app: edu-master-webinar-checker
|
|
||||||
spec:
|
|
||||||
# Enforces dependency order like compose depends_on:
|
|
||||||
# redis healthy -> session-keeper healthy (EXISTS EDU_PHPSESSID)
|
|
||||||
initContainers:
|
|
||||||
- name: wait-deps
|
|
||||||
image: redis:8.10.2-alpine
|
|
||||||
env:
|
|
||||||
- name: REDISCLI_AUTH
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: edu-master-secrets
|
|
||||||
key: REDIS_PASSWORD
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -ec
|
|
||||||
- |
|
|
||||||
i=0
|
|
||||||
until redis-cli -h redis ping | grep -q PONG; do
|
|
||||||
i=$((i+1))
|
|
||||||
[ "$i" -ge 300 ] && echo "TIMEOUT: redis not ready" && exit 1
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
echo "redis ok"
|
|
||||||
until [ "$(redis-cli -h redis EXISTS EDU_PHPSESSID)" = "1" ]; do
|
|
||||||
i=$((i+1))
|
|
||||||
[ "$i" -ge 300 ] && echo "TIMEOUT: no PHPSESSID (session-keeper down?)" && exit 1
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
echo "PHPSESSID ok"
|
|
||||||
containers:
|
|
||||||
- name: webinar-checker
|
|
||||||
image: gcr.forust.xyz/forust/webinar-checker@sha256:66c146f7b43cb9f0dc31ba9aa36d217e01df42ddafba5971b79c12ec215b2c01
|
|
||||||
ports:
|
|
||||||
- name: metrics
|
|
||||||
containerPort: 8000
|
|
||||||
protocol: TCP
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: metrics
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 3
|
|
||||||
failureThreshold: 12
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /live
|
|
||||||
port: metrics
|
|
||||||
initialDelaySeconds: 60
|
|
||||||
periodSeconds: 15
|
|
||||||
timeoutSeconds: 3
|
|
||||||
failureThreshold: 4
|
|
||||||
envFrom:
|
|
||||||
- secretRef:
|
|
||||||
name: edu-master-secrets
|
|
||||||
env:
|
|
||||||
- name: TZ
|
|
||||||
value: "Europe/Kyiv"
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: "50m"
|
|
||||||
memory: "192Mi"
|
|
||||||
limits:
|
|
||||||
cpu: "600m"
|
|
||||||
memory: "384Mi"
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
FROM python:3.14-slim
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends redis-tools && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
RUN pip install --no-cache-dir requests==2.32.3 redis==5.2.1
|
|
||||||
|
|
||||||
# Copy application code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Run the bot
|
|
||||||
CMD ["python", "bot.py"]
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
import logging
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import redis
|
|
||||||
import requests
|
|
||||||
|
|
||||||
# Configure logging
|
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
# Load configuration (adapted to .env keys)
|
|
||||||
def _env(key, default=None):
|
|
||||||
v = os.getenv(key, default)
|
|
||||||
if isinstance(v, str) and len(v) >= 2 and ((v[0] == '"' and v[-1] == '"') or (v[0] == "'" and v[-1] == "'")):
|
|
||||||
return v[1:-1]
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
LOGIN = _env('KEEPER_LOGIN')
|
|
||||||
PASSWORD = _env('KEEPER_PASSWORD')
|
|
||||||
|
|
||||||
EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua')
|
|
||||||
EDU_LOGIN_PATH = _env('EDU_URL_LOGIN', '/user/login')
|
|
||||||
EDU_COURSES_PATH = _env('EDU_URL_COURSES', '/course/userlist')
|
|
||||||
URL_LOGIN = f'{EDU_BASE.rstrip("/")}/{EDU_LOGIN_PATH.lstrip("/")}'
|
|
||||||
URL_VERIFY = f'{EDU_BASE.rstrip("/")}/{EDU_COURSES_PATH.lstrip("/")}'
|
|
||||||
|
|
||||||
INTERVAL = int(_env('KEEPER_INTERVAL', 10))
|
|
||||||
USER_AGENT = _env(
|
|
||||||
'USER_AGENT',
|
|
||||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',
|
|
||||||
)
|
|
||||||
REDIS_HOST = _env('REDIS_HOST', 'redis')
|
|
||||||
REDIS_PORT = int(_env('REDIS_PORT', 6379))
|
|
||||||
|
|
||||||
SUCCESS_FILE = '/tmp/last_success' # noqa: S108
|
|
||||||
|
|
||||||
|
|
||||||
def touch_success_file():
|
|
||||||
"""Updates the timestamp of the success file for healthchecks."""
|
|
||||||
try:
|
|
||||||
with open(SUCCESS_FILE, 'w') as f:
|
|
||||||
f.write(str(datetime.now().timestamp()))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'Failed to touch success file: {e}')
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
logger.info('Starting Session Keeper Bot')
|
|
||||||
|
|
||||||
# Connect to Redis
|
|
||||||
try:
|
|
||||||
redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)
|
|
||||||
redis_client.ping()
|
|
||||||
logger.info(f'Connected to Redis at {REDIS_HOST}:{REDIS_PORT}')
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'Failed to connect to Redis: {e}')
|
|
||||||
return
|
|
||||||
|
|
||||||
session = requests.Session()
|
|
||||||
|
|
||||||
# Set headers
|
|
||||||
headers = {
|
|
||||||
'User-Agent': USER_AGENT,
|
|
||||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
|
||||||
'Accept-Language': 'en-US,en;q=0.9',
|
|
||||||
'Cache-Control': 'max-age=0',
|
|
||||||
'Upgrade-Insecure-Requests': '1',
|
|
||||||
'Sec-Fetch-Site': 'same-origin',
|
|
||||||
'Sec-Fetch-Mode': 'navigate',
|
|
||||||
'Sec-Fetch-User': '?1',
|
|
||||||
'Sec-Fetch-Dest': 'document',
|
|
||||||
'Sec-Ch-Ua': '"Not_A Brand";v="99", "Chromium";v="142"',
|
|
||||||
'Sec-Ch-Ua-Mobile': '?0',
|
|
||||||
'Sec-Ch-Ua-Platform': '"Linux"',
|
|
||||||
'Accept-Encoding': 'gzip, deflate, br',
|
|
||||||
'Priority': 'u=0, i',
|
|
||||||
}
|
|
||||||
session.headers.update(headers)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
logger.info('Attempting login...')
|
|
||||||
|
|
||||||
# Login payload
|
|
||||||
payload = {'login': LOGIN, 'password': PASSWORD}
|
|
||||||
|
|
||||||
# Perform Login
|
|
||||||
# Note: The user request shows a POST to /user/login with form data
|
|
||||||
# We need to make sure we handle the PHPSESSID correctly.
|
|
||||||
# If we already have a PHPSESSID, requests will send it.
|
|
||||||
|
|
||||||
login_response = session.post(URL_LOGIN, data=payload, allow_redirects=True)
|
|
||||||
|
|
||||||
logger.info(f'Login Response Status: {login_response.status_code}')
|
|
||||||
logger.info(f'Cookies after login: {session.cookies.get_dict()}')
|
|
||||||
|
|
||||||
# Verify Session
|
|
||||||
logger.info('Verifying session...')
|
|
||||||
verify_response = session.get(URL_VERIFY, allow_redirects=False)
|
|
||||||
|
|
||||||
logger.info(f'Verify Response Status: {verify_response.status_code}')
|
|
||||||
|
|
||||||
if verify_response.status_code == 200:
|
|
||||||
logger.info('Session verification SUCCESS (200 OK).')
|
|
||||||
touch_success_file()
|
|
||||||
|
|
||||||
# Save PHPSESSID to Redis
|
|
||||||
phpsessid = session.cookies.get('PHPSESSID')
|
|
||||||
if phpsessid:
|
|
||||||
try:
|
|
||||||
redis_client.set('EDU_PHPSESSID', phpsessid)
|
|
||||||
logger.info(f'Saved PHPSESSID to Redis: {phpsessid}')
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'Failed to save PHPSESSID to Redis: {e}')
|
|
||||||
elif verify_response.status_code == 302:
|
|
||||||
logger.warning('Session verification FAILED (302 Redirect). Session might be invalid.')
|
|
||||||
else:
|
|
||||||
logger.warning(f'Session verification returned unexpected status: {verify_response.status_code}')
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'An error occurred: {e}')
|
|
||||||
|
|
||||||
logger.info(f'Sleeping for {INTERVAL} minutes...')
|
|
||||||
time.sleep(INTERVAL * 60)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
FROM python:3.14-slim
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# renovate: datasource=pypi depName=playwright versioning=pep440
|
|
||||||
ARG PLAYWRIGHT_VERSION=1.56.0
|
|
||||||
|
|
||||||
# Install dependencies - PLAYWRIGHT_VERSION is single-source, renovate updates ARG above and all other places via regexManagers
|
|
||||||
RUN pip install --no-cache-dir pip==25.0.1 && pip install --no-cache-dir playwright==${PLAYWRIGHT_VERSION} redis==5.2.1 requests==2.32.3 "python-telegram-bot[job-queue]==21.10"
|
|
||||||
|
|
||||||
COPY checker.py .
|
|
||||||
|
|
||||||
CMD ["python", "checker.py"]
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -29,31 +29,6 @@ data:
|
|||||||
"managerFilePatterns": ["/k8s/.+\\.ya?ml$/"]
|
"managerFilePatterns": ["/k8s/.+\\.ya?ml$/"]
|
||||||
},
|
},
|
||||||
"customManagers": [
|
"customManagers": [
|
||||||
{
|
|
||||||
"customType": "regex",
|
|
||||||
"description": "singlesource: playwright npm version pinned in npx command (k8s + compose)",
|
|
||||||
"managerFilePatterns": ["edu_master/k8s/playwright.yaml", "edu_master/compose.yaml"],
|
|
||||||
"matchStrings": ["playwright@(?<currentValue>\\d+\\.\\d+\\.\\d+)"],
|
|
||||||
"datasourceTemplate": "npm",
|
|
||||||
"depNameTemplate": "playwright"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"customType": "regex",
|
|
||||||
"description": "singlesource: PLAYWRIGHT_VERSION file",
|
|
||||||
"managerFilePatterns": ["edu_master/PLAYWRIGHT_VERSION"],
|
|
||||||
"matchStrings": ["^(?<currentValue>\\d+\\.\\d+\\.\\d+)(?:\\r?\\n)?$"],
|
|
||||||
"datasourceTemplate": "pypi",
|
|
||||||
"depNameTemplate": "playwright"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"customType": "regex",
|
|
||||||
"description": "singlesource: playwright Python client version pinned in Dockerfile ARG",
|
|
||||||
"managerFilePatterns": ["edu_master/webinar-checker/Dockerfile"],
|
|
||||||
"matchStrings": ["(?:^|\\n)ARG PLAYWRIGHT_VERSION=(?<currentValue>\\d+\\.\\d+\\.\\d+)(?:\\r?\\n|$)"],
|
|
||||||
"datasourceTemplate": "pypi",
|
|
||||||
"depNameTemplate": "playwright",
|
|
||||||
"versioningTemplate": "pep440"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"customType": "regex",
|
"customType": "regex",
|
||||||
"description": "kube-prometheus-stack chart version pinned in the deploy workflow",
|
"description": "kube-prometheus-stack chart version pinned in the deploy workflow",
|
||||||
@@ -212,17 +187,6 @@ data:
|
|||||||
"matchPackageNames": ["/gcr\\.forust\\.xyz\\/forust\\/.+/"],
|
"matchPackageNames": ["/gcr\\.forust\\.xyz\\/forust\\/.+/"],
|
||||||
"enabled": false
|
"enabled": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "singlesource playwright - use whichever version is found, keep docker+pypi+npm in sync",
|
|
||||||
"matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"],
|
|
||||||
"groupName": "playwright singlesource",
|
|
||||||
"groupSlug": "playwright"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "playwright must not automerge - version skew breaks the WS handshake (checker.py:1523 vs playwright.yaml:20)",
|
|
||||||
"matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"],
|
|
||||||
"automerge": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Renovate updates itself in lockstep across the CronJob and the Compose file",
|
"description": "Renovate updates itself in lockstep across the CronJob and the Compose file",
|
||||||
"matchPackageNames": ["renovate/renovate"],
|
"matchPackageNames": ["renovate/renovate"],
|
||||||
|
|||||||
@@ -18,31 +18,6 @@
|
|||||||
"managerFilePatterns": ["/k8s/.+\\.ya?ml$/"]
|
"managerFilePatterns": ["/k8s/.+\\.ya?ml$/"]
|
||||||
},
|
},
|
||||||
"customManagers": [
|
"customManagers": [
|
||||||
{
|
|
||||||
"customType": "regex",
|
|
||||||
"description": "singlesource: playwright npm version pinned in npx command (k8s + compose)",
|
|
||||||
"managerFilePatterns": ["edu_master/k8s/playwright.yaml", "edu_master/compose.yaml"],
|
|
||||||
"matchStrings": ["playwright@(?<currentValue>\\d+\\.\\d+\\.\\d+)"],
|
|
||||||
"datasourceTemplate": "npm",
|
|
||||||
"depNameTemplate": "playwright"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"customType": "regex",
|
|
||||||
"description": "singlesource: PLAYWRIGHT_VERSION file",
|
|
||||||
"managerFilePatterns": ["edu_master/PLAYWRIGHT_VERSION"],
|
|
||||||
"matchStrings": ["^(?<currentValue>\\d+\\.\\d+\\.\\d+)(?:\\r?\\n)?$"],
|
|
||||||
"datasourceTemplate": "pypi",
|
|
||||||
"depNameTemplate": "playwright"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"customType": "regex",
|
|
||||||
"description": "singlesource: playwright Python client version pinned in Dockerfile ARG",
|
|
||||||
"managerFilePatterns": ["edu_master/webinar-checker/Dockerfile"],
|
|
||||||
"matchStrings": ["(?:^|\\n)ARG PLAYWRIGHT_VERSION=(?<currentValue>\\d+\\.\\d+\\.\\d+)(?:\\r?\\n|$)"],
|
|
||||||
"datasourceTemplate": "pypi",
|
|
||||||
"depNameTemplate": "playwright",
|
|
||||||
"versioningTemplate": "pep440"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"customType": "regex",
|
"customType": "regex",
|
||||||
"description": "kube-prometheus-stack chart version pinned in the deploy workflow",
|
"description": "kube-prometheus-stack chart version pinned in the deploy workflow",
|
||||||
@@ -201,17 +176,6 @@
|
|||||||
"matchPackageNames": ["/gcr\\.forust\\.xyz\\/forust\\/.+/"],
|
"matchPackageNames": ["/gcr\\.forust\\.xyz\\/forust\\/.+/"],
|
||||||
"enabled": false
|
"enabled": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"description": "singlesource playwright - use whichever version is found, keep docker+pypi+npm in sync",
|
|
||||||
"matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"],
|
|
||||||
"groupName": "playwright singlesource",
|
|
||||||
"groupSlug": "playwright"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "playwright must not automerge - version skew breaks the WS handshake (checker.py:1523 vs playwright.yaml:20)",
|
|
||||||
"matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"],
|
|
||||||
"automerge": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Renovate updates itself in lockstep across the CronJob and the Compose file",
|
"description": "Renovate updates itself in lockstep across the CronJob and the Compose file",
|
||||||
"matchPackageNames": ["renovate/renovate"],
|
"matchPackageNames": ["renovate/renovate"],
|
||||||
|
|||||||
Reference in new issue
Block a user