name: Nightly Build Oracle # Oracle module is tested separately because it requires a lot of disk space # and does not fit in one job together with other modules on: schedule: - cron: '0 6 * * *' # daily at 06:00 UTC workflow_dispatch: permissions: contents: read jobs: build: if: github.repository == 'langchain4j/langchain4j' runs-on: ubuntu-latest strategy: fail-fast: false steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up JDK 17 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: java-version: 17 distribution: 'temurin' cache: 'maven' - name: Build with JDK 17 run: | ## compile and install ALL modules to avoid running integration tests on dependent modules in the step below mvn -B -U -T8C -DskipTests -DskipITs -DembeddingsSkipCache install mvn -B -U verify \ -pl langchain4j-oracle \ --fail-at-end \ -Djunit.jupiter.extensions.autodetection.enabled=true \ -DembeddingsSkipCache \ -Dtinylog.writer.level=info env: LC4J_GLOBAL_TEST_RETRY_ENABLED: true - name: Clean Docker run: docker system prune -af || true - name: Publish Test Summary if: always() run: | python3 << 'PYEOF' import xml.etree.ElementTree as ET import glob, os, re files = glob.glob('**/target/*-reports/TEST-*.xml', recursive=True) total = failures = errors = skipped = 0 failed_tests = [] for path in files: try: root = ET.parse(path).getroot() total += int(root.get('tests', 0)) failures += int(root.get('failures', 0)) errors += int(root.get('errors', 0)) skipped += int(root.get('skipped', 0)) for tc in root.findall('.//testcase'): fail = tc.find('failure') if fail is None: fail = tc.find('error') if fail is not None: cls = tc.get('classname', '') name = tc.get('name', '') trace = fail.text or '' m = re.search(r'\bat ' + re.escape(cls) + r'[.$][^\n(]*\(([^)]+\.java:\d+)\)', trace) loc = f'`{m.group(1)}`' if m else '' msg = (fail.get('message', '') or '').replace('\n', '
').replace('|', '\\|') if len(msg) > 500: msg = f"{msg[:500]}
show more{msg[500:]}
" failed_tests.append(f"| `{cls}` | `{name}` | {loc} | {msg} |") except Exception: pass passed = total - failures - errors - skipped summary = os.environ.get('GITHUB_STEP_SUMMARY', '/dev/null') with open(summary, 'a') as f: f.write('## Test Results\n\n') if failures == 0 and errors == 0: f.write(f':white_check_mark: **{passed} passed**, {skipped} skipped out of {total} tests\n') else: f.write(f':x: **{failures} failed, {errors} errors**, {passed} passed, {skipped} skipped out of {total} tests\n') if failed_tests: f.write('\n### Failures\n\n') f.write('| Class | Test | Location | Error Message |\n') f.write('|-------|------|----------|---------------|\n') for line in failed_tests[:50]: f.write(line + '\n') if len(failed_tests) > 50: f.write(f'\n*... and {len(failed_tests) - 50} more*\n') PYEOF - name: Upload Test Reports if: always() # always run even if the previous step failed or was cancelled uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: Test-Reports-${{ strategy.job-index }} path: '**/target/*-reports/*' - name: Upload JaCoCo Reports if: always() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: jacoco-${{ strategy.job-index }} path: '**/target/site/jacoco/**' - name: Upload JaCoCo Execution Data if: always() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: jacoco-exec-${{ strategy.job-index }} path: '**/target/jacoco.exec'