From 6c36c35c9c9186258a220e5b7a537fc2c65c6ab6 Mon Sep 17 00:00:00 2001 From: Jonathan Payne Date: Mon, 28 Sep 2026 14:06:12 -0400 Subject: [PATCH 1/2] closes OpenConceptLab/ocl_issues#2838 | Pull requests run the Pylint and Tests jobs, and the Tests job really waits for Elasticsearch Pylint and tests ran only in build.yml, on pushes to master, so a pull request showed no lint or test result before it merged. - .github/workflows/pr.yml runs build.yml's Pylint and Tests jobs unchanged on every pull_request, Postgres and Elasticsearch service containers included (read-only token; a newer push cancels the older run). - Tests' "Wait for Elasticsearch HTTP" step, in build.yml and pr.yml alike: the curl URL was unquoted, so its & backgrounded curl and the step always passed at once; and it named the es host, which doesn't resolve for a job on the runner, where the service is on localhost's mapped port. The URL is quoted, points at localhost, and curl -f treats a 408 (not yet yellow) as not ready. build.yml's deploy jobs are untouched. This completes #2838 after oclmap#84, oclweb3#57 and oclweb2#52. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Lkm1wDY1zKSDTm2apzPJLi --- .github/workflows/build.yml | 4 +- .github/workflows/pr.yml | 135 ++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/pr.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e1490bda..fd339208 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -95,7 +95,9 @@ jobs: run: | echo "Waiting for Elasticsearch..." for i in {1..60}; do - curl -s http://es:9200/_cluster/health?wait_for_status=yellow&timeout=1s >/dev/null && \ + # Quoted: an unquoted & backgrounded curl, so this step always passed at once. + # localhost: the job runs on the runner, where the es service is on a mapped port. + curl -sf "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=1s" >/dev/null && \ echo "Elasticsearch is ready!" && exit 0 echo "Still not ready..." sleep 2 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..dc255091 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,135 @@ +name: PR checks + +# Runs build.yml's "Pylint" and "Tests" jobs on every pull request, so a PR +# shows the result before it merges (OpenConceptLab/ocl_issues#2838). Keep +# these jobs the same as build.yml's. + +on: + pull_request: + +permissions: + contents: read + +concurrency: + group: pr-checks-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + pylint: + name: Pylint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run linting rules + uses: actions/setup-python@v4 + with: + python-version: '3.12' + cache: 'pip' + - run: pip install -r requirements.txt + - run: pylint -j0 core/ + + tests: + name: Tests + needs: [pylint] + runs-on: ubuntu-latest + services: + db: + image: postgres:14.7 + ports: + - 5432:5432 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: Postgres123 + POSTGRES_DB: postgres + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + es: + image: docker.elastic.co/elasticsearch/elasticsearch:8.15.2 + options: >- + --ulimit memlock=-1:-1 + --ulimit nofile=65536:65536 + ports: + - 9200:9200 + env: + discovery.type: single-node + xpack.security.enabled: "false" + xpack.security.http.ssl.enabled: "false" + xpack.security.transport.ssl.enabled: "false" + ES_JAVA_OPTS: "-Xmx1024m -Xms1024m" + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.12" + - name: Cache pip dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Install dependencies + run: | + python -m venv venv + . venv/bin/activate + python -m pip install --cache-dir ~/.cache/pip --upgrade pip "setuptools==80.9.0" + python -m pip install --cache-dir ~/.cache/pip -r requirements.txt + - name: Wait for Postgres + run: | + echo "Waiting for Postgres..." + for i in {1..30}; do + pg_isready -h 0.0.0.0 -p 5432 -U postgres && echo "Postgres ready!" && exit 0 + echo "Still waiting..." + sleep 2 + done + echo "Postgres did not start!" && exit 1 + - name: Wait for Elasticsearch HTTP + run: | + echo "Waiting for Elasticsearch..." + for i in {1..60}; do + # Quoted: an unquoted & backgrounded curl, so this step always passed at once. + # localhost: the job runs on the runner, where the es service is on a mapped port. + curl -sf "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=1s" >/dev/null && \ + echo "Elasticsearch is ready!" && exit 0 + echo "Still not ready..." + sleep 2 + done + echo "Elasticsearch failed to start" + exit 1 + - name: Free Disk Space + uses: jlumbroso/free-disk-space@main + with: + android: true + dotnet: true + haskell: true + tool-cache: false + docker-images: false + large-packages: false + swap-storage: false + - name: Create Elasticsearch indices + env: + DB_HOST: 0.0.0.0 + ES_HOST: 0.0.0.0 + ENVIRONMENT: ci + run: | + . venv/bin/activate + python manage.py search_index --create + - name: Run Tests + env: + DB_HOST: 0.0.0.0 + ES_HOST: 0.0.0.0 + ENVIRONMENT: ci + # Concept.properties dicts carry both a `value` key and a `display` key; ConceptDocument.prepare_properties + # picks between them via `list(set(prop.keys()) - {'code'})[0]`, whose result depends on Python's per-process + # string hash randomization. Pinning the seed makes that pick land on `value` deterministically instead + # of varying by CI run. See OpenConceptLab/ocl_issues#2708. + PYTHONHASHSEED: "2" + run: | + . venv/bin/activate + COVERAGE_FILE=/tmp/.coverage coverage run --parallel-mode --source='core' manage.py test --parallel=1 -v 3 --keepdb + COVERAGE_FILE=/tmp/.coverage coverage combine + COVERAGE_FILE=/tmp/.coverage coverage report -m --include=core/* --fail-under=95 --sort=cover From ad20cc9c3d2088dfff07c396c318f4d0cdc9b856 Mon Sep 17 00:00:00 2001 From: Jonathan Payne Date: Mon, 28 Sep 2026 14:45:19 -0400 Subject: [PATCH 2/2] OpenConceptLab/ocl_issues#2838 | The Elasticsearch wait checks the cluster status, since ES 8 answers 200 when wait_for_status times out From Codex pass 1: since Elasticsearch 8.0, _cluster/health answers 200 with "timed_out": true when wait_for_status times out, so curl -f passed while the cluster was still red. The step now also requires "status":"yellow" or "status":"green" in the body; anything else is another try. Same change in build.yml and pr.yml. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Lkm1wDY1zKSDTm2apzPJLi --- .github/workflows/build.yml | 4 +++- .github/workflows/pr.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fd339208..b8f5b7b6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -97,7 +97,9 @@ jobs: for i in {1..60}; do # Quoted: an unquoted & backgrounded curl, so this step always passed at once. # localhost: the job runs on the runner, where the es service is on a mapped port. - curl -sf "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=1s" >/dev/null && \ + # ES 8 answers 200 even when wait_for_status times out, so check the status too. + health=$(curl -sf "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=1s") && \ + echo "$health" | grep -E '"status":"(yellow|green)"' >/dev/null && \ echo "Elasticsearch is ready!" && exit 0 echo "Still not ready..." sleep 2 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index dc255091..9e24c44f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -93,7 +93,9 @@ jobs: for i in {1..60}; do # Quoted: an unquoted & backgrounded curl, so this step always passed at once. # localhost: the job runs on the runner, where the es service is on a mapped port. - curl -sf "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=1s" >/dev/null && \ + # ES 8 answers 200 even when wait_for_status times out, so check the status too. + health=$(curl -sf "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=1s") && \ + echo "$health" | grep -E '"status":"(yellow|green)"' >/dev/null && \ echo "Elasticsearch is ready!" && exit 0 echo "Still not ready..." sleep 2