a9cd7750f4
CI / detect-changes (push) Waiting to run
CI / build (push) Waiting to run
UI v2 CI / E2E (Mocked) (push) Blocked by required conditions
UI v2 Integration CI / E2E (Integration) (push) Waiting to run
CI / unit-test (push) Blocked by required conditions
CI / test-harness (push) Waiting to run
CI / generate-e2e-matrix (push) Waiting to run
CI / e2e (push) Blocked by required conditions
CI / build-ui (push) Waiting to run
Publish docs via GitHub Pages / Deploy docs (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run
UI v2 CI / Lint, Format & Test (push) Waiting to run
420 lines
15 KiB
YAML
420 lines
15 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
paths-ignore:
|
|
- "conductor-clients/**"
|
|
pull_request:
|
|
paths-ignore:
|
|
- "conductor-clients/**"
|
|
workflow_dispatch:
|
|
inputs:
|
|
redis_es8:
|
|
description: "Redis + Elasticsearch 8"
|
|
type: boolean
|
|
default: true
|
|
postgres:
|
|
description: "PostgreSQL"
|
|
type: boolean
|
|
default: false
|
|
mysql:
|
|
description: "MySQL"
|
|
type: boolean
|
|
default: false
|
|
redis_os3:
|
|
description: "Redis + OpenSearch 3"
|
|
type: boolean
|
|
default: false
|
|
redis_es7:
|
|
description: "Redis + Elasticsearch 7"
|
|
type: boolean
|
|
default: false
|
|
cassandra_es7:
|
|
description: "Cassandra + Elasticsearch 7"
|
|
type: boolean
|
|
default: false
|
|
|
|
jobs:
|
|
detect-changes:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
heavy-persistence: ${{ steps.filter.outputs.heavy-persistence }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
fetch-depth: 0
|
|
- uses: dorny/paths-filter@v3
|
|
id: filter
|
|
with:
|
|
filters: |
|
|
heavy-persistence:
|
|
- 'cassandra-persistence/**'
|
|
- 'es6-persistence/**'
|
|
- 'es7-persistence/**'
|
|
- 'es8-persistence/**'
|
|
- 'mysql-persistence/**'
|
|
- 'os-persistence/**'
|
|
- 'os-persistence-v2/**'
|
|
- 'os-persistence-v3/**'
|
|
- 'scheduler/cassandra-persistence/**'
|
|
- 'scheduler/mysql-persistence/**'
|
|
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
fetch-depth: 0
|
|
- name: Free disk space
|
|
run: |
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo rm -rf /usr/local/lib/android
|
|
sudo rm -rf /opt/ghc
|
|
- name: Gradle wrapper validation
|
|
uses: gradle/wrapper-validation-action@v3
|
|
- name: Set up Zulu JDK 21
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: "zulu"
|
|
java-version: "21"
|
|
- name: Cache SonarCloud packages
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: ~/.sonar/cache
|
|
key: ${{ runner.os }}-sonar
|
|
restore-keys: ${{ runner.os }}-sonar
|
|
- name: Cache Gradle packages
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
restore-keys: ${{ runner.os }}-gradle-
|
|
- name: Build with Gradle
|
|
if: github.ref != 'refs/heads/main'
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
run: |
|
|
./gradlew build -x :conductor-test-harness:test -x test
|
|
- name: Build and Publish snapshot
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
run: |
|
|
echo "Running build for commit ${{ github.sha }}"
|
|
./gradlew build -x :conductor-test-harness:test -x test
|
|
- name: Generate aggregated coverage report
|
|
if: always()
|
|
run: ./gradlew jacocoAggregatedReport -x test || true
|
|
- name: Publish Test Report
|
|
uses: mikepenz/action-junit-report@v6
|
|
if: always()
|
|
with:
|
|
report_paths: "**/build/test-results/test/TEST-*.xml"
|
|
- name: Upload build artifacts
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: build-artifacts
|
|
path: "**/build/reports"
|
|
- name: Upload coverage report
|
|
uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: coverage-report
|
|
path: build/reports/jacoco/aggregated
|
|
unit-test:
|
|
needs: detect-changes
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
fetch-depth: 0
|
|
- name: Free disk space
|
|
run: |
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo rm -rf /usr/local/lib/android
|
|
sudo rm -rf /opt/ghc
|
|
- name: Set up Zulu JDK 21
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: "zulu"
|
|
java-version: "21"
|
|
- name: Cache Gradle packages
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
restore-keys: ${{ runner.os }}-gradle-
|
|
- name: Force Docker API Version
|
|
run: echo 'api.version=1.44' > ~/.docker-java.properties
|
|
- name: Run unit tests
|
|
run: |
|
|
# On main (post-merge): run everything
|
|
if [ "${{ github.event_name }}" != "pull_request" ]; then
|
|
./gradlew test -x :conductor-test-harness:test
|
|
exit 0
|
|
fi
|
|
# On PRs: skip heavy container tests (cassandra, es, mysql, opensearch)
|
|
# unless their code changed. Redis, postgres, sqlite always run.
|
|
if [ "${{ needs.detect-changes.outputs.heavy-persistence }}" == "true" ]; then
|
|
./gradlew test -x :conductor-test-harness:test
|
|
else
|
|
./gradlew test \
|
|
-x :conductor-test-harness:test \
|
|
-x :conductor-cassandra-persistence:test \
|
|
-x :conductor-scheduler-cassandra-persistence:test \
|
|
-x :conductor-es6-persistence:test \
|
|
-x :conductor-es7-persistence:test \
|
|
-x :conductor-es8-persistence:test \
|
|
-x :conductor-mysql-persistence:test \
|
|
-x :conductor-scheduler-mysql-persistence:test \
|
|
-x :conductor-os-persistence:test \
|
|
-x :conductor-os-persistence-v2:test \
|
|
-x :conductor-os-persistence-v3:test
|
|
fi
|
|
- name: Publish Test Report
|
|
uses: mikepenz/action-junit-report@v6
|
|
if: always()
|
|
with:
|
|
report_paths: "**/build/test-results/test/TEST-*.xml"
|
|
check_name: Unit Test Report
|
|
- name: Upload unit-test reports
|
|
uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: unit-test-reports
|
|
path: "**/build/reports/tests"
|
|
test-harness:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
fetch-depth: 0
|
|
- name: Free disk space
|
|
run: |
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo rm -rf /usr/local/lib/android
|
|
sudo rm -rf /opt/ghc
|
|
- name: Set up Zulu JDK 21
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: "zulu"
|
|
java-version: "21"
|
|
- name: Cache Gradle packages
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
restore-keys: ${{ runner.os }}-gradle-
|
|
- name: Force Docker API Version
|
|
run: echo 'api.version=1.44' > ~/.docker-java.properties
|
|
- name: Cache Docker images
|
|
uses: actions/cache@v5
|
|
id: docker-cache
|
|
with:
|
|
path: /tmp/docker-images-test-harness.tar
|
|
key: docker-test-harness-v2
|
|
- name: Load cached Docker images
|
|
if: steps.docker-cache.outputs.cache-hit == 'true'
|
|
run: docker load -i /tmp/docker-images-test-harness.tar || true
|
|
- name: Run test-harness tests
|
|
run: |
|
|
./gradlew :conductor-test-harness:test
|
|
- name: Save Docker images for cache
|
|
if: steps.docker-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
set -euo pipefail
|
|
docker image prune -f
|
|
mapfile -t images < <((docker images --format '{{.Repository}}:{{.Tag}}' \
|
|
| grep -v '<none>' \
|
|
| grep -E '(^|/)(elasticsearch|redis|postgres|mysql|mongo|cassandra)(:|/)|mockserver/mockserver|opensearchproject/opensearch|testcontainers/|orkesio/') || true)
|
|
if [ "${#images[@]}" -eq 0 ]; then
|
|
echo "No Testcontainers-related images to cache; writing empty tar for cache action."
|
|
tar -cf /tmp/docker-images-test-harness.tar --files-from /dev/null
|
|
exit 0
|
|
fi
|
|
printf '%s\n' "${images[@]}"
|
|
docker save -o /tmp/docker-images-test-harness.tar "${images[@]}"
|
|
- name: Publish Test Report
|
|
uses: mikepenz/action-junit-report@v6
|
|
if: always()
|
|
with:
|
|
report_paths: "test-harness/build/test-results/test/TEST-*.xml"
|
|
- name: Upload test-harness reports
|
|
uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: test-harness-reports
|
|
path: "test-harness/build/reports"
|
|
- name: Upload test-harness coverage report
|
|
uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: test-harness-coverage-report
|
|
path: "test-harness/build/reports/jacoco"
|
|
generate-e2e-matrix:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
steps:
|
|
- id: set-matrix
|
|
shell: bash
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
items=""
|
|
[ "${{ inputs.redis_es8 }}" = "true" ] && items="${items}{\"name\":\"redis-es8\",\"script\":\"./e2e/run_tests-es8.sh\"},"
|
|
[ "${{ inputs.postgres }}" = "true" ] && items="${items}{\"name\":\"postgres\",\"script\":\"./e2e/run_tests-postgres.sh\"},"
|
|
[ "${{ inputs.mysql }}" = "true" ] && items="${items}{\"name\":\"mysql\",\"script\":\"./e2e/run_tests-mysql.sh\"},"
|
|
[ "${{ inputs.redis_os3 }}" = "true" ] && items="${items}{\"name\":\"redis-os3\",\"script\":\"./e2e/run_tests-redis-os3.sh\"},"
|
|
[ "${{ inputs.redis_es7 }}" = "true" ] && items="${items}{\"name\":\"redis-es7\",\"script\":\"./e2e/run_tests-redis-es7.sh\"},"
|
|
[ "${{ inputs.cassandra_es7 }}" = "true" ] && items="${items}{\"name\":\"cassandra-es7\",\"script\":\"./e2e/run_tests-cassandra-es7.sh\"},"
|
|
items="${items%,}"
|
|
echo "matrix={\"include\":[${items}]}" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo 'matrix={"include":[{"name":"redis-es8","script":"./e2e/run_tests-es8.sh"}]}' >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
e2e:
|
|
needs: generate-e2e-matrix
|
|
if: ${{ needs.generate-e2e-matrix.outputs.matrix != '{"include":[]}' }}
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix: ${{ fromJson(needs.generate-e2e-matrix.outputs.matrix) }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event.pull_request.head.sha }}
|
|
fetch-depth: 0
|
|
- name: Free disk space
|
|
run: |
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo rm -rf /usr/local/lib/android
|
|
sudo rm -rf /opt/ghc
|
|
- name: Set up Zulu JDK 21
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: "zulu"
|
|
java-version: "21"
|
|
- name: Cache Gradle packages
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
restore-keys: ${{ runner.os }}-gradle-
|
|
- name: Run E2E tests (${{ matrix.name }})
|
|
run: ${{ matrix.script }}
|
|
- name: Publish Test Report
|
|
uses: mikepenz/action-junit-report@v6
|
|
if: always()
|
|
with:
|
|
report_paths: "e2e/build/test-results/test/TEST-*.xml"
|
|
- name: Generate test summary table
|
|
if: always()
|
|
shell: python3 {0}
|
|
run: |
|
|
import os, glob, xml.etree.ElementTree as ET
|
|
|
|
xml_files = glob.glob("e2e/build/test-results/test/TEST-*.xml")
|
|
rows = []
|
|
totals = {"passed": 0, "failed": 0, "skipped": 0}
|
|
|
|
for f in sorted(xml_files):
|
|
try:
|
|
root = ET.parse(f).getroot()
|
|
except ET.ParseError:
|
|
continue
|
|
for tc in root.iter("testcase"):
|
|
name = tc.get("name", "?")
|
|
classname = tc.get("classname", "").split(".")[-1]
|
|
duration = float(tc.get("time", 0))
|
|
if tc.find("skipped") is not None:
|
|
status, totals["skipped"] = "⏭ skip", totals["skipped"] + 1
|
|
elif tc.find("failure") is not None or tc.find("error") is not None:
|
|
node = tc.find("failure") if tc.find("failure") is not None else tc.find("error")
|
|
msg = (node.get("message") or "")[:120]
|
|
status, totals["failed"] = f"❌ `{msg}`", totals["failed"] + 1
|
|
else:
|
|
status, totals["passed"] = "✅", totals["passed"] + 1
|
|
rows.append((classname, name, f"{duration:.1f}s", status))
|
|
|
|
backend = "${{ matrix.name }}"
|
|
lines = [
|
|
f"## E2E results — {backend}",
|
|
f"**✅ {totals['passed']} passed · ❌ {totals['failed']} failed · ⏭ {totals['skipped']} skipped**",
|
|
"",
|
|
"| Class | Test | Duration | Result |",
|
|
"|-------|------|----------|--------|",
|
|
]
|
|
for cls, name, dur, status in rows:
|
|
lines.append(f"| {cls} | {name} | {dur} | {status} |")
|
|
|
|
summary = os.environ.get("GITHUB_STEP_SUMMARY", "/dev/null")
|
|
with open(summary, "a") as fh:
|
|
fh.write("\n".join(lines) + "\n")
|
|
- name: Upload E2E reports
|
|
uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: e2e-reports-${{ matrix.name }}
|
|
path: "e2e/build/reports"
|
|
|
|
build-ui:
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: ui
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
- name: Set up Node
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: "22"
|
|
cache: "yarn"
|
|
cache-dependency-path: ui/yarn.lock
|
|
|
|
- name: Install Dependencies
|
|
run: yarn install
|
|
|
|
- name: Build UI
|
|
run: yarn run build
|
|
|
|
- name: Cache Playwright browsers
|
|
uses: actions/cache@v5
|
|
id: playwright-cache
|
|
with:
|
|
path: ~/.cache/ms-playwright
|
|
key: playwright-chromium-${{ hashFiles('ui/yarn.lock') }}
|
|
|
|
- name: Install Playwright browsers
|
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
|
run: npx playwright install --with-deps chromium
|
|
|
|
- name: Install Playwright browser deps (cached)
|
|
if: steps.playwright-cache.outputs.cache-hit == 'true'
|
|
run: npx playwright install-deps chromium
|
|
|
|
- name: Run Playwright E2E Tests
|
|
run: yarn test:e2e
|
|
|
|
- name: Upload Playwright report
|
|
uses: actions/upload-artifact@v7
|
|
if: failure()
|
|
with:
|
|
name: playwright-report
|
|
path: ui/playwright-report
|
|
retention-days: 7
|