From 7e085af4aeec37497b135a9ed06bcca0d8551729 Mon Sep 17 00:00:00 2001 From: Magnus Persson <1431685+magnusp@users.noreply.github.com> Date: Wed, 21 Oct 2026 23:20:10 +0200 Subject: [PATCH 1/2] fix(conformance): enforce vendored upstream checksums Make the manifest the single source of truth for the conformance copy and the export tool. Verify CHECKSUMS.sha256 in CI and during the conformance build, copy only the manifest set, and delete stale generated sources on every build. Record upstream hashes separately from current hashes so a deliberate divergence requires an updated checksum and a rationale in the manifest. Add a self-test that proves drift, unlisted files, deleted files, and unrecorded divergences are rejected. --- .github/workflows/ci.yml | 6 + .github/workflows/pr.yml | 6 + libsql-conformance/pom.xml | 51 ++- tools/export-upstream | 430 ++++++++++++++++--- upstream-xerial_sqlite-jdbc.manifest | 10 + upstream-xerial_sqlite-jdbc/CHECKSUMS.sha256 | 23 +- 6 files changed, 456 insertions(+), 70 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0508abb..f251baa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,12 @@ jobs: java-version: ${{ matrix.java-version }} cache: 'maven' + - name: Verify vendored upstream integrity + run: ./tools/export-upstream --verify + + - name: Self-check vendoring enforcement + run: ./tools/export-upstream --self-test + - name: Check Code Formatting (Spotless) run: mvn --batch-mode spotless:check diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5a41960..89484d3 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -27,6 +27,12 @@ jobs: java-version: ${{ matrix.java-version }} cache: 'maven' + - name: Verify vendored upstream integrity + run: ./tools/export-upstream --verify + + - name: Self-check vendoring enforcement + run: ./tools/export-upstream --self-test + - name: Check Code Formatting (Spotless) run: mvn --batch-mode spotless:check diff --git a/libsql-conformance/pom.xml b/libsql-conformance/pom.xml index 37a727c..00085a0 100644 --- a/libsql-conformance/pom.xml +++ b/libsql-conformance/pom.xml @@ -59,35 +59,58 @@ 3.3.1 - copy-xerial-test-sources - generate-test-sources + copy-docker-config + pre-integration-test copy-resources - ${project.build.directory}/generated-test-sources/xerial + ${project.build.directory}/docker-config - ${project.basedir}/../upstream-xerial_sqlite-jdbc/src/test/java + ${project.basedir}/src/test/docker-config false + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.2.0 + - copy-docker-config - pre-integration-test + verify-vendored-upstream + validate - copy-resources + run - ${project.build.directory}/docker-config - - - ${project.basedir}/src/test/docker-config - false - - + + + + + + + + + + copy-xerial-test-sources + generate-test-sources + + run + + + + + + + + diff --git a/tools/export-upstream b/tools/export-upstream index 390059e..524199c 100755 --- a/tools/export-upstream +++ b/tools/export-upstream @@ -4,31 +4,47 @@ set -euo pipefail usage() { cat <<'EOF' Usage: export-upstream [options] + export-upstream --verify [--manifest FILE] + export-upstream --self-test Export xerial/sqlite-jdbc at the given tag into upstream-xerial_sqlite-jdbc/ and update upstream-xerial_sqlite-jdbc/UPSTREAM.md. The tag is resolved to its commit SHA with the GitHub API, and both the tag and the SHA are recorded. +The selected files come from upstream-xerial_sqlite-jdbc.manifest by default. +The manifest is the single source of truth for both the export and the +libsql-conformance build. + Requires the GitHub CLI, authenticated with `gh auth login`. Examples: export-upstream 3.53.4.0 export-upstream 3.53.4.0 --manifest upstream.manifest + export-upstream --verify Options: - --manifest FILE Repo-relative paths to export, one per line. + --manifest FILE Repo-relative paths, one per line. Blank lines and lines starting with # are ignored. - --all Export the whole repository instead of the default slice. + --all Export the whole repository instead of the manifest. --force Replace a non-empty destination not created by this script. + --verify Check the vendored tree against the manifest and + CHECKSUMS.sha256 without contacting GitHub. + --self-test Exercise the verification rules on a temporary copy of + the vendored tree. -h, --help Show this help. -Default export slice when no manifest is given and --all is not used: - src/test/java - src/test/resources - LICENSE - LICENSE.zentus - NOTICE - VERSION +Default manifest: upstream-xerial_sqlite-jdbc.manifest + +Vendored divergences + CHECKSUMS.sha256 records the upstream hash of every exported file on a + "# upstream ./path" line and the current hash on the following line. + A deliberate edit keeps the upstream line, updates the current hash, and + records a rationale in the manifest: + + # divergence: -- + + --verify rejects an edit whose current hash differs from upstream without a + rationale, and rejects a rationale whose file still matches upstream. The destination must be empty, missing, or marked with .export-upstream. The script never edits UPSTREAM.md content outside its managed markers. @@ -44,36 +60,305 @@ md_cell() { printf '%s' "$1" | sed 's/|/\\|/g' } +sha256_file() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{print $1}' + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$1" | awk '{print $1}' + else + die "sha256sum or shasum is required" + fi +} + +sha256_check() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum -c "$1" + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 -c "$1" + else + die "sha256sum or shasum is required" + fi +} + +check_manifest_path() { + case "$1" in + /*|..|../*|*/../*|*/..) + die "manifest path must be relative and stay inside the repository: $1" + ;; + esac +} + +load_manifest_files() { + local manifest=$1 + local out=$2 + local line + + [ -f "$manifest" ] || die "manifest not found: $manifest" + : > "$out" + while IFS= read -r line || [ -n "$line" ]; do + line=${line%$'\r'} + case "$line" in + ""|"#"*) + continue + ;; + esac + check_manifest_path "$line" + printf '%s\n' "$line" >> "$out" + done < "$manifest" +} + +load_divergences() { + local manifest=$1 + local out=$2 + local line rest path reason + + : > "$out" + while IFS= read -r line || [ -n "$line" ]; do + line=${line%$'\r'} + case "$line" in + "# divergence:"*) + rest=$(printf '%s' "$line" | sed -E 's/^# divergence:[[:space:]]*//') + path=${rest%% *} + reason=$(printf '%s' "$rest" | sed -E 's/^[^ ]+[[:space:]]*(--)?[[:space:]]*//') + if [ -z "$path" ] || [ -z "$reason" ]; then + die "malformed divergence rationale: $line" + fi + check_manifest_path "$path" + printf '%s\t%s\n' "$path" "$reason" >> "$out" + ;; + esac + done < "$manifest" +} + +verify_tree() { + local manifest=$1 + local entry path current upstream saved + local bad=0 + + [ -f "$dest/.export-upstream" ] || die "not an export-upstream tree: $dest" + + load_manifest_files "$manifest" "$tmp/manifest.raw" + [ -s "$tmp/manifest.raw" ] || die "manifest is empty: $manifest" + + : > "$tmp/manifest.paths" + while IFS= read -r entry; do + if [ -f "$dest/$entry" ]; then + printf '%s\n' "$entry" >> "$tmp/manifest.paths" + elif [ -d "$dest/$entry" ]; then + (cd "$dest" && find "$entry" -type f -print) + else + die "manifest path not found in $dest: $entry" + fi + done < "$tmp/manifest.raw" + + LC_ALL=C sort "$tmp/manifest.paths" -o "$tmp/manifest.paths.sorted" + if uniq -d "$tmp/manifest.paths.sorted" | grep -q .; then + die "manifest contains duplicate paths: $manifest" + fi + mv "$tmp/manifest.paths.sorted" "$tmp/manifest.paths" + + ( + cd "$dest" + find . -type f \ + ! -name CHECKSUMS.sha256 \ + ! -name .export-upstream \ + ! -name UPSTREAM.md \ + -print \ + | sed 's|^\./||' + ) | LC_ALL=C sort > "$tmp/actual.paths" + + if ! diff -u "$tmp/manifest.paths" "$tmp/actual.paths" > "$tmp/tree.diff"; then + cat "$tmp/tree.diff" >&2 + die "vendored tree does not match $manifest" + fi + + [ -f "$dest/CHECKSUMS.sha256" ] || die "missing $dest/CHECKSUMS.sha256" + if ! (cd "$dest" && sha256_check CHECKSUMS.sha256 >/dev/null); then + die "CHECKSUMS.sha256 does not match the vendored tree" + fi + + awk ' + /^# upstream / { + p = $4 + sub(/^\.\//, "", p) + if (p in upstream) { + print "DUPLICATE_UPSTREAM\t" p + bad = 1 + } + upstream[p] = $3 + next + } + /^#/ { + next + } + NF >= 2 { + p = $2 + sub(/^\.\//, "", p) + if (p in current) { + print "DUPLICATE_CURRENT\t" p + bad = 1 + } + current[p] = $1 + next + } + END { + for (p in current) { + if (!(p in upstream)) { + print "MISSING_UPSTREAM\t" p + bad = 1 + } else { + print "ENTRY\t" p "\t" current[p] "\t" upstream[p] + } + } + for (p in upstream) { + if (!(p in current)) { + print "MISSING_CURRENT\t" p + bad = 1 + } + } + exit bad + } + ' "$dest/CHECKSUMS.sha256" > "$tmp/checksums.tsv" || die "malformed CHECKSUMS.sha256" + + load_divergences "$manifest" "$tmp/divergences" + + while IFS=$'\t' read -r entry path current upstream; do + case "$entry" in + ENTRY) + if ! grep -Fqx "$path" "$tmp/manifest.paths"; then + echo "checksum entry not in manifest: $path" >&2 + bad=1 + fi + if [ "$current" != "$upstream" ] \ + && ! awk -F '\t' -v path="$path" '$1 == path { found = 1 } END { exit !found }' "$tmp/divergences"; then + echo "divergence without rationale in $manifest: $path" >&2 + bad=1 + fi + ;; + *) + echo "$entry: $path" >&2 + bad=1 + ;; + esac + done < "$tmp/checksums.tsv" + + while IFS=$'\t' read -r path saved; do + [ -n "$saved" ] || { + echo "empty divergence rationale in $manifest: $path" >&2 + bad=1 + } + if ! grep -Fqx "$path" "$tmp/manifest.paths"; then + echo "divergence path not in manifest: $path" >&2 + bad=1 + continue + fi + entry=$(awk -F '\t' -v path="$path" '$1 == "ENTRY" && $2 == path { print $3 "\t" $4 }' "$tmp/checksums.tsv") + if [ -z "$entry" ]; then + echo "divergence path has no checksums: $path" >&2 + bad=1 + continue + fi + current=${entry%%$'\t'*} + upstream=${entry#*$'\t'} + if [ "$current" = "$upstream" ]; then + echo "divergence rationale but file matches upstream: $path" >&2 + bad=1 + fi + done < "$tmp/divergences" + + [ "$bad" -eq 0 ] || die "vendored upstream verification failed" + echo "Verified $dest against $manifest" +} + +self_test_reset() { + rm -rf "$self_root/$dest" + cp -a "$dest" "$self_root/$dest" + cp "$manifest" "$self_root/$manifest" +} + +self_test_expect_pass() { + local label=$1 + + if ! "$self_root/tools/export-upstream" --verify > "$tmp/self-test.out" 2>&1; then + cat "$tmp/self-test.out" >&2 + die "self-test: $label was rejected" + fi +} + +self_test_expect_fail() { + local label=$1 + + if "$self_root/tools/export-upstream" --verify > "$tmp/self-test.out" 2>&1; then + die "self-test: $label was accepted" + fi +} + +self_test() { + local path="src/test/java/org/sqlite/Utils.java" + local file hash + + self_root="$tmp/self-test" + mkdir -p "$self_root/tools" + cp -a "$dest" "$self_root/$dest" + cp "$manifest" "$self_root/$manifest" + cp "$repo_root/tools/export-upstream" "$self_root/tools/export-upstream" + chmod +x "$self_root/tools/export-upstream" + + self_test_expect_pass "pristine tree" + + file="$self_root/$dest/$path" + printf ' ' >> "$file" + self_test_expect_fail "one-byte change" + + self_test_reset + printf 'class ExtraTest {}\n' > "$self_root/$dest/src/test/java/org/sqlite/ExtraTest.java" + self_test_expect_fail "unlisted vendored file" + + self_test_reset + rm "$self_root/$dest/$path" + self_test_expect_fail "deleted vendored file" + + self_test_reset + file="$self_root/$dest/$path" + printf ' ' >> "$file" + hash=$(sha256_file "$file") + awk -v path="./$path" -v hash="$hash" ' + /^#/ { print; next } + $2 == path { print hash " " $2; next } + { print } + ' "$self_root/$dest/CHECKSUMS.sha256" > "$self_root/$dest/CHECKSUMS.sha256.new" + mv "$self_root/$dest/CHECKSUMS.sha256.new" "$self_root/$dest/CHECKSUMS.sha256" + self_test_expect_fail "divergence without rationale" + + printf '\n# divergence: %s -- self-test rationale\n' "$path" >> "$self_root/$manifest" + self_test_expect_pass "recorded divergence" + + self_test_reset + mv "$self_root/$manifest" "$self_root/manifest.hidden" + self_test_expect_fail "missing default manifest" + + echo "Self-test passed" +} + repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) cd "$repo_root" repo="xerial/sqlite-jdbc" dest="upstream-xerial_sqlite-jdbc" upstream_md="$dest/UPSTREAM.md" -manifest="" +manifest="upstream-xerial_sqlite-jdbc.manifest" manifest_set=0 export_all=0 force=0 +verify=0 +self_test_enabled=0 +tag="" if [ "$#" -lt 1 ]; then usage exit 2 fi -case "$1" in - -h|--help) - usage - exit 0 - ;; - -*) - usage - exit 2 - ;; -esac - -tag="$1" -shift - while [ "$#" -gt 0 ]; do case "$1" in --manifest) @@ -90,16 +375,57 @@ while [ "$#" -gt 0 ]; do force=1 shift ;; + --verify) + verify=1 + shift + ;; + --self-test) + self_test_enabled=1 + shift + ;; -h|--help) usage exit 0 ;; - *) + -*) die "unknown option: $1" ;; + *) + [ -z "$tag" ] || die "unexpected argument: $1" + tag="$1" + shift + ;; esac done +if [ "$self_test_enabled" -eq 1 ]; then + [ -z "$tag" ] || die "--self-test does not take a tag" + [ "$export_all" -eq 0 ] || die "--self-test cannot be used with --all" + [ "$force" -eq 0 ] || die "--self-test cannot be used with --force" + [ "$verify" -eq 0 ] || die "--self-test cannot be used with --verify" + + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + self_test + exit 0 +fi + +if [ "$verify" -eq 1 ]; then + [ -z "$tag" ] || die "--verify does not take a tag" + [ "$export_all" -eq 0 ] || die "--verify cannot be used with --all" + [ "$force" -eq 0 ] || die "--verify cannot be used with --force" + + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + verify_tree "$manifest" + exit 0 +fi + +[ -n "$tag" ] || { + usage + exit 2 +} + if [ "$export_all" -eq 1 ] && [ "$manifest_set" -eq 1 ]; then die "--all and --manifest cannot be used together" fi @@ -139,23 +465,10 @@ if [ "$export_all" -eq 1 ]; then paths_label="whole repository" else paths=() - if [ -n "$manifest" ]; then - [ -f "$manifest" ] || die "manifest not found: $manifest" - while IFS= read -r line || [ -n "$line" ]; do - line=${line%$'\r'} - case "$line" in - ""|"#"*) - continue - ;; - /*|..|../*|*/../*|*/..) - die "manifest path must be relative and stay inside the repository: $line" - ;; - esac - paths+=("$line") - done < "$manifest" - else - paths=(src/test/java src/test/resources LICENSE LICENSE.zentus NOTICE VERSION) - fi + load_manifest_files "$manifest" "$tmp/manifest.paths" + while IFS= read -r line; do + paths+=("$line") + done < "$tmp/manifest.paths" [ "${#paths[@]}" -gt 0 ] || die "manifest is empty: $manifest" @@ -237,18 +550,27 @@ else fi mv "$upstream_md.tmp" "$upstream_md" -if command -v sha256sum >/dev/null 2>&1; then - ( - cd "$dest" - find . -type f \ - ! -name CHECKSUMS.sha256 \ - ! -name .export-upstream \ - ! -name UPSTREAM.md \ - -print0 \ - | sort -z \ - | xargs -0 -r sha256sum > CHECKSUMS.sha256 - ) -fi +( + cd "$dest" + find . -type f \ + ! -name CHECKSUMS.sha256 \ + ! -name .export-upstream \ + ! -name UPSTREAM.md \ + -print \ + | sed 's|^\./||' +) | LC_ALL=C sort > "$tmp/checksum.paths" + +{ + echo '# Generated by tools/export-upstream. Do not edit by hand except for a deliberate divergence.' + echo '# The uncommented line is the current hash; "# upstream" is the hash at the pinned tag.' + echo '# A deliberate divergence keeps the upstream line, updates the current line, and records' + echo '# "# divergence: -- " in upstream-xerial_sqlite-jdbc.manifest.' +} > "$dest/CHECKSUMS.sha256" +while IFS= read -r path; do + hash=$(sha256_file "$dest/$path") + printf '# upstream %s ./%s\n' "$hash" "$path" >> "$dest/CHECKSUMS.sha256" + printf '%s ./%s\n' "$hash" "$path" >> "$dest/CHECKSUMS.sha256" +done < "$tmp/checksum.paths" echo "Exported tag $tag ($sha) to $dest" echo "Updated $upstream_md" diff --git a/upstream-xerial_sqlite-jdbc.manifest b/upstream-xerial_sqlite-jdbc.manifest index 2381609..a839a9d 100644 --- a/upstream-xerial_sqlite-jdbc.manifest +++ b/upstream-xerial_sqlite-jdbc.manifest @@ -1,3 +1,13 @@ +# Source of truth for the vendored xerial/sqlite-jdbc tree: the conformance +# build copies these paths, and tools/export-upstream exports these paths by +# default. Run `tools/export-upstream --verify` after any change. +# +# A deliberate divergence from upstream keeps the "# upstream" hash in +# upstream-xerial_sqlite-jdbc/CHECKSUMS.sha256, updates the current hash, and +# adds a line in this file: +# # divergence: -- +# Use the reason to say which libSQL or Hrana behavior makes parity impossible. +# # First conformance batch. src/test/java/org/sqlite/Utils.java src/test/java/org/sqlite/ResultSetTest.java diff --git a/upstream-xerial_sqlite-jdbc/CHECKSUMS.sha256 b/upstream-xerial_sqlite-jdbc/CHECKSUMS.sha256 index 0240387..234b581 100644 --- a/upstream-xerial_sqlite-jdbc/CHECKSUMS.sha256 +++ b/upstream-xerial_sqlite-jdbc/CHECKSUMS.sha256 @@ -1,15 +1,34 @@ +# Generated by tools/export-upstream. Do not edit by hand except for a deliberate divergence. +# The uncommented line is the current hash; "# upstream" is the hash at the pinned tag. +# A deliberate divergence keeps the upstream line, updates the current line, and records +# "# divergence: -- " in upstream-xerial_sqlite-jdbc.manifest. +# upstream 3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5 ./LICENSE 3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5 ./LICENSE +# upstream 89167dab92289c7e5e2b65b044f0856b703d05e5d5e35c3548e73d9c7d2f5048 ./LICENSE.zentus 89167dab92289c7e5e2b65b044f0856b703d05e5d5e35c3548e73d9c7d2f5048 ./LICENSE.zentus +# upstream a0670cf020edf700524c43c85321c2ba027a8bace5682001db60875d4a9fe52b ./NOTICE a0670cf020edf700524c43c85321c2ba027a8bace5682001db60875d4a9fe52b ./NOTICE +# upstream 0a60829ce6edd56de46fb87303401c5c1e1e666da5631056340f5c5ce902dcdf ./VERSION +0a60829ce6edd56de46fb87303401c5c1e1e666da5631056340f5c5ce902dcdf ./VERSION +# upstream 1351fdca2644e90ac9028ed99c6096c9ecee82ccb7d45cedd9bb454887fd2c6b ./src/test/java/org/sqlite/CachedRowSetTest.java 1351fdca2644e90ac9028ed99c6096c9ecee82ccb7d45cedd9bb454887fd2c6b ./src/test/java/org/sqlite/CachedRowSetTest.java +# upstream 9fb0f2df7de66cac0a46b241c0c1cd3311a80646d0431d377097741b99053760 ./src/test/java/org/sqlite/FetchSizeTest.java 9fb0f2df7de66cac0a46b241c0c1cd3311a80646d0431d377097741b99053760 ./src/test/java/org/sqlite/FetchSizeTest.java +# upstream 6dfcecb209e01829ba0aea782b47351443e3970ad1d33ac4c3fb979e6dff1f40 ./src/test/java/org/sqlite/InsertQueryTest.java 6dfcecb209e01829ba0aea782b47351443e3970ad1d33ac4c3fb979e6dff1f40 ./src/test/java/org/sqlite/InsertQueryTest.java +# upstream bb07398946480ca44c70d03e6f8c399f53506b44c83a312e2d28aebd239c0238 ./src/test/java/org/sqlite/JSON1Test.java bb07398946480ca44c70d03e6f8c399f53506b44c83a312e2d28aebd239c0238 ./src/test/java/org/sqlite/JSON1Test.java +# upstream 242bb51330240174bab5f443aa4d8de1049e6d947374e080a636c4aa4d0049ee ./src/test/java/org/sqlite/MetadataLeakTest.java 242bb51330240174bab5f443aa4d8de1049e6d947374e080a636c4aa4d0049ee ./src/test/java/org/sqlite/MetadataLeakTest.java +# upstream 7af8ae0999a0bf26a20c96ab23131505bd79827e6af17742771e36e9d8d985f4 ./src/test/java/org/sqlite/PreparedStatementThreadTest.java 7af8ae0999a0bf26a20c96ab23131505bd79827e6af17742771e36e9d8d985f4 ./src/test/java/org/sqlite/PreparedStatementThreadTest.java +# upstream d787e71bdf2b8d12a2669eb06c432cf2a39ac77f678a92459e3dd9b6c4519a43 ./src/test/java/org/sqlite/RSMetaDataTest.java +d787e71bdf2b8d12a2669eb06c432cf2a39ac77f678a92459e3dd9b6c4519a43 ./src/test/java/org/sqlite/RSMetaDataTest.java +# upstream 6065d284933c8462cc13217992820a9261075c0e9ee126df6cbe9961d90303f9 ./src/test/java/org/sqlite/ResultSetTest.java 6065d284933c8462cc13217992820a9261075c0e9ee126df6cbe9961d90303f9 ./src/test/java/org/sqlite/ResultSetTest.java +# upstream 958c1f2ff5ca2f1257dfd847207ea793839969b234d140576b51af346697233c ./src/test/java/org/sqlite/ResultSetWithoutResultsTest.java 958c1f2ff5ca2f1257dfd847207ea793839969b234d140576b51af346697233c ./src/test/java/org/sqlite/ResultSetWithoutResultsTest.java -d787e71bdf2b8d12a2669eb06c432cf2a39ac77f678a92459e3dd9b6c4519a43 ./src/test/java/org/sqlite/RSMetaDataTest.java +# upstream ed2fb4bf7319bcba0662b83707375501337173956bae7c57dda6e6a2c46b7505 ./src/test/java/org/sqlite/TypeMapTest.java ed2fb4bf7319bcba0662b83707375501337173956bae7c57dda6e6a2c46b7505 ./src/test/java/org/sqlite/TypeMapTest.java +# upstream bef3eddca2a6b80472050a3a52d5e7246128f0644d29c24ed091709b0ed6f121 ./src/test/java/org/sqlite/Utils.java bef3eddca2a6b80472050a3a52d5e7246128f0644d29c24ed091709b0ed6f121 ./src/test/java/org/sqlite/Utils.java -0a60829ce6edd56de46fb87303401c5c1e1e666da5631056340f5c5ce902dcdf ./VERSION From 95e891241639826c0b33d1aecf270d7c4a0e0daf Mon Sep 17 00:00:00 2001 From: Magnus Persson <1431685+magnusp@users.noreply.github.com> Date: Wed, 21 Oct 2026 23:26:05 +0200 Subject: [PATCH 2/2] fix(ci): stop vendoring check from breaking root validate The antrun run goal requires test-scope dependency resolution, so binding verify-vendored-upstream to validate made the dependency-submission job resolve unpublished sibling artifacts. Run the check at generate-test-sources instead, where the conformance build already needs the test dependencies, and add a root validate step to CI to guard the aggregator. --- .github/workflows/ci.yml | 3 +++ .github/workflows/pr.yml | 3 +++ libsql-conformance/pom.xml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f251baa..6e5280c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,5 +35,8 @@ jobs: - name: Check Code Formatting (Spotless) run: mvn --batch-mode spotless:check + - name: Validate reactor + run: mvn --batch-mode validate + - name: Run Tests and Verifications run: mvn --batch-mode clean verify diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 89484d3..fcc2888 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -36,5 +36,8 @@ jobs: - name: Check Code Formatting (Spotless) run: mvn --batch-mode spotless:check + - name: Validate reactor + run: mvn --batch-mode validate + - name: Run Tests and Verifications run: mvn --batch-mode clean verify diff --git a/libsql-conformance/pom.xml b/libsql-conformance/pom.xml index 00085a0..f1eba3d 100644 --- a/libsql-conformance/pom.xml +++ b/libsql-conformance/pom.xml @@ -83,7 +83,7 @@ verify-vendored-upstream - validate + generate-test-sources run