fix(datagrid): carry identity columns through so a new row pre-fills DEFAULT #2085
Workflow file for this run
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: macOS Tests | |
| on: | |
| # No paths filter here on purpose. A required status check has to report on every pull | |
| # request, and a workflow skipped by a paths filter reports nothing at all, which leaves | |
| # the check pending forever and blocks docs-only work. The filtering moved into the | |
| # changes job below, so the gate job always runs and always reports. | |
| pull_request: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "TablePro/**" | |
| - "Plugins/**" | |
| - "Packages/**" | |
| - "LocalPackages/**" | |
| - "TableProTests/**" | |
| - "TableProUITests/**" | |
| - "Native/**" | |
| - "project.yml" | |
| - "TableProMobile/project.yml" | |
| - "Configs/**" | |
| - "Libs/**" | |
| - "scripts/**" | |
| - "TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" | |
| - ".github/actions/**" | |
| - ".github/macos-test-quarantine.txt" | |
| - ".github/macos-ui-test-quarantine.txt" | |
| - ".github/workflows/macos-tests.yml" | |
| workflow_dispatch: | |
| workflow_call: | |
| # One run per PR/branch at a time. On a pull request a new push cancels the run in flight, because | |
| # only the newest commit's verdict is wanted and the older one is about to be replaced anyway. | |
| # | |
| # A push to main is different: its run is the only verdict that commit will ever get, and cancelling | |
| # it leaves main untested. That is not hypothetical. Of the last 30 main runs of this workflow, 13 | |
| # were cancelled by the next merge, and the merges are frequent enough that whole days passed with | |
| # no conclusive result on main at all. | |
| # | |
| # Queueing them does not pile up. With cancel-in-progress false GitHub still replaces the *pending* | |
| # run in the group: "any existing pending job or workflow in the same concurrency group will be | |
| # canceled and the new queued job or workflow will take its place." So a burst of merges holds at | |
| # most one running plus one queued, and the one that ends up queued is the newest. | |
| concurrency: | |
| group: macos-tests-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| # A downgrade or an exact match is always allowed for a called workflow; only asking for more than | |
| # the caller holds fails at run creation. Declaring read here therefore cannot break build.yml's | |
| # workflow_call, and it stops every job in this file from inheriting a write token if the repository | |
| # default is ever flipped. | |
| permissions: | |
| contents: read | |
| env: | |
| XCODE_VERSION: "26.4.1" | |
| XCODE_PROJECT: TablePro.xcodeproj | |
| XCODE_SCHEME: TablePro | |
| TEST_DESTINATION: "platform=macOS" | |
| # Inside the workspace rather than the default location, because the build job tars it and the | |
| # test jobs unpack it. Every path inside the generated .xctestrun is written relative to | |
| # __TESTROOT__, so the products relocate to any directory. | |
| DERIVED_DATA: DerivedData | |
| jobs: | |
| # Reproduces the paths filter this workflow used to carry on `on: pull_request`. Keep this | |
| # list and the `push:` paths above identical, so a pull request and a push to main run the | |
| # same suites. `Native/` is on both because the Dameng steps below build and test the Rust | |
| # bridge that lives there, and a bridge-only change would otherwise compile nowhere. | |
| changes: | |
| name: Detect relevant changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| run: ${{ steps.decide.outputs.run }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: Decide whether the macOS suites need to run | |
| id: decide | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| REF: ${{ github.ref }} | |
| # The reasoning lives in scripts/ci/detect-changed-paths.sh. It was inline here and in | |
| # ios-tests.yml, with a comment asking a human to keep the two copies in step and nothing | |
| # enforcing it. | |
| run: | | |
| set -euo pipefail | |
| RUN=$(scripts/ci/detect-changed-paths.sh \ | |
| --event "$EVENT_NAME" \ | |
| --base "$BASE_SHA" \ | |
| --ref "$REF" \ | |
| --skip-release-commit \ | |
| 'TablePro/' 'Plugins/' 'Packages/' 'LocalPackages/' \ | |
| 'TableProTests/' 'TableProUITests/' 'Native/' 'Configs/' 'Libs/' 'scripts/' \ | |
| '\.github/actions/' '\.github/macos-(ui-)?test-quarantine\.txt$' \ | |
| 'TablePro\.xcodeproj/project\.xcworkspace/xcshareddata/swiftpm/Package\.resolved$' \ | |
| 'TableProMobile/project\.yml$' 'project\.yml$' \ | |
| '\.github/workflows/macos-tests\.yml$') | |
| echo "run=$RUN" >> "$GITHUB_OUTPUT" | |
| packages: | |
| name: Package Tests | |
| needs: changes | |
| if: needs.changes.outputs.run == 'true' | |
| runs-on: macos-26 | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Select Xcode | |
| uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0 | |
| with: | |
| xcode-version: ${{ env.XCODE_VERSION }} | |
| - name: Run TableProCore package tests | |
| run: swift test --package-path Packages/TableProCore | |
| # CodeEditSourceEditor is deliberately not run here. `swift test` cannot build it on this | |
| # runner: its CodeEditSymbols dependency fails with "type 'Bundle' has no member 'module'", | |
| # after a SwiftPM cache warning about a missing maintenance.lock. It builds fine inside the | |
| # Xcode project, so the app target still compiles it; only the standalone package test run | |
| # is broken. Its own suites also fail on main (HighlighterTests, TagEditingTests, plus one | |
| # that aborts the runner), so this needs fixing at the package level before it can gate. | |
| - name: Run CodeEditTextView package tests | |
| run: swift test --package-path LocalPackages/CodeEditTextView | |
| # Compiles the app, both test bundles and all 31 plugins once, then hands the products to the | |
| # test jobs. That build used to happen inside the same job that ran the tests, which meant the | |
| # unit step spent 12 of its 15.3 minutes compiling and the UI step could not start until the | |
| # unit step had finished. All 12,662 unit cases execute in 206 seconds, so the build was almost | |
| # all of the cost and none of it was parallel. | |
| build: | |
| name: Build for testing | |
| needs: changes | |
| if: needs.changes.outputs.run == 'true' | |
| runs-on: macos-26 | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Select Xcode | |
| uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0 | |
| with: | |
| xcode-version: ${{ env.XCODE_VERSION }} | |
| - name: Install xcbeautify | |
| run: brew list xcbeautify &>/dev/null || brew install xcbeautify | |
| - name: Cache static libraries | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: Libs | |
| key: ${{ runner.os }}-libs-${{ hashFiles('Libs/checksums.sha256', 'Plugins/MSSQLDriverPlugin/CFreeTDS/include/sybdb.h') }} | |
| - name: Download static libraries | |
| env: | |
| 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 | |
| run: scripts/build-dameng.sh arm64 | |
| # Only the bridge's own FFI tests live here. The DM8 protocol crates are a pinned | |
| # revision of TableProApp/rust-dameng and carry their own suite in that repository. | |
| # Run from the bridge directory so rustup reads its rust-toolchain.toml: rustup resolves | |
| # that file from the working directory, not from --manifest-path, and testing on the | |
| # runner's default Rust would not be the toolchain the shipped staticlib is built with. | |
| - name: Test Dameng native bridge | |
| working-directory: Native/DamengBridge | |
| run: cargo test --locked | |
| # Secrets.xcconfig is gitignored. Tests do not need analytics keys, and the project only | |
| # needs the variable to resolve, so an empty value is enough and no secret is read here. | |
| - name: Create Secrets.xcconfig | |
| run: echo "ANALYTICS_HMAC_SECRET = " > Configs/Secrets.xcconfig | |
| - name: Setup XcodeGen | |
| uses: ./.github/actions/setup-xcodegen | |
| - name: Generate Xcode project | |
| run: scripts/generate-project.sh | |
| # Package.resolved pins every revision and is tracked, which is why it can key the cache. | |
| # build.yml has cached these checkouts for a while; this workflow paid 1.5 minutes a run | |
| # re-resolving them. | |
| - name: Cache Swift package checkouts | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.spm-cache | |
| key: ${{ runner.os }}-spm-${{ hashFiles('TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }} | |
| restore-keys: ${{ runner.os }}-spm- | |
| - name: Resolve Swift package dependencies | |
| run: | | |
| xcodebuild -resolvePackageDependencies \ | |
| -project "$XCODE_PROJECT" \ | |
| -scheme "$XCODE_SCHEME" \ | |
| -clonedSourcePackagesDirPath ~/.spm-cache \ | |
| -skipPackagePluginValidation | |
| # The TablePro scheme only builds the 14 bundled plugins, so a change confined to one | |
| # of the 17 registry-only plugins compiles nowhere else in CI. AllPlugins is the only | |
| # target that covers them, and build-plugin.yml does not run until a release tag. | |
| - name: Compile every plugin | |
| run: | | |
| set -o pipefail | |
| xcodebuild build \ | |
| -project "$XCODE_PROJECT" \ | |
| -scheme AllPlugins \ | |
| -destination "$TEST_DESTINATION" \ | |
| -derivedDataPath "$DERIVED_DATA" \ | |
| -clonedSourcePackagesDirPath ~/.spm-cache \ | |
| -skipPackagePluginValidation \ | |
| CODE_SIGNING_ALLOWED=NO \ | |
| | xcbeautify --renderer github-actions | |
| # Builds and signs used to be the whole check: nothing ever tried to load one. "Bundle | |
| # failed to load executable" has shipped twice, and dlopen is exactly the dyld path that | |
| # fails in that case. | |
| - name: Verify every plugin loads | |
| run: scripts/ci/verify-plugin-loads.sh "$DERIVED_DATA/Build/Products/Debug" | |
| - name: Build the app and both test bundles | |
| run: | | |
| set -o pipefail | |
| xcodebuild build-for-testing \ | |
| -project "$XCODE_PROJECT" \ | |
| -scheme "$XCODE_SCHEME" \ | |
| -destination "$TEST_DESTINATION" \ | |
| -derivedDataPath "$DERIVED_DATA" \ | |
| -clonedSourcePackagesDirPath ~/.spm-cache \ | |
| -skipPackagePluginValidation \ | |
| CODE_SIGNING_ALLOWED=NO \ | |
| | xcbeautify --renderer github-actions | |
| # DamengDriverTests is the one plugin-owned unit bundle: its suites import CDameng, so | |
| # they cannot move into TableProTests the way pure-logic plugin tests do. Without this | |
| # step nothing runs them and they rot silently. | |
| - name: Build the Dameng driver tests | |
| run: | | |
| set -o pipefail | |
| xcodebuild build-for-testing \ | |
| -project "$XCODE_PROJECT" \ | |
| -scheme DamengDriverTests \ | |
| -destination "$TEST_DESTINATION" \ | |
| -derivedDataPath "$DERIVED_DATA" \ | |
| -clonedSourcePackagesDirPath ~/.spm-cache \ | |
| -skipPackagePluginValidation \ | |
| CODE_SIGNING_ALLOWED=NO \ | |
| | xcbeautify --renderer github-actions | |
| # Tarred rather than uploaded as a directory. actions/upload-artifact does not preserve | |
| # the executable bit or symlinks, and both matter to an .app bundle: the app would arrive | |
| # unable to run. The archive measures 144 MB against 559 MB on disk. | |
| - name: Pack the built products | |
| run: tar -czf products.tar.gz -C "$DERIVED_DATA/Build" Products | |
| - name: Upload the built products | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: test-products | |
| path: products.tar.gz | |
| retention-days: 1 | |
| compression-level: 0 | |
| unit: | |
| name: Unit tests | |
| needs: [changes, build] | |
| if: needs.changes.outputs.run == 'true' | |
| runs-on: macos-26 | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Select Xcode | |
| uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0 | |
| with: | |
| xcode-version: ${{ env.XCODE_VERSION }} | |
| - name: Install xcbeautify | |
| run: brew list xcbeautify &>/dev/null || brew install xcbeautify | |
| - uses: ./.github/actions/unpack-test-products | |
| # Quarantined cases are listed one per line in .github/macos-test-quarantine.txt. Burn that | |
| # list down. The script enumerates the target first and refuses to run if any entry would | |
| # skip nothing, because xcodebuild accepts -skip-testing for a case that does not exist and | |
| # says nothing: that left one line inert for 479 commits, and it is also what happens to a | |
| # Swift Testing case written without its parentheses. | |
| - name: Run unit tests | |
| run: | | |
| set -o pipefail | |
| # To a file on its own line, not through a process substitution. `< <(...)` discards the | |
| # producer's exit status, so the script refusing an entry that would skip nothing printed | |
| # its diagnostic, exited 1, and the step carried on to run the whole suite unskipped and | |
| # report success. The refusal the comment above promises never once failed a job. | |
| SKIP_FILE="${RUNNER_TEMP:-.}/quarantine-skip-args.txt" | |
| scripts/ci/quarantine-args.sh .github/macos-test-quarantine.txt TableProTests "$XCTESTRUN" "$TEST_DESTINATION" > "$SKIP_FILE" | |
| # A read loop, not mapfile: the macOS runner's /bin/bash is 3.2, which has neither | |
| # mapfile nor readarray, and a `run:` block gets that bash. | |
| SKIP_ARGS=() | |
| while IFS= read -r arg; do | |
| # A blank line would become a literal empty argument, and xcodebuild reads that as a | |
| # build action and refuses the whole run. An empty array expands to nothing and is | |
| # fine; one empty string is not. | |
| [ -n "$arg" ] || continue | |
| SKIP_ARGS+=("$arg") | |
| done < "$SKIP_FILE" | |
| xcodebuild test-without-building \ | |
| -xctestrun "$XCTESTRUN" \ | |
| -destination "$TEST_DESTINATION" \ | |
| -only-testing:TableProTests \ | |
| ${SKIP_ARGS[@]+"${SKIP_ARGS[@]}"} \ | |
| -parallel-testing-enabled NO \ | |
| -resultBundlePath TestResults.xcresult \ | |
| | xcbeautify --renderer github-actions | |
| - name: Run Dameng driver tests | |
| run: | | |
| set -o pipefail | |
| xcodebuild test-without-building \ | |
| -xctestrun "$DAMENG_XCTESTRUN" \ | |
| -destination "$TEST_DESTINATION" \ | |
| -parallel-testing-enabled NO \ | |
| | xcbeautify --renderer github-actions | |
| # Always, including on failure: a red run is when the durations matter most. | |
| - name: Summarise test durations | |
| if: ${{ !cancelled() }} | |
| run: scripts/ci/summarize-xcresult.sh TestResults.xcresult "Unit tests" | |
| - name: Upload test results | |
| if: ${{ !cancelled() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: macos-test-results | |
| path: TestResults.xcresult | |
| retention-days: 7 | |
| # UI tests drive the real app, so they need the runner's GUI session and cannot run beside | |
| # the unit run. Every case launches the app against a throwaway storage sandbox that | |
| # UITestCase hands it, so nothing here touches a real store. | |
| # | |
| # Sharded across runners rather than parallelised on one. Three things in the suite are shared | |
| # per machine and cannot be made per-worker: the single `com.TablePro.uitest` defaults domain, | |
| # the preferences sweep in UITestCase's class setUp, and the menu bar itself, which belongs to | |
| # whichever app is frontmost. A separate runner per shard has its own of all three. | |
| ui: | |
| name: UI tests ${{ matrix.shard }}/${{ matrix.of }} | |
| needs: [changes, build] | |
| if: needs.changes.outputs.run == 'true' | |
| runs-on: macos-26 | |
| timeout-minutes: 40 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [0, 1, 2] | |
| of: [3] | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Select Xcode | |
| uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0 | |
| with: | |
| xcode-version: ${{ env.XCODE_VERSION }} | |
| - name: Install xcbeautify | |
| run: brew list xcbeautify &>/dev/null || brew install xcbeautify | |
| - uses: ./.github/actions/unpack-test-products | |
| # One retry is allowed. A full local suite flaked once in three runs on a single | |
| # waitForExistence, which without a retry would fail roughly a third of runs and train | |
| # people to re-run until green. A case that fails twice is a real failure. | |
| - name: Run UI tests | |
| env: | |
| SHARD: ${{ matrix.shard }} | |
| SHARD_COUNT: ${{ matrix.of }} | |
| run: | | |
| set -o pipefail | |
| # To a file on its own line, so a failure of the lister fails the step. A process | |
| # substitution discards its exit status, which reads here as "the shard selected nothing". | |
| ONLY_FILE="${RUNNER_TEMP:-.}/shard-only-args.txt" | |
| scripts/ci/list-tests.sh \ | |
| --xctestrun "$XCTESTRUN" \ | |
| --target TableProUITests \ | |
| --quarantine .github/macos-ui-test-quarantine.txt \ | |
| --shard "$SHARD/$SHARD_COUNT" > "$ONLY_FILE" | |
| # A read loop, not mapfile: the macOS runner's /bin/bash is 3.2, which has neither. | |
| ONLY_ARGS=() | |
| while IFS= read -r arg; do | |
| # The count check below cannot see an empty argument: one blank line makes the count 1 | |
| # and xcodebuild then reads that empty argument as a build action. | |
| [ -n "$arg" ] || continue | |
| ONLY_ARGS+=("$arg") | |
| done < "$ONLY_FILE" | |
| [ "${#ONLY_ARGS[@]}" -gt 0 ] || { echo "::error::shard $SHARD selected no cases"; exit 1; } | |
| echo "shard $SHARD of $SHARD_COUNT runs ${#ONLY_ARGS[@]} cases" | |
| xcodebuild test-without-building \ | |
| -xctestrun "$XCTESTRUN" \ | |
| -destination "$TEST_DESTINATION" \ | |
| "${ONLY_ARGS[@]}" \ | |
| -parallel-testing-enabled NO \ | |
| -test-iterations 2 \ | |
| -retry-tests-on-failure \ | |
| -resultBundlePath UITestResults.xcresult \ | |
| | xcbeautify --renderer github-actions | |
| - name: Summarise UI test durations | |
| if: ${{ !cancelled() }} | |
| run: scripts/ci/summarize-xcresult.sh UITestResults.xcresult "UI tests ${{ matrix.shard }}/${{ matrix.of }}" | |
| - name: Upload UI test results | |
| if: ${{ !cancelled() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: macos-ui-test-results-${{ matrix.shard }} | |
| path: UITestResults.xcresult | |
| retention-days: 7 | |
| # The one check to mark required on main. It reports on every pull request, so a docs-only | |
| # change passes on skipped suites instead of hanging, and a red suite cannot be merged past. | |
| gate: | |
| name: macOS Tests Gate | |
| if: always() | |
| needs: [changes, packages, build, unit, ui] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Require every suite to have passed or been skipped | |
| env: | |
| RESULTS: ${{ join(needs.*.result, ' ') }} | |
| run: | | |
| set -euo pipefail | |
| echo "suite results: $RESULTS" | |
| for result in $RESULTS; do | |
| case "$result" in | |
| success|skipped) ;; | |
| *) | |
| echo "A macOS suite reported '$result'." >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| done |