Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .claude/rules/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The required check names are in `AGENTS.md` (CI). Reading a red check: `.claude/

- Triggers: pull request, push to main or `release/**`, dispatch. A PR keeps only its newest run; each pushed commit has its own concurrency group and is never cancelled.
- `Changes (macOS)` outputs `run` (detector `macos`) and `plugins` (detector `plugins`). The test job has a 120-minute limit and sets `NSUnbufferedIO=YES`.
- Setup, in order: checkout, setup-xcode, setup-xcodegen, `scripts/download-libs.sh`, `generate-project.sh macos`, SPM restore, resolve, SPM save.
- Setup, in order: checkout, setup-xcode, prepare-build (setup-xcodegen, `scripts/download-libs.sh`, `generate-project.sh macos`, SPM restore), resolve, SPM save.
- One `build-for-testing` with no `-testPlan` builds both plans and writes one `.xctestrun` per plan, with signing, the index store and the compilation cache off.
- `COMPILE_PLUGINS` is true on every push and dispatch, and on a PR when the `plugins` detector is not false. It adds Go, the Dameng and HANA bridge builds, `xcodebuild build -scheme AllPlugins`, then `scripts/ci/verify-plugin-loads.sh`.
- `test-without-building -testPlan Unit`, then `-testPlan UI`. The UI plan runs after a Unit failure too, so one round trip reports both.
Expand All @@ -59,11 +59,11 @@ The required check names are in `AGENTS.md` (CI). Reading a red check: `.claude/

## Release Build (`release-build.yml`)

- Push to main or `release/**`, and dispatch; never a PR. Each commit has its own concurrency group and is never cancelled. Not a required check.
- `Changes (Release)` outputs `run` (detector `release`). `Release Build` runs on `xcode-27` with a 150-minute limit.
- Setup as macOS Tests up to the SPM restore, which it never saves; then Go, `build-dameng.sh both`, `build-hana.sh both` and create-dmg.
- Push to main or `release/**`, and dispatch; never a PR. Never cancelled. Not a required check.
- `Changes (Release)` outputs `run` (detector `release`). `Release Build` runs on `xcode-27` with a 150-minute limit, with VERSION 0.0.0-dev and BUILD 9999999, so build.sh passes the version settings a tag passes.
- Setup as macOS Tests up to the SPM restore, which it never saves; then Go, `build-dameng.sh both` and `build-hana.sh both`.
- `scripts/release/build.sh app --unsigned` archives the `TablePro` scheme; `build.sh plugins --unsigned` builds `RegistryPlugins` and copies the publish set, the targets `scripts/release/registry.py targets` reads from `.github/plugin-registry.json`, to `build/release/plugins`. Both build universal. They run `lipo -verify_arch` once per arch (arm64, x86_64), since Xcode 27's `lipo` takes one arch per call, then `verify-plugin-loads.sh`.
- `scripts/release/package.sh app 0.0.0-dev --unsigned` writes the release assets: the DMG, the `.tar.xz` (extracted and compared with the app) and the dSYMs zip; `package.sh plugins` writes one zip per plugin and the plugin dSYMs zip.
- `scripts/release/package.sh app --unsigned` writes the release assets: the DMG, the `.tar.xz` (extracted and compared with the app) and the dSYMs zip; `package.sh plugins` writes one zip per plugin and the plugin dSYMs zip.
- The plugins build runs after an app failure too. The xcodebuild logs in `build/release/logs` upload only on failure.

## Release, Withdraw and Pages
Expand Down Expand Up @@ -114,11 +114,11 @@ The required check names are in `AGENTS.md` (CI). Reading a red check: `.claude/

- `detect-changed-paths.sh [--base <sha>] macos|plugins|ios|oracle|hana|release` prints `true` or `false`. An unknown suite or flag exits 2.
- A PR passes its base SHA, a push `github.event.before`. No base prints true. The all-zero SHA of a new ref diffs against `git merge-base HEAD origin/main`, so a branch cut from main skips what main already ran; with no merge base it prints true.
- `macos` watches the app, test, plugin, package, native, config and libs folders, `TestPlans/`, `project.yml`, the app's `Package.resolved`, the build scripts and CI scripts it runs, both setup actions and its workflow. `Packages/` covers TableProMacCore.
- `macos` watches the app, test, plugin, package, native, config and libs folders, `TestPlans/`, `project.yml`, the app's `Package.resolved`, the build scripts and CI scripts it runs, the setup actions and its workflow. `Packages/` covers TableProMacCore.
- `plugins` watches `Plugins/`, `Packages/TableProCore/`, `Packages/TableProOracle/`, `Native/`, `Libs/`, `Configs/`, `project.yml`, the bridge scripts and `verify-plugin-loads.sh`.
- `ios` watches `TableProMobile/`, `Libs/`, `Plugins/TableProPluginKit/` (the package target is a symlink to it), and every `../` path it reads out of `TableProMobile/project.yml`.
- `oracle` watches `Packages/TableProOracle/`; `hana` watches `Native/HanaBridge/`. Each suite also watches its own workflow.
- `release` watches what the Release build reads: the app, plugin, package, native, config and libs folders, `project.yml`, the app's `Package.resolved`, `.github/plugin-registry.json`, `scripts/release/`, the build and CI scripts it runs and both setup actions. It skips `Plugins/*Tests/` and `Packages/*/Tests/`.
- `release` watches what the Release build reads: the app, plugin, package, native, config and libs folders, `project.yml`, the app's `Package.resolved`, `.github/plugin-registry.json`, the release scripts it runs (`build.sh`, `package.sh`, `registry.py`, `version.py`, `notes.py`), the build and CI scripts it runs and the setup actions. It skips `Plugins/*Tests/` and `Packages/*/Tests/`.
- A path a suite newly reads joins that suite's pattern in the same change, or the suite skips on it.
- `CHANGELOG.md` and `changelog.d/` are on no suite's pattern: the What's New phase writes nothing at `0.0.0-dev`, and Repo Hygiene renders every fragment and section instead.

Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/native-bridges.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ paths:
- **First-party bridge code lives in `Native/<X>Bridge`**, built by `scripts/build-<x>.sh`, with output in a gitignored folder beside the source, never in `Libs/`.
- **Rust may run in the app process; Go may not.** The Dameng staticlib is built with `panic = "unwind"`, and every export that does driver work wraps it in `catch_unwind`; a new export that does work takes the same guard. Go's `recover()` cannot catch a panic on a goroutine go-hdb starts, so HANA runs out of process as `tablepro-hana-helper`, one per driver session.
- **Toolchains and upstream code are pinned**: `rust-toolchain.toml`, `Cargo.lock` with `--locked`, the `toolchain` line of `go.mod`, `go.sum`. A protocol change for Dameng lands on the [TableProApp/rust-dameng](https://github.com/TableProApp/rust-dameng) fork first, then the pinned `rev` moves.
- **Every nested Mach-O is signed with Developer ID, the hardened runtime and a timestamp** before the main binary. The `HanaHelperEmbedding` copy phase in `project.yml` signs the HANA helper with the plugin's identity, and `scripts/release/build.sh plugins` checks every executable with `codesign -dvvv`.
- **Every nested Mach-O is signed with Developer ID, the hardened runtime and a timestamp** before the main binary. The `HanaHelperEmbedding` copy phase in `project.yml` signs the HANA helper with the plugin's identity; the plugins' notary submission rejects any executable signed otherwise, and `scripts/release/notarize.sh` prints the log.
14 changes: 1 addition & 13 deletions .github/actions/apple-signing/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,5 @@ runs:
run: |
set -euo pipefail
profiles="$HOME/Library/MobileDevice/Provisioning Profiles"
decoded="$RUNNER_TEMP/profile.plist"
mkdir -p "$profiles"
echo "$PROVISIONING_PROFILE" | base64 --decode > "$RUNNER_TEMP/tablepro.provisionprofile"
security cms -D -i "$RUNNER_TEMP/tablepro.provisionprofile" > "$decoded"
name="$(plutil -extract Name raw -o - "$decoded")"
uuid="$(plutil -extract UUID raw -o - "$decoded")"
expiry="$(plutil -extract ExpirationDate raw -o - "$decoded")"
mv "$RUNNER_TEMP/tablepro.provisionprofile" "$profiles/$uuid.provisionprofile"
rm -f "$decoded"
# Both are ISO 8601 in UTC, so they compare as strings.
if [[ "$expiry" < "$(date -u -v+60d +%Y-%m-%dT%H:%M:%SZ)" ]]; then
echo "::warning::The provisioning profile '$name' expires on $expiry"
fi
echo "Installed the provisioning profile '$name' ($uuid), valid until $expiry"
printf '%s' "$PROVISIONING_PROFILE" | base64 --decode > "$profiles/tablepro.provisionprofile"
34 changes: 34 additions & 0 deletions .github/actions/prepare-build/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Prepare build
description: Installs XcodeGen, downloads the static libraries, generates TablePro.xcodeproj and restores the Swift package checkouts.

outputs:
cache-hit:
description: The restore step's cache-hit output.
value: ${{ steps.spm.outputs.cache-hit }}
cache-primary-key:
description: The restore step's cache-primary-key output.
value: ${{ steps.spm.outputs.cache-primary-key }}

runs:
using: composite
steps:
- uses: ./.github/actions/setup-xcodegen

- name: Download static libraries
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: scripts/download-libs.sh

- name: Generate the Xcode project
shell: bash
run: scripts/generate-project.sh macos

# macos-tests.yml saves this cache.
- name: Restore Swift package checkouts
id: spm
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.spm-cache
key: spm-${{ runner.os }}-${{ hashFiles('TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ runner.os }}-
25 changes: 5 additions & 20 deletions .github/workflows/macos-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,37 +54,22 @@ jobs:

- uses: ./.github/actions/setup-xcode

- uses: ./.github/actions/setup-xcodegen

- name: Download static libraries
env:
GH_TOKEN: ${{ github.token }}
run: scripts/download-libs.sh

- name: Generate the Xcode project
run: scripts/generate-project.sh macos

# Saved on a push only: a cache saved on a pull request ref is visible to that PR alone.
- name: Restore Swift package checkouts
id: spm
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.spm-cache
key: spm-${{ runner.os }}-${{ hashFiles('TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ runner.os }}-
- uses: ./.github/actions/prepare-build
id: prepare

- name: Resolve Swift packages
run: |
xcodebuild -resolvePackageDependencies \
-project TablePro.xcodeproj -scheme TablePro \
-clonedSourcePackagesDirPath ~/.spm-cache -skipPackagePluginValidation

# Saved on a push only: a cache saved on a pull request ref is visible to that PR alone.
- name: Save Swift package checkouts
if: github.event_name == 'push' && steps.spm.outputs.cache-hit != 'true'
if: github.event_name == 'push' && steps.prepare.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.spm-cache
key: ${{ steps.spm.outputs.cache-primary-key }}
key: ${{ steps.prepare.outputs.cache-primary-key }}

# One build covers both test plans: build-for-testing writes an .xctestrun per plan.
- name: Build for testing
Expand Down
34 changes: 7 additions & 27 deletions .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ on:
branches: [main, 'release/**']
workflow_dispatch:

# Each commit has a group of its own and is never cancelled, as in macos-tests.yml.
concurrency:
group: release-build-${{ github.sha }}
cancel-in-progress: false

permissions:
contents: read

Expand Down Expand Up @@ -44,28 +39,16 @@ jobs:
if: ${{ !cancelled() && needs.changes.outputs.run != 'false' }}
runs-on: xcode-27
timeout-minutes: 150
# The tag's version settings run here first; 9999999 is the largest build at MAJOR 0 (RELEASING.md, Versions).
env:
VERSION: 0.0.0-dev
BUILD: "9999999"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: ./.github/actions/setup-xcode

- uses: ./.github/actions/setup-xcodegen

- name: Download static libraries
env:
GH_TOKEN: ${{ github.token }}
run: scripts/download-libs.sh

- name: Generate the Xcode project
run: scripts/generate-project.sh macos

# macos-tests.yml saves this cache.
- name: Restore Swift package checkouts
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.spm-cache
key: spm-${{ runner.os }}-${{ hashFiles('TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ runner.os }}-
- uses: ./.github/actions/prepare-build

- name: Set up Go for the SAP HANA helper
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
Expand All @@ -79,9 +62,6 @@ jobs:
scripts/build-dameng.sh both
scripts/build-hana.sh both

- name: Install create-dmg
run: brew list create-dmg > /dev/null 2>&1 || brew install create-dmg

- name: Build the app
id: app
run: scripts/release/build.sh app --unsigned
Expand All @@ -94,11 +74,11 @@ jobs:

- name: Package the app
if: ${{ !cancelled() && steps.app.outcome == 'success' }}
run: scripts/release/package.sh app 0.0.0-dev --unsigned
run: scripts/release/package.sh app --unsigned

- name: Package the plugins
if: ${{ !cancelled() && steps.plugins.outcome == 'success' }}
run: scripts/release/package.sh plugins 0.0.0-dev --unsigned
run: scripts/release/package.sh plugins --unsigned

- name: Upload the build logs
if: ${{ failure() }}
Expand Down
47 changes: 5 additions & 42 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,33 +108,14 @@ jobs:
- name: Check the Production CloudKit schema
env:
CLOUDKIT_MANAGEMENT_TOKEN: ${{ secrets.CLOUDKIT_MANAGEMENT_TOKEN }}
run: |
set -euo pipefail
xcrun cktool save-token --type management --method file --force "$CLOUDKIT_MANAGEMENT_TOKEN"
scripts/export-cloudkit-schema.sh --check

- uses: ./.github/actions/setup-xcodegen

- name: Download static libraries
env:
GH_TOKEN: ${{ github.token }}
run: scripts/download-libs.sh
run: scripts/export-cloudkit-schema.sh --check

- name: Write Secrets.xcconfig
env:
ANALYTICS_HMAC_SECRET: ${{ secrets.ANALYTICS_HMAC_SECRET }}
run: printf 'ANALYTICS_HMAC_SECRET = %s\n' "$ANALYTICS_HMAC_SECRET" > Configs/Secrets.xcconfig

- name: Generate the Xcode project
run: scripts/generate-project.sh macos

# macos-tests.yml saves this cache; a save in tag scope would be unreadable from other refs.
- name: Restore Swift package checkouts
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.spm-cache
key: spm-${{ runner.os }}-${{ hashFiles('TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ runner.os }}-
- uses: ./.github/actions/prepare-build

- uses: ./.github/actions/apple-signing
with:
Expand All @@ -145,17 +126,14 @@ jobs:
asc-issuer-id: ${{ secrets.ASC_ISSUER_ID }}
asc-key-p8: ${{ secrets.ASC_KEY_P8 }}

- name: Install create-dmg
run: brew list create-dmg > /dev/null 2>&1 || brew install create-dmg

- name: Build the app
run: scripts/release/build.sh app

- name: Notarize the app
run: scripts/release/notarize.sh build/release/TablePro.app

- name: Package the app
run: scripts/release/package.sh app "$VERSION"
run: scripts/release/package.sh app

- name: Notarize the disk image
run: scripts/release/notarize.sh "dist/TablePro-$VERSION.dmg"
Expand Down Expand Up @@ -185,22 +163,7 @@ jobs:
with:
require-release: ${{ needs.verify.outputs.channel == 'stable' }}

- uses: ./.github/actions/setup-xcodegen

- name: Download static libraries
env:
GH_TOKEN: ${{ github.token }}
run: scripts/download-libs.sh

- name: Generate the Xcode project
run: scripts/generate-project.sh macos

- name: Restore Swift package checkouts
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.spm-cache
key: spm-${{ runner.os }}-${{ hashFiles('TablePro.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
restore-keys: spm-${{ runner.os }}-
- uses: ./.github/actions/prepare-build

- name: Set up Go for the SAP HANA helper
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
Expand Down Expand Up @@ -229,7 +192,7 @@ jobs:
run: scripts/release/notarize.sh build/release/plugins/*.tableplugin

- name: Package the plugins
run: scripts/release/package.sh plugins "$VERSION"
run: scripts/release/package.sh plugins

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand Down
4 changes: 1 addition & 3 deletions Configs/Base.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ STRINGS_FILE_OUTPUT_ENCODING = binary
SWIFT_VERSION = 6.0
CODE_SIGN_STYLE = Automatic

// Signing knobs. The app targets and the plugin bundles sign with this team; the helper tool
// signs ad hoc. A contributor overrides these in Configs/Secrets.xcconfig without touching a
// tracked file, and the release scripts pass their own values on the xcodebuild command line.
// A contributor overrides these in Configs/Secrets.xcconfig.
TABLEPRO_DEVELOPMENT_TEAM = D7HJ5TFYCU
TABLEPRO_APP_BUNDLE_IDENTIFIER = com.TablePro

Expand Down
Loading
Loading