You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This plan assumes ICTU/quality-time#13531 ("Build (not download) images in the Kubernetes application-tests workflow") is already done (which itself required #13530). So when this work starts the repo already has:
.github/workflows/application-tests-k8s.yml — builds branch images (:ci), loads them into minikube, deploys the chart with CI overrides, port-forwards, and runs the application tests against Kubernetes.
helm/values-application-tests.yaml — CI values: image.tag: ci + image.pullPolicy: Never for all components, plus api_server.env.LDAP_URL: ldap://quality-time-ldap:389.
helm/deploy-ci.yaml — CI-only test-LDAP (quality-time-ldap) and Selenium (selenium) Deployments/Services (and, pre-this-plan, the db + ldap Secrets).
Configurable imagePullPolicy in the chart; the testldap ldap compose service named ictu/quality-time_testldap.
Application tests parametrized via WWW_URL / WWW_BROWSER_URL / SELENIUM_URL.
Context
The repo runs CI on both GitHub Actions and CircleCI. CircleCI (.circleci/config.yml) already has an application_tests job that mirrors the Docker GitHub workflow: a machine executor that does docker compose build && docker compose up -d and runs the tests in a uv container joined to the proxy's network. There is no CircleCI job that exercises the Kubernetes deployment.
This plan adds that: a CircleCI job that builds the branch images and runs the application tests against a minikube Kubernetes deployment — the CircleCI counterpart of application-tests-k8s.yml.
Per the chosen approach, the build→deploy→test logic is extracted into one portable shell script that both CI systems call, so the two never drift. Each CI keeps only what is genuinely CI-specific: provisioning the cluster and toolchain.
Changes
1. New shared script ci/application-tests-k8s.sh
A portable bash script (long-form options, per project convention) that assumes a reachable Kubernetes context (kubectl/helm/docker on PATH, minikube started) and does:
Create CI secrets imperatively (portable, no GitHub-only action, no yq):
Wait: kubectl wait for all pods Ready and all deployments Available (covers quality-time-ldap and selenium).
Port-forwardservice/quality-time-www 8080:8080 and service/selenium 4444:4444 in the background, then poll until both ports accept connections.
Run the tests: docker run --network=host --env WWW_URL=http://localhost:8080 --env SELENIUM_URL=http://localhost:4444 --env WWW_BROWSER_URL=http://quality-time-www:8080 ... ghcr.io/astral-sh/uv:python3.14-bookworm tests/application_tests/test.sh
Failure diagnostics (kubectl get pods/events, describe, per-pod logs) go in a small companion ci/k8s-diagnostics.sh, called by each CI on failure.
2. Adjust the #13531 artifacts to use the shared script
.github/workflows/application-tests-k8s.yml: replace the inline build/load/deploy steps (and the VALUE_CHANGES + fjogeleit/yaml-update-action secret step) with bash ci/application-tests-k8s.sh. Keep: checkout, medyagh/setup-minikube, artifact upload, and if: failure() → bash ci/k8s-diagnostics.sh. (kubectl/helm are preinstalled on GitHub-hosted Ubuntu runners; minikube via the action.)
helm/deploy-ci.yaml: remove the two Secret documents (now created imperatively by the script); keep the LDAP + Selenium Deployments/Services. Both CIs create the secrets the same way.
Ordering note: since #13531 is not merged yet, these two edits are cleanest folded into #13531 itself. If #13531 lands first, this plan adjusts them.
3. New CircleCI job application_tests_k8s (.circleci/config.yml)
machine: image: default, resource_class: large (bump to xlarge if minikube + 8 images + Selenium's 2 GB shm are resource-starved).
Steps: checkout; docker login with the QualityTime context creds ($DOCKERHUB_USERNAME/$DOCKERHUB_PASSWORD) to avoid pull-rate limits; install pinned kubectl, helm, minikube (curl, SHA/version-pinned per repo convention); minikube start --driver=docker --container-runtime=containerd (matching the GitHub setup); bash ci/application-tests-k8s.sh; store_artifacts for build/; and a when: on_fail step running bash ci/k8s-diagnostics.sh.
4. Rename the CircleCI Docker job for symmetry
Rename application_tests → application_tests_docker in .circleci/config.yml and update its reference in the build_test workflow.
Add application_tests_k8s to the build_test workflow with context: QualityTime (alongside application_tests_docker, feature_tests, unittests).
5. Docs
Update docs/src/development.md (the "Application tests" section, ~line 174) to note the application tests run against Kubernetes on both GitHub Actions and CircleCI, via the shared ci/application-tests-k8s.sh.
The compose build pair (docker/docker-compose.yml + docker/docker-compose.ci.yml) — same invocation the existing CircleCI application_tests job already uses, just with QUALITY_TIME_VERSION=ci.
CI cost: CircleCI now builds all 8 images and runs minikube on a machine executor — heavier and slower than the lightweight docker-executor jobs; large/xlarge resource class required.
minikube provisioning stays per-CI: GitHub uses the setup-minikube action; CircleCI installs minikube manually. Only this thin provisioning layer differs; everything downstream is shared.
Local end-to-end (containerd minikube):minikube start --driver=docker --container-runtime=containerd; bash ci/application-tests-k8s.sh → secrets created, 8 :ci images loaded (minikube image ls | grep :ci), all pods Ready, tests pass, build/ artifacts written.
Parity: the GitHub application-tests-k8s run (now calling the same script) and the CircleCI application_tests_k8s run produce the same test outcome on the same commit.
Branch sensitivity: a trivial visible change in a component appears in both CIs' k8s runs, confirming branch code (not published images) is under test.
Per the arm64/amd64 memory note: both CI runners are amd64; trust them for runtime-constraint behavior in images embedding browsers/native binaries (renderer, selenium).
Prerequisite
This plan assumes ICTU/quality-time#13531 ("Build (not download) images in the Kubernetes application-tests workflow") is already done (which itself required #13530). So when this work starts the repo already has:
.github/workflows/application-tests-k8s.yml— builds branch images (:ci), loads them into minikube, deploys the chart with CI overrides, port-forwards, and runs the application tests against Kubernetes.helm/values-application-tests.yaml— CI values:image.tag: ci+image.pullPolicy: Neverfor all components, plusapi_server.env.LDAP_URL: ldap://quality-time-ldap:389.helm/deploy-ci.yaml— CI-only test-LDAP (quality-time-ldap) and Selenium (selenium) Deployments/Services (and, pre-this-plan, the db + ldap Secrets).imagePullPolicyin the chart; the testldapldapcompose service namedictu/quality-time_testldap.WWW_URL/WWW_BROWSER_URL/SELENIUM_URL.Context
The repo runs CI on both GitHub Actions and CircleCI. CircleCI (
.circleci/config.yml) already has anapplication_testsjob that mirrors the Docker GitHub workflow: amachineexecutor that doesdocker compose build && docker compose up -dand runs the tests in a uv container joined to the proxy's network. There is no CircleCI job that exercises the Kubernetes deployment.This plan adds that: a CircleCI job that builds the branch images and runs the application tests against a minikube Kubernetes deployment — the CircleCI counterpart of
application-tests-k8s.yml.Per the chosen approach, the build→deploy→test logic is extracted into one portable shell script that both CI systems call, so the two never drift. Each CI keeps only what is genuinely CI-specific: provisioning the cluster and toolchain.
Changes
1. New shared script
ci/application-tests-k8s.shA portable bash script (long-form options, per project convention) that assumes a reachable Kubernetes context (
kubectl/helm/dockeron PATH,minikubestarted) and does:yq):printf | base64to dodge, andkubectlbase64-encodes automatically. testldap'scn=adminpassword comes from the samequality-time-ldap-secret(wired in Run the application tests against Kubernetes as well as Docker #13530's deploy-ci.yaml), so lookup-user and admin passwords stay identical.ci):QUALITY_TIME_VERSION=ci docker compose --progress quiet --file docker/docker-compose.yml --file docker/docker-compose.ci.yml --project-name quality-time buildminikube image load <image>over the 8ictu/quality-time_*:ciimages (7 components +ictu/quality-time_testldap:ci).kubectl apply --filename helm/deploy-ci.yaml;helm dependency build helm;helm upgrade --install --render-subchart-notes quality-time helm --values helm/values-application-tests.yaml.kubectl waitfor all pods Ready and all deployments Available (coversquality-time-ldapandselenium).service/quality-time-www 8080:8080andservice/selenium 4444:4444in the background, then poll until both ports accept connections.docker run --network=host --env WWW_URL=http://localhost:8080 --env SELENIUM_URL=http://localhost:4444 --env WWW_BROWSER_URL=http://quality-time-www:8080 ... ghcr.io/astral-sh/uv:python3.14-bookworm tests/application_tests/test.shFailure diagnostics (
kubectl get pods/events,describe, per-pod logs) go in a small companionci/k8s-diagnostics.sh, called by each CI on failure.2. Adjust the #13531 artifacts to use the shared script
.github/workflows/application-tests-k8s.yml: replace the inline build/load/deploy steps (and theVALUE_CHANGES+fjogeleit/yaml-update-actionsecret step) withbash ci/application-tests-k8s.sh. Keep: checkout,medyagh/setup-minikube, artifact upload, andif: failure()→bash ci/k8s-diagnostics.sh. (kubectl/helmare preinstalled on GitHub-hosted Ubuntu runners; minikube via the action.)helm/deploy-ci.yaml: remove the twoSecretdocuments (now created imperatively by the script); keep the LDAP + Selenium Deployments/Services. Both CIs create the secrets the same way.3. New CircleCI job
application_tests_k8s(.circleci/config.yml)machine: image: default,resource_class: large(bump toxlargeif minikube + 8 images + Selenium's 2 GB shm are resource-starved).checkout; docker login with theQualityTimecontext creds ($DOCKERHUB_USERNAME/$DOCKERHUB_PASSWORD) to avoid pull-rate limits; install pinnedkubectl,helm,minikube(curl, SHA/version-pinned per repo convention);minikube start --driver=docker --container-runtime=containerd(matching the GitHub setup);bash ci/application-tests-k8s.sh;store_artifactsforbuild/; and awhen: on_failstep runningbash ci/k8s-diagnostics.sh.4. Rename the CircleCI Docker job for symmetry
application_tests→application_tests_dockerin.circleci/config.ymland update its reference in thebuild_testworkflow.application_tests_k8sto thebuild_testworkflow withcontext: QualityTime(alongsideapplication_tests_docker,feature_tests,unittests).5. Docs
Update
docs/src/development.md(the "Application tests" section, ~line 174) to note the application tests run against Kubernetes on both GitHub Actions and CircleCI, via the sharedci/application-tests-k8s.sh.Critical files
ci/application-tests-k8s.sh— shared build→secrets→load→deploy→port-forward→test logic.ci/k8s-diagnostics.sh— shared on-failure dump..circleci/config.yml— newapplication_tests_k8sjob + renameapplication_tests→application_tests_docker+ workflow wiring..github/workflows/application-tests-k8s.yml— call the shared script instead of inline steps.helm/deploy-ci.yaml— drop the Secret documents (created imperatively now).docs/src/development.md— document the dual-CI k8s run.Reuse (don't re-create)
helm/values-application-tests.yaml, the LDAP/Selenium manifests inhelm/deploy-ci.yaml, the:ciimage tags, and the parametrized tests — all from Run the application tests against Kubernetes as well as Docker #13530/Build (not download) images in the Kubernetes application-tests workflow #13531; the script just orchestrates them.docker/docker-compose.yml+docker/docker-compose.ci.yml) — same invocation the existing CircleCIapplication_testsjob already uses, just withQUALITY_TIME_VERSION=ci.Trade-offs / notes
yaml-update-actionstep and the Secret stanzas fromhelm/deploy-ci.yaml. This simplifies both CIs but means this plan touches files Build (not download) images in the Kubernetes application-tests workflow #13531 introduces — best coordinated by folding into Build (not download) images in the Kubernetes application-tests workflow #13531 if not yet merged.machineexecutor — heavier and slower than the lightweightdocker-executor jobs;large/xlargeresource class required.setup-minikubeaction; CircleCI installs minikube manually. Only this thin provisioning layer differs; everything downstream is shared.kubectl create secretwith random hex replaces base64-of-$RANDOM; hex is control-character-free, preserving the SASLprep safety Run the application tests against Kubernetes as well as Docker #13530 cared about.Verification
bash -n ci/application-tests-k8s.shandshellcheck ci/application-tests-k8s.sh ci/k8s-diagnostics.shpass.circleci config validateaccepts.circleci/config.yml; thebuild_testworkflow listsapplication_tests_docker,application_tests_k8s,feature_tests,unittests.minikube start --driver=docker --container-runtime=containerd;bash ci/application-tests-k8s.sh→ secrets created, 8:ciimages loaded (minikube image ls | grep :ci), all pods Ready, tests pass,build/artifacts written.application-tests-k8srun (now calling the same script) and the CircleCIapplication_tests_k8srun produce the same test outcome on the same commit.