diff --git a/.github/workflows/macos-tests.yml b/.github/workflows/macos-tests.yml index 6755e9e0c..4e9778653 100644 --- a/.github/workflows/macos-tests.yml +++ b/.github/workflows/macos-tests.yml @@ -186,6 +186,12 @@ jobs: GH_TOKEN: ${{ github.token }} run: scripts/download-libs.sh + # Compiles a C probe against Libs/libbson and parses every filter document the MongoDB + # query builder can emit. It guarded that invariant and ran nowhere; it needs clang and the + # vendored libraries, so this is the first job where it can run at all. + - name: Check the MongoDB filter shapes against the query builder + run: scripts/check-mongodb-filter-shapes.sh + # Cargo and rustup need access outside Xcode's user-script sandbox. Build the # native bridge explicitly before compiling the driver target. - name: Build Dameng native bridge diff --git a/.github/workflows/repo-hygiene.yml b/.github/workflows/repo-hygiene.yml index 97dd7eefd..1ea1bef83 100644 --- a/.github/workflows/repo-hygiene.yml +++ b/.github/workflows/repo-hygiene.yml @@ -14,7 +14,10 @@ on: - ".github/actions/**" - ".github/scripts/**" - ".github/plugin-registry.json" + - ".github/duplicate-contract-baseline.txt" - "Plugins/**/*.swift" + - "TablePro/**/*.swift" + - "Packages/**/*.swift" push: branches: [main] paths: @@ -23,7 +26,10 @@ on: - ".github/actions/**" - ".github/scripts/**" - ".github/plugin-registry.json" + - ".github/duplicate-contract-baseline.txt" - "Plugins/**/*.swift" + - "TablePro/**/*.swift" + - "Packages/**/*.swift" workflow_dispatch: concurrency: @@ -68,3 +74,10 @@ jobs: - name: Validate the registry update script run: python3 .github/scripts/test_update_registry.py + + # A check that guarded a real invariant and ran nowhere. Pure grep over Swift sources, so it + # belongs on the free Linux runner. The MongoDB filter-shape check is the other one that was + # orphaned, but it compiles a C probe against Libs/libbson, so it lives in the macOS build + # job in macos-tests.yml where the toolchain and the libraries already are. + - name: Check the shared-contract drift gates + run: scripts/audit-refactor-health.sh --check diff --git a/scripts/build-duckdb-ios.sh b/scripts/build-duckdb-ios.sh index b1d02694d..553b1f0e1 100755 --- a/scripts/build-duckdb-ios.sh +++ b/scripts/build-duckdb-ios.sh @@ -198,8 +198,8 @@ echo echo "Done: $OUT_DIR/DuckDB.xcframework" echo echo "Next steps (these modify the libs-v1 release; run them yourself):" -echo " tar czf /tmp/tablepro-libs-ios-v1.tar.gz -C \"$REPO_ROOT/Libs/ios\" ." -echo " gh release upload libs-v1 /tmp/tablepro-libs-ios-v1.tar.gz --clobber --repo TableProApp/TablePro" +echo " scripts/publish-ios-libs.sh" +echo " git add Libs/ios/checksums.sha256 && git commit -m 'build: update iOS xcframework checksums'" echo echo "Then build TableProMobile in Xcode. Cleaning up $WORK_DIR" rm -rf "$WORK_DIR" diff --git a/scripts/build-freetds.sh b/scripts/build-freetds.sh index 578c20991..245583448 100755 --- a/scripts/build-freetds.sh +++ b/scripts/build-freetds.sh @@ -22,7 +22,17 @@ IOS_LIBS_DIR="$LIBS_DIR/ios" FREETDS_VERSION="1.4.22" FREETDS_SHA256="6acb9086350425f5178e544bbe2d54a001097e8e20277a2b766ad0799a2e7d87" FREETDS_URL="https://www.freetds.org/files/stable/freetds-${FREETDS_VERSION}.tar.gz" -BUILD_DIR="/tmp/freetds-build" +# A private directory with a cleanup trap. This was a fixed /tmp path, which is world-writable and +# therefore something another local user can own first, and it collided between two concurrent +# runs. The four mktemp dirs below were also never removed, so a run leaked all five. +BUILD_DIR="$(mktemp -d)" +TEMP_DIRS=("$BUILD_DIR") +cleanup_freetds() { rm -rf "${TEMP_DIRS[@]}"; } +trap cleanup_freetds EXIT + +# Keeps the downloaded tarballs across runs, which the fixed build root used to provide for free. +CACHE_DIR="${TMPDIR:-/tmp}/tablepro-freetds-cache" +mkdir -p "$CACHE_DIR" SOURCE_DIR="$BUILD_DIR/freetds-${FREETDS_VERSION}" MACOS_DEPLOYMENT_TARGET="14.0" IOS_DEPLOYMENT_TARGET="17.0" @@ -39,14 +49,17 @@ fi mkdir -p "$BUILD_DIR" "$LIBS_DIR" "$IOS_LIBS_DIR" +# Cached outside the per-run build directory, so moving off a fixed /tmp build root does not turn +# every run into a fresh download. Verified on every run, not only after a download. +TARBALL="$CACHE_DIR/freetds-${FREETDS_VERSION}.tar.gz" echo "==> Downloading FreeTDS ${FREETDS_VERSION}..." -if [ ! -f "$BUILD_DIR/freetds-${FREETDS_VERSION}.tar.gz" ]; then - curl -fSL "$FREETDS_URL" -o "$BUILD_DIR/freetds-${FREETDS_VERSION}.tar.gz" +if [ ! -f "$TARBALL" ]; then + curl -fSL "$FREETDS_URL" -o "$TARBALL" fi -echo "$FREETDS_SHA256 $BUILD_DIR/freetds-${FREETDS_VERSION}.tar.gz" | shasum -a 256 -c - +echo "$FREETDS_SHA256 $TARBALL" | shasum -a 256 -c - rm -rf "$SOURCE_DIR" -tar xz -C "$BUILD_DIR" -f "$BUILD_DIR/freetds-${FREETDS_VERSION}.tar.gz" +tar xz -C "$BUILD_DIR" -f "$TARBALL" # FreeTDS has never implemented the TDS FEDAUTH login extension, so Microsoft Entra ID # authentication rides as a patch on the pinned release tarball. Upstream tracks the gap in @@ -134,8 +147,12 @@ build_slice() { # macOS slices use per-arch static OpenSSL from Libs/ to avoid brew's arm64-only dylib at the # linker step. Brew supplies headers (arch-agnostic); the .a files come from Libs/. -MACOS_OPENSSL_ARM64="$(mktemp -d)/openssl-macos-arm64" -MACOS_OPENSSL_X86_64="$(mktemp -d)/openssl-macos-x86_64" +MACOS_OPENSSL_ARM64_ROOT="$(mktemp -d)" +TEMP_DIRS+=("$MACOS_OPENSSL_ARM64_ROOT") +MACOS_OPENSSL_ARM64="${MACOS_OPENSSL_ARM64_ROOT}/openssl-macos-arm64" +MACOS_OPENSSL_X86_64_ROOT="$(mktemp -d)" +TEMP_DIRS+=("$MACOS_OPENSSL_X86_64_ROOT") +MACOS_OPENSSL_X86_64="${MACOS_OPENSSL_X86_64_ROOT}/openssl-macos-x86_64" mkdir -p "$MACOS_OPENSSL_ARM64/include/openssl" "$MACOS_OPENSSL_ARM64/lib" mkdir -p "$MACOS_OPENSSL_X86_64/include/openssl" "$MACOS_OPENSSL_X86_64/lib" cp -R "$MACOS_OPENSSL_PREFIX/include/openssl/." "$MACOS_OPENSSL_ARM64/include/openssl/" @@ -183,8 +200,12 @@ cp "$LIBS_DIR/libsybdb_macos_universal.a" "$LIBS_DIR/libsybdb.a" # iOS slices link OpenSSL statically from the existing xcframeworks; reconstruct a unix-style prefix # for FreeTDS's --with-openssl which expects include/ and lib/ siblings. -IOS_OPENSSL_DEVICE="$(mktemp -d)/openssl-ios-arm64" -IOS_OPENSSL_SIM="$(mktemp -d)/openssl-ios-arm64-simulator" +IOS_OPENSSL_DEVICE_ROOT="$(mktemp -d)" +TEMP_DIRS+=("$IOS_OPENSSL_DEVICE_ROOT") +IOS_OPENSSL_DEVICE="${IOS_OPENSSL_DEVICE_ROOT}/openssl-ios-arm64" +IOS_OPENSSL_SIM_ROOT="$(mktemp -d)" +TEMP_DIRS+=("$IOS_OPENSSL_SIM_ROOT") +IOS_OPENSSL_SIM="${IOS_OPENSSL_SIM_ROOT}/openssl-ios-arm64-simulator" mkdir -p "$IOS_OPENSSL_DEVICE/include" "$IOS_OPENSSL_DEVICE/lib" mkdir -p "$IOS_OPENSSL_SIM/include" "$IOS_OPENSSL_SIM/lib" cp -R "$IOS_OPENSSL_SSL_XCFW/ios-arm64/Headers/." "$IOS_OPENSSL_DEVICE/include/" @@ -243,6 +264,6 @@ find "$XCFRAMEWORK_OUT" -mindepth 1 -maxdepth 1 ! -name Info.plist -exec basenam echo "" echo "NEXT STEPS:" echo " 1. Inspect: xcodebuild -checkFirstLaunchStatus; file ${XCFRAMEWORK_OUT}/*/libsybdb.a" -echo " 2. Re-pack iOS libs archive and upload to libs-v1 release:" -echo " tar czf /tmp/tablepro-libs-ios-v1.tar.gz -C ${IOS_LIBS_DIR} ." -echo " gh release upload libs-v1 /tmp/tablepro-libs-ios-v1.tar.gz --clobber --repo TableProApp/TablePro" +echo " 2. Publish the iOS libs and refresh their integrity baseline:" +echo " scripts/publish-ios-libs.sh" +echo " git add Libs/ios/checksums.sha256 && git commit -m 'build: update iOS xcframework checksums'" diff --git a/scripts/check-pluginkit-abi.sh b/scripts/check-pluginkit-abi.sh index f25380311..555560041 100755 --- a/scripts/check-pluginkit-abi.sh +++ b/scripts/check-pluginkit-abi.sh @@ -90,22 +90,17 @@ if diff -u "$work/base.txt" "$work/head.txt"; then exit 0 fi -if [ "${ABI_ACKNOWLEDGED_ADDITIVE:-}" = "1" ]; then - cat <<'EOF' - -::notice::TableProPluginKit public ABI changed vs base (diff above). -The PR carries the abi-additive label: a maintainer reviewed the diff as additive (new defaulted -requirements or non-frozen types), so no version bump is required and the gate passes. -Remove the label if the diff gains a breaking change; the gate will fail again. -EOF - exit 0 -fi - +# This is run by hand, per CLAUDE.md, not by a workflow. It used to have a branch that passed when +# ABI_ACKNOWLEDGED_ADDITIVE=1, set by a CI job that no longer exists, alongside instructions to add +# an `abi-additive` label and re-run. Nothing can set the variable and nothing reads the label, so +# both were telling the reader to do something that has no effect. cat <<'EOF' -::error::TableProPluginKit public ABI changed vs base (diff above). Decide additive vs breaking: - Additive: no version bump. After review, add the abi-additive label to the PR and re-run. - Breaking: bump currentPluginKitVersion + every plugin Info.plist TableProPluginKitVersion, - then run scripts/release-all-plugins.sh . +TableProPluginKit public ABI changed vs base (diff above). Decide additive vs breaking: + Additive: a new requirement with a default, a reordering, or a field on a non-frozen transfer + struct. No version bump, nothing further to do. + Breaking: a changed or removed requirement, a requirement without a default, a case on a @frozen + enum, or a frozen type's layout. Bump currentPluginKitVersion and every plugin + Info.plist TableProPluginKitVersion, then run scripts/release-all-plugins.sh . EOF exit 1 diff --git a/scripts/create-dmg.sh b/scripts/create-dmg.sh index 69e78df8e..8b244eb7a 100755 --- a/scripts/create-dmg.sh +++ b/scripts/create-dmg.sh @@ -25,6 +25,18 @@ SOURCE_APP="${3:-build/Release/${APP_NAME}.app}" DMG_NAME="${APP_NAME}-${VERSION}-${ARCH}.dmg" VOLUME_NAME="${APP_NAME} ${VERSION}" FINAL_DMG="build/Release/$DMG_NAME" + +# The hdiutil fallback below attaches a volume and writes a temp image. Without this, a failure +# anywhere between the attach and the detach leaves both behind, and the next run then fails on a +# volume name that is already mounted. +TEMP_DMG="" +MOUNT_DIR="" +cleanup_dmg() { + [ -n "$MOUNT_DIR" ] && [ -d "$MOUNT_DIR" ] && hdiutil detach "$MOUNT_DIR" -quiet 2> /dev/null || true + [ -n "$TEMP_DMG" ] && rm -f "$TEMP_DMG" || true +} +trap cleanup_dmg EXIT + SIGN_IDENTITY="${SIGN_IDENTITY:-Developer ID Application: Dat Ngo Quoc (D7HJ5TFYCU)}" NOTARIZE="${NOTARIZE:-false}"