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
59 changes: 59 additions & 0 deletions .github/duplicate-contract-baseline.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Known divergent shared contracts pending Phase 1 consolidation (R-001 / R-002).
# scripts/audit-refactor-health.sh --check fails when a shared contract diverges
# that is NOT listed here. Burn this list down as contracts move into TableProCore;
# never add a fresh entry just to make new divergence pass.
#
# Format: <kind>:<key>
# pluginkit:<path under the PluginKit tree that differs or exists in only one tree>
# databasetype:<file defining a production DatabaseType other than the TableProCore source>

databasetype:TablePro/Models/Connection/DatabaseConnection.swift

pluginkit:ArrayExtension.swift
pluginkit:CompletionEntry.swift
pluginkit:ConnectionField.swift
pluginkit:ConnectionMode.swift
pluginkit:DocumentInspectorPlugin.swift
pluginkit:DriverConnectionConfig.swift
pluginkit:DriverPlugin.swift
pluginkit:EditorLanguage.swift
pluginkit:EnumValueParser.swift
pluginkit:ExportFormatPlugin.swift
pluginkit:GroupingStrategy.swift
pluginkit:HttpQueryTimeout.swift
pluginkit:HttpQueryTimeoutBox.swift
pluginkit:HugeIntFormatter.swift
pluginkit:ImportFormatPlugin.swift
pluginkit:Info.plist
pluginkit:MongoShellParser.swift
pluginkit:NavigationModel.swift
pluginkit:PathFieldRole.swift
pluginkit:PluginCapabilities.swift
pluginkit:PluginCapability.swift
pluginkit:PluginCellValue.swift
pluginkit:PluginColumnInfo.swift
pluginkit:PluginCreateDatabaseFormSpec.swift
pluginkit:PluginDatabaseDriver.swift
pluginkit:PluginDefaultSortProvider.swift
pluginkit:PluginDiagnostic.swift
pluginkit:PluginDriverError.swift
pluginkit:PluginExportDataSource.swift
pluginkit:PluginExportProgress.swift
pluginkit:PluginExportTypes.swift
pluginkit:PluginExportUtilities.swift
pluginkit:PluginImportDataSink.swift
pluginkit:PluginImportProgress.swift
pluginkit:PluginImportSource.swift
pluginkit:PluginImportTypes.swift
pluginkit:PluginPagedResult.swift
pluginkit:PluginProcedureFunctionSupport.swift
pluginkit:PluginQueryResult.swift
pluginkit:PluginSettingsStorage.swift
pluginkit:PluginStreamTypes.swift
pluginkit:PluginTableInfo.swift
pluginkit:PluginTableMetadata.swift
pluginkit:SQLDialectDescriptor.swift
pluginkit:SSLConfiguration.swift
pluginkit:SSLHandshakeError.swift
pluginkit:SchemaTypes.swift
pluginkit:SettablePlugin.swift
39 changes: 39 additions & 0 deletions .github/workflows/contract-drift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Contract Drift

on:
pull_request:
paths:
- "Plugins/TableProPluginKit/**"
- "Packages/TableProCore/Sources/TableProPluginKit/**"
- "Packages/TableProCore/Sources/TableProModels/**"
- "TablePro/Models/**"
- "TableProMobile/**"
Comment on lines +9 to +10
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run drift check for all scanned DatabaseType paths

The audit script scans all of TablePro, Plugins, Packages, and TableProMobile for extra DatabaseType definitions, but this workflow only runs when a narrow subset of those paths changes. For example, a PR that adds struct DatabaseType under TablePro/Core or another package/plugin path outside these filters will not trigger contract-drift.yml, so the new duplicate contract can merge without the new gate ever running. Broaden the path filters to cover every directory scanned by databasetype_extra_defs or remove the filter for this workflow.

Useful? React with 👍 / 👎.

- "scripts/audit-refactor-health.sh"
- ".github/duplicate-contract-baseline.txt"
- ".github/workflows/contract-drift.yml"
push:
branches: [main]
paths:
- "Plugins/TableProPluginKit/**"
- "Packages/TableProCore/Sources/TableProPluginKit/**"
- "Packages/TableProCore/Sources/TableProModels/**"
- "TablePro/Models/**"
- "TableProMobile/**"
- "scripts/audit-refactor-health.sh"
- ".github/duplicate-contract-baseline.txt"
- ".github/workflows/contract-drift.yml"
workflow_dispatch:

concurrency:
group: contract-drift-${{ github.ref }}
cancel-in-progress: true

jobs:
drift:
name: Shared contract drift
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: Fail on new duplicate-contract drift
run: scripts/audit-refactor-health.sh --check
63 changes: 52 additions & 11 deletions scripts/audit-refactor-health.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,30 @@ count_swift_matches() {
grep_swift "$pattern" "$@" | grep -c . || true
}

BASELINE_FILE=".github/duplicate-contract-baseline.txt"
PLUGINKIT_A="Plugins/TableProPluginKit"
PLUGINKIT_B="Packages/TableProCore/Sources/TableProPluginKit"
DATABASETYPE_AUTHORITATIVE="Packages/TableProCore/Sources/TableProModels/DatabaseType.swift"

baseline_keys() {
[ -f "$BASELINE_FILE" ] || return 0
sed -E 's/#.*//' "$BASELINE_FILE" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' | grep -v '^$' || true
}

pluginkit_divergent_paths() {
[ -d "$PLUGINKIT_A" ] && [ -d "$PLUGINKIT_B" ] || return 0
{ diff -qr "$PLUGINKIT_A" "$PLUGINKIT_B" 2>/dev/null || true; } | sed -E \
-e "s#^Files $PLUGINKIT_A/(.*) and .* differ#\\1#" \
-e "s#^Only in $PLUGINKIT_A(/?[^:]*): #\\1/#" \
-e "s#^Only in $PLUGINKIT_B(/?[^:]*): #\\1/#" \
| sed -E 's#^/##' | sort -u | grep -v '^$' || true
}

databasetype_extra_defs() {
grep_swift '^(public )?(struct|enum) DatabaseType[ :<]' TablePro Plugins Packages TableProMobile \
| awk -F: '{print $1}' | sort -u | grep -vxF "$DATABASETYPE_AUTHORITATIVE" || true
}

report_loc_by_area() {
section "Swift LOC by area"
echo "Swift files (app + plugins + packages + mobile): $(count_swift_files TablePro Plugins Packages TableProMobile)"
Expand All @@ -76,23 +100,20 @@ report_duplicate_contracts() {
section "Duplicate shared contracts (R-001 / R-002 / R-008)"

echo "DatabaseType definitions:"
local dbtype_defs dbtype_count
local dbtype_defs
dbtype_defs=$(grep_swift '^(public )?(struct|enum) DatabaseType[ :<]' TablePro Plugins Packages TableProMobile | awk -F: '{print $1}' | sort -u)
if [ -n "$dbtype_defs" ]; then
printf '%s\n' "$dbtype_defs" | sed 's/^/ /'
else
echo " (none found)"
fi
dbtype_count=$(printf '%s\n' "$dbtype_defs" | grep -c . || true)

echo
echo "PluginKit source trees:"
if [ -d "Plugins/TableProPluginKit" ] && [ -d "Packages/TableProCore/Sources/TableProPluginKit" ]; then
echo " both present (duplicate authoritative PluginKit)"
if $CHECK_MODE; then
echo " ⚠️ drift gate: duplicate PluginKit source trees still present"
DRIFT_FAILURES=$((DRIFT_FAILURES + 1))
fi
local pk_divergent
pk_divergent=$(pluginkit_divergent_paths)
if [ -d "$PLUGINKIT_A" ] && [ -d "$PLUGINKIT_B" ]; then
echo " both present; $(printf '%s\n' "$pk_divergent" | grep -c . || true) divergent file(s) pending Phase 1 consolidation"
else
echo " single source ✅"
fi
Expand All @@ -105,9 +126,29 @@ report_duplicate_contracts() {
echo " single source ✅"
fi

if $CHECK_MODE && [ "${dbtype_count:-0}" -gt 1 ]; then
echo " ⚠️ drift gate: more than one production DatabaseType definition"
DRIFT_FAILURES=$((DRIFT_FAILURES + 1))
if $CHECK_MODE; then
local baseline new_drift=0 path
baseline=$(baseline_keys)
while IFS= read -r path; do
[ -z "$path" ] && continue
if ! printf '%s\n' "$baseline" | grep -qxF "pluginkit:$path"; then
echo " ❌ new PluginKit divergence not in baseline: $path"
new_drift=$((new_drift + 1))
fi
done <<< "$pk_divergent"
while IFS= read -r path; do
[ -z "$path" ] && continue
if ! printf '%s\n' "$baseline" | grep -qxF "databasetype:$path"; then
echo " ❌ DatabaseType defined outside the authoritative source and baseline: $path"
new_drift=$((new_drift + 1))
fi
done <<< "$(databasetype_extra_defs)"
if [ "$new_drift" -gt 0 ]; then
DRIFT_FAILURES=$((DRIFT_FAILURES + new_drift))
else
echo
echo " ✅ no new shared-contract drift beyond $BASELINE_FILE"
fi
fi
}

Expand Down
Loading