Nightly #6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Nightly | |
| # build.yml runs on push and pull_request, so it only tells you about changes | |
| # made HERE. This repository's other moving part is outside it: the parser | |
| # artifact published to https://www.sqlparser.com/maven/. A release there can | |
| # change demo output or drop an API without a single commit landing in this | |
| # repo, and nothing would notice until someone ran a demo by hand. | |
| # | |
| # So this workflow runs on a timer and covers both: | |
| # pinned -- ${gsp.core.version} from pom.xml, the version the README | |
| # documents, on the two JDKs the demos claim to support. | |
| # latest -- whatever is newest on sqlparser.com right now. This is the job | |
| # that earns the nightly. When it goes red and `pinned` stays | |
| # green, a new parser release broke the demos. | |
| # | |
| # Both jobs do the same four things: prove no parser jar is committed, compile | |
| # everything, run the whole test suite, and then actually run the demos -- | |
| # every one of them for launch, and a table of them with real arguments checked | |
| # against expected output. | |
| on: | |
| schedule: | |
| # 03:17 UTC. Not on the hour: GitHub drops scheduled runs when too many | |
| # repositories ask for the same popular minute. | |
| - cron: "17 3 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| parser_version: | |
| description: "Parser version for the 'latest' job (blank = resolve newest from sqlparser.com)" | |
| required: false | |
| type: string | |
| permissions: | |
| contents: read | |
| jobs: | |
| pinned: | |
| name: "pinned parser (JDK ${{ matrix.java }})" | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # The parser jar is Java 8 bytecode and the POM pins source/target 1.8. | |
| java: ["8", "21"] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: ${{ matrix.java }} | |
| cache: maven | |
| # A vendored parser is what once let these demos compile against a build | |
| # years older than their own source, so its absence is an invariant worth | |
| # asserting rather than a convention worth documenting. | |
| - name: No parser jar is committed | |
| run: | | |
| set -euo pipefail | |
| if git ls-files | grep -E '(^|/)(gsqlparser|gudusoft\.gsqlparser)[^/]*\.jar$'; then | |
| echo "::error::a parser jar is committed; it must be resolved from Gudu's Maven repository" | |
| exit 1 | |
| fi | |
| echo "ok: no parser jar in the index" | |
| - name: Parser version is consistent across all POMs | |
| run: .github/scripts/set-parser-version.sh --check | |
| - name: Report the parser being used | |
| run: | | |
| set -euo pipefail | |
| v=$(mvn -B -q help:evaluate -Dexpression=gsp.core.version -DforceStdout) | |
| echo "pinned parser version: $v" | |
| echo "### Pinned parser \`$v\` (JDK ${{ matrix.java }})" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Compile everything | |
| run: mvn -B package -DskipTests | |
| - name: Test | |
| run: mvn -B test | |
| - name: Check the results | |
| run: .github/scripts/check-test-results.sh | |
| # The standalone lineage tool, run rather than merely built. It was its own | |
| # POM until 2026-07-28, and only ever compiled here, which is how it | |
| # shipped a missing JAXB dependency (issue #47) and an unrunnable | |
| # documented command (issue #46) through green builds. | |
| - name: Run the standalone dlineage jar | |
| run: .github/scripts/smoke-dlineage-jar.sh | |
| - name: Every demo starts | |
| run: .github/scripts/run-all-demos.sh --timeout 90 | |
| - name: Demos produce their expected output | |
| run: .github/scripts/run-demo-cases.sh --timeout 180 | |
| latest: | |
| name: "latest published parser" | |
| runs-on: ubuntu-latest | |
| # Needed only by the last step, which opens the bump PR. Everything above it | |
| # is read-only. | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: "21" | |
| cache: maven | |
| # <release> is the newest non-snapshot; fall back to <latest>, then to the | |
| # last <version> listed. Failing to resolve is an error rather than a | |
| # silent fallback to the pinned version: a job that quietly re-tests what | |
| # `pinned` already covers looks green while testing nothing new. | |
| - name: Resolve the newest published parser | |
| id: ver | |
| run: | | |
| set -euo pipefail | |
| if [ -n "${{ inputs.parser_version }}" ]; then | |
| v="${{ inputs.parser_version }}" | |
| echo "using the version supplied to workflow_dispatch: $v" | |
| else | |
| url=https://www.sqlparser.com/maven/com/gudusoft/gsqlparser/maven-metadata.xml | |
| curl -sSf --retry 3 --retry-delay 5 -o meta.xml "$url" | |
| v=$(sed -n 's:.*<release>\(.*\)</release>.*:\1:p' meta.xml | head -1) | |
| [ -n "$v" ] || v=$(sed -n 's:.*<latest>\(.*\)</latest>.*:\1:p' meta.xml | head -1) | |
| [ -n "$v" ] || v=$(sed -n 's:.*<version>\(.*\)</version>.*:\1:p' meta.xml | tail -1) | |
| if [ -z "$v" ]; then | |
| echo "::error::could not read a version out of $url" | |
| cat meta.xml | |
| exit 1 | |
| fi | |
| fi | |
| pinned=$(mvn -B -q help:evaluate -Dexpression=gsp.core.version -DforceStdout) | |
| echo "version=$v" >> "$GITHUB_OUTPUT" | |
| echo "newest published: $v / pinned in pom.xml: $pinned" | |
| { | |
| echo "### Latest published parser \`$v\`" | |
| if [ "$v" = "$pinned" ]; then | |
| echo "Same as the pinned version, so this job duplicates \`pinned\`." | |
| else | |
| echo "Pinned version is \`$pinned\`. If this job is red and \`pinned\` is green," | |
| echo "release \`$v\` broke the demos. If it is green, \`gsp.core.version\`" | |
| echo "can be bumped to \`$v\`." | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Compile everything | |
| run: mvn -B package -DskipTests -Dgsp.core.version=${{ steps.ver.outputs.version }} | |
| - name: Test | |
| run: mvn -B test -Dgsp.core.version=${{ steps.ver.outputs.version }} | |
| - name: Check the results | |
| run: .github/scripts/check-test-results.sh | |
| # Same smoke run as the pinned job, against the jar "Compile everything" | |
| # just built with -Dgsp.core.version=<newest release>. This is the check | |
| # that would notice a new parser breaking data lineage at runtime while | |
| # still compiling, which is precisely the failure mode a build-only step | |
| # cannot see. | |
| - name: Run the standalone dlineage jar against the newest release | |
| run: .github/scripts/smoke-dlineage-jar.sh | |
| - name: Every demo starts | |
| env: | |
| MVN_ARGS: -Dgsp.core.version=${{ steps.ver.outputs.version }} | |
| run: .github/scripts/run-all-demos.sh --timeout 90 | |
| - name: Demos produce their expected output | |
| env: | |
| MVN_ARGS: -Dgsp.core.version=${{ steps.ver.outputs.version }} | |
| run: .github/scripts/run-demo-cases.sh --timeout 180 | |
| # Everything above passed against this version, so the bump is already | |
| # proven rather than proposed on faith: full suite, every demo started, | |
| # and the demo cases checked against expected output. Opening a PR instead | |
| # of committing keeps a human in the loop and gives the push-triggered | |
| # build.yml a chance to disagree. | |
| # | |
| # Deliberately the LAST step in the job: any earlier failure means the new | |
| # release broke something, and then no PR is what we want. | |
| - name: Propose the bump when the newest release is green | |
| if: github.ref == 'refs/heads/master' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NEW: ${{ steps.ver.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| pinned=$(mvn -B -q help:evaluate -Dexpression=gsp.core.version -DforceStdout) | |
| if [ "$NEW" = "$pinned" ]; then | |
| echo "already on $NEW, nothing to propose" | |
| exit 0 | |
| fi | |
| branch="bump-parser-$NEW" | |
| if gh pr list --head "$branch" --state open --json number \ | |
| --jq 'length' | grep -qv '^0$'; then | |
| echo "a PR for $branch is already open" | |
| exit 0 | |
| fi | |
| if git ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then | |
| echo "branch $branch already exists on origin; leaving it alone" | |
| exit 0 | |
| fi | |
| .github/scripts/set-parser-version.sh "$NEW" | |
| .github/scripts/set-parser-version.sh --check | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git checkout -b "$branch" | |
| git commit -aqm "Bump the parser from $pinned to $NEW | |
| The nightly ran the newest published release through the same checks | |
| the pinned version gets, and it passed all of them: | |
| - full test suite, no failures | |
| - every demo starts | |
| - the demo cases in .github/scripts/demo-cases.tsv produce their | |
| expected output | |
| Opened automatically by .github/workflows/nightly.yml. Merge it if you | |
| want the demos on $NEW; close it to stay on $pinned, and the nightly | |
| will keep testing $NEW without asking again until a newer release | |
| appears." | |
| git push -q origin "$branch" | |
| gh pr create --base master --head "$branch" \ | |
| --title "Bump the parser from $pinned to $NEW" \ | |
| --body "\`$NEW\` is the newest release on sqlparser.com. Tonight's nightly ran it through the same checks the pinned \`$pinned\` gets, and it passed all of them: the full test suite with no failures, every demo starting, and every case in \`.github/scripts/demo-cases.tsv\` producing its expected output. | |
| This touches the four places the version is written -- \`pom.xml\` and the three \`connector/*/pom.xml\` -- via \`.github/scripts/set-parser-version.sh\`, so they cannot drift apart. | |
| Merge to move the demos to \`$NEW\`. Close to stay on \`$pinned\`; the nightly will go on testing \`$NEW\` and will not reopen this until a newer release appears. | |
| Opened by [\`nightly.yml\`](https://github.com/${{ github.repository }}/actions/workflows/nightly.yml)." | |
| # The .bat family is the original Windows, no-Maven workflow. It went stale | |
| # for years because nothing ran it; build.yml runs it per push and this runs | |
| # it nightly, against a parser fetched fresh rather than a cached one. | |
| windows-bat: | |
| name: "Windows .bat scripts" | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: "8" | |
| cache: maven | |
| - name: setenv.bat bootstraps the parser from Maven | |
| shell: cmd | |
| run: | | |
| if exist external_lib\gsqlparser-*.jar ( | |
| echo ::error::a parser jar is committed to this repository; it must be fetched, not vendored | |
| exit /b 1 | |
| ) | |
| call setenv\fetch-parser.bat | |
| dir external_lib | |
| for %%f in (external_lib\gsqlparser-*.jar) do exit /b 0 | |
| echo ::error::fetch-parser.bat produced no parser jar | |
| exit /b 1 | |
| - name: Compile every demo via compile_*.bat | |
| shell: cmd | |
| run: | | |
| setlocal enabledelayedexpansion | |
| set FAILED=0 | |
| set COUNT=0 | |
| for /r "%GITHUB_WORKSPACE%\src\main\java" %%s in (compile_*.bat) do ( | |
| set /a COUNT+=1 | |
| pushd "%%~dps" | |
| call "%%~nxs" < NUL > "%GITHUB_WORKSPACE%\c.txt" 2>&1 | |
| findstr /i /c:"error" /c:"file not found" /c:"no source files" "%GITHUB_WORKSPACE%\c.txt" >NUL && ( | |
| echo ::error::%%~nxs failed | |
| type "%GITHUB_WORKSPACE%\c.txt" | |
| set /a FAILED+=1 | |
| ) || echo ok %%~nxs | |
| popd | |
| ) | |
| echo. | |
| echo compiled !COUNT! demos, !FAILED! failed | |
| rem An explicit exit is required: a trailing `if` whose condition is | |
| rem false leaves whatever ERRORLEVEL the loop last set. | |
| if !FAILED! GTR 0 exit /b 1 | |
| exit /b 0 | |
| - name: Launch every demo via run_*.bat | |
| shell: cmd | |
| run: | | |
| setlocal enabledelayedexpansion | |
| set FAILED=0 | |
| set COUNT=0 | |
| for /r "%GITHUB_WORKSPACE%\src\main\java" %%s in (run_*.bat) do ( | |
| set /a COUNT+=1 | |
| pushd "%%~dps" | |
| call "%%~nxs" < NUL > "%GITHUB_WORKSPACE%\r.txt" 2>&1 | |
| findstr /c:"ClassNotFoundException" /c:"NoClassDefFoundError" /c:"Main method not found" "%GITHUB_WORKSPACE%\r.txt" >NUL && ( | |
| echo ::error::%%~nxs could not launch its class | |
| type "%GITHUB_WORKSPACE%\r.txt" | |
| set /a FAILED+=1 | |
| ) || echo ok %%~nxs | |
| popd | |
| ) | |
| echo. | |
| echo launched !COUNT! demos, !FAILED! failed | |
| if !FAILED! GTR 0 exit /b 1 | |
| exit /b 0 | |
| - name: Run four demos with real arguments | |
| shell: cmd | |
| run: | | |
| echo SELECT a.id FROM ta a; > "%GITHUB_WORKSPACE%\q.sql" | |
| call :demo checksyntax checksyntax checksyntax "/f %GITHUB_WORKSPACE%\q.sql /t oracle" "syntax errors: 0" || exit /b 1 | |
| call :demo formatsql formatsql formatsql "%GITHUB_WORKSPACE%\q.sql" "SELECT" || exit /b 1 | |
| call :demo listGSPInfo listGSPInfo listGSPInfo "" "Supported DBs" || exit /b 1 | |
| call :demo modifysql modifysql replaceTablename "" "output sql" || exit /b 1 | |
| echo All .bat demos passed. | |
| exit /b 0 | |
| :demo | |
| setlocal | |
| set DEMO=%~1 | |
| set CSCRIPT=%~2 | |
| set RSCRIPT=%~3 | |
| set ARGS=%~4 | |
| set EXPECT=%~5 | |
| echo. | |
| echo ==== %DEMO% : compile_%CSCRIPT%.bat / run_%RSCRIPT%.bat ==== | |
| cd /d "%GITHUB_WORKSPACE%\src\main\java\gudusoft\gsqlparser\demos\%DEMO%" | |
| call compile_%CSCRIPT%.bat < NUL > "%GITHUB_WORKSPACE%\c.txt" 2>&1 | |
| type "%GITHUB_WORKSPACE%\c.txt" | |
| findstr /i /c:"error" "%GITHUB_WORKSPACE%\c.txt" >NUL && ( | |
| echo ::error::compile_%CSCRIPT%.bat reported an error | |
| endlocal & exit /b 1 | |
| ) | |
| call run_%RSCRIPT%.bat %ARGS% < NUL > "%GITHUB_WORKSPACE%\r.txt" 2>&1 | |
| type "%GITHUB_WORKSPACE%\r.txt" | |
| findstr /c:"%EXPECT%" "%GITHUB_WORKSPACE%\r.txt" >NUL || ( | |
| echo ::error::run_%RSCRIPT%.bat did not print "%EXPECT%" | |
| endlocal & exit /b 1 | |
| ) | |
| endlocal & exit /b 0 |