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
10 changes: 1 addition & 9 deletions TablePro/Core/Menu/AppDelegate+MainMenuActions.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
//
// AppDelegate+MainMenuActions.swift
// TablePro
//

import AppKit

/// Commands that belong to the app rather than to any one window. They sit on the
Expand Down Expand Up @@ -109,8 +104,6 @@ extension AppDelegate: NSMenuItemValidation {
open(MainMenuLink.integrations)
}

/// Opens the in-app notes rather than the browser. The window's own button reaches the full
/// changelog for anyone who wants every entry.
@objc func openChangelog(_ sender: Any?) {
WhatsNewWindowController.present()
}
Expand Down Expand Up @@ -144,8 +137,7 @@ extension AppDelegate: NSMenuItemValidation {
case #selector(switchToRecentTab(_:)), #selector(switchToLeastRecentTab(_:)):
return (NSApp.keyWindow?.tabbedWindows?.count ?? 0) > 1
case #selector(checkForUpdates(_:)):
/// Menu validation is the one moment AppKit gives an already-built item, and a
/// deferred update has no other way to reach this title.
// A deferred update reaches this title only through menu validation.
menuItem.title = SoftwareUpdater.shared.checkForUpdatesTitle
return SoftwareUpdater.shared.canCheckForUpdates
case #selector(reopenClosedTab(_:)):
Expand Down
14 changes: 1 addition & 13 deletions TablePro/Core/Plugins/Registry/RegistryModels.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
//
// RegistryModels.swift
// TablePro
//

import Foundation

enum PluginArchitecture: String, Codable, Sendable {
Expand Down Expand Up @@ -78,10 +73,7 @@ struct RegistryPlugin: Codable, Sendable, Identifiable {
if let decodedBinaries = try container.decodeIfPresent([RegistryBinary].self, forKey: .binaries) {
binaries = decodedBinaries
} else if let url = legacyDownloadURL, let hash = legacySha256 {
// v1 manifests carried a single downloadURL with no architecture.
// Historically those ZIPs shipped a universal binary, so we synthesize
// entries for both architectures. Code signature verification will
// reject mismatched arch at install time.
// A v1 entry has one universal ZIP and no architecture.
binaries = [
RegistryBinary(
architecture: .arm64,
Expand Down Expand Up @@ -122,10 +114,6 @@ extension RegistryPlugin {
return highestInRange.binary
}

// Every published binary being above this app's PluginKit version is a stale app, not a
// missing architecture, and saying "architecture" sends the user looking at their CPU.
// It is the ordinary outcome of the registry's retention policy: once the two newest kit
// versions are both above what an app ships, that app can install nothing at all.
let publishedKits = forThisArchitecture.compactMap(\.pluginKitVersion)
if let oldest = publishedKits.min(), oldest > currentKitVersion {
throw PluginError.appTooOldForPlugin(oldestPublishedKit: oldest, appKit: currentKitVersion)
Expand Down
10 changes: 0 additions & 10 deletions TablePro/Core/Services/Infrastructure/MacAnalyticsProvider.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
//
// MacAnalyticsProvider.swift
// TablePro
//

import Foundation
import os
import TableProAnalytics
Expand Down Expand Up @@ -107,11 +102,6 @@ final class MacAnalyticsProvider: AnalyticsEnvironmentProvider {
Self.logger.info("Recorded \(label, privacy: .public) for first time")
}

/// Read from Sparkle rather than from a stored setting, because Sparkle owns these and a
/// managed preference or its own permission prompt can set them without the app involved.
///
/// This is the only measure of whether a default that was flipped for every existing install
/// was accepted. It carries nothing about a person, a host or a query.
var updateInstallMode: String? {
let updater = SoftwareUpdater.shared
guard updater.automaticallyChecksForUpdates else { return "off" }
Expand Down
7 changes: 0 additions & 7 deletions TablePro/Core/Services/Infrastructure/PostLaunchWork.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
//
// PostLaunchWork.swift
// TablePro
//

import Foundation
import os
import TableProUtilities
Expand Down Expand Up @@ -40,8 +35,6 @@ internal enum PostLaunchWork {

guard !AppStorageEnvironment.shared.isIsolated else { return }

/// The registry manifest only feeds the plugin-install UI, and fetching it is a network
/// call. A sandboxed run has no user plugins directory to install into anyway.
Task { await RegistryClient.shared.ensureManifest(.ifStale) }

/// The retired per-tab split name left one permanent defaults record per query tab ever
Expand Down
4 changes: 1 addition & 3 deletions TablePro/Core/Storage/AppSettingsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,7 @@ final class AppSettingsManager: ObservableObject {
await queryHistoryManager.applySettingsChange()
}

/// The update preferences belong to Sparkle rather than to the structs reassigned above, so
/// they are cleared here and not by the caller: the alert promises every section, and a second
/// reset entry point would otherwise skip them silently.
/// Resets Sparkle's update preferences here too: the reset alert promises every section.
func resetToDefaults() {
general = .default
appearance = .default
Expand Down
12 changes: 1 addition & 11 deletions TablePro/Views/Settings/GeneralSettingsView.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
//
// GeneralSettingsView.swift
// TablePro
//

import AppKit
import SwiftUI

struct GeneralSettingsView: View {
@Binding var settings: GeneralSettings
@Binding var tabSettings: TabSettings
/// Observed, because this view reads `canCheckForUpdates`, `lastUpdateCheckDate` and the button
/// title off it. Held as a plain property, Last checked and Check for Updates… kept whatever they
/// said when Settings opened, however many checks ran behind them.
@ObservedObject var updater: SoftwareUpdater
var onResetAll: () -> Void

Expand All @@ -21,9 +13,7 @@ struct GeneralSettingsView: View {

private static let standardTimeouts = [10, 20, 30, 40, 50, 60, 90, 120, 180, 300, 600]

/// Bindings straight onto Sparkle's own properties. Nothing about the update section is stored
/// in `GeneralSettings`, so there is no second copy to fall out of step and nothing for a
/// synced settings blob to overwrite on another Mac.
/// Sparkle owns these, so `GeneralSettings` holds no copy for settings sync to overwrite.
private var automaticallyChecksForUpdates: Binding<Bool> {
Binding(
get: { updater.automaticallyChecksForUpdates },
Expand Down
10 changes: 1 addition & 9 deletions TablePro/Views/Welcome/WelcomeActionsPanel.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
//
// WelcomeActionsPanel.swift
// TablePro
//

import Combine
import SwiftUI

Expand All @@ -25,8 +20,7 @@ struct WelcomeActionsPanel: View {
.map(\.showWelcomeVersionInfo)
.removeDuplicates()

/// Captured once, because the stored value is overwritten on the same appearance that reads
/// it. Without the capture the line would replace itself with nothing on the next redraw.
/// Captured once: the same appearance that reads the stored value overwrites it.
@State private var lastSeenAppVersion = ""

var body: some View {
Expand Down Expand Up @@ -129,8 +123,6 @@ struct WelcomeActionsPanel: View {
defaults.set(current, forKey: key)
}

/// Updates install on quit with no dialog, so the release notes stop passing in front of
/// anyone. This is the pull-shaped replacement, shown on the window the user chose to open.
private var showsWhatsNew: Bool {
!lastSeenAppVersion.isEmpty && lastSeenAppVersion != Bundle.main.appVersion
}
Expand Down
14 changes: 0 additions & 14 deletions TablePro/Views/WhatsNew/WhatsNewView.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
//
// WhatsNewView.swift
// TablePro
//

import SwiftUI

/// What the running version brought, rendered from the bundled `WhatsNew.md`.
///
/// Markdown through `AttributedString(markdown:)` rather than a web view, per the native-only
/// rule. The file is generated from the same CHANGELOG lead block the Sparkle feed carries, so
/// the window and the update dialog cannot disagree about what a release contained.
struct WhatsNewView: View {
private let content: WhatsNewContent
private let onViewFullChangelog: () -> Void
Expand Down Expand Up @@ -69,10 +59,6 @@ struct WhatsNewView: View {
}
}

/// The bundled notes, parsed once.
///
/// A missing or unreadable file is not a failure worth showing: the window still opens, says so
/// plainly and offers the changelog button, which is the route that always works.
struct WhatsNewContent {
let title: String
let paragraphs: [AttributedString]
Expand Down
12 changes: 0 additions & 12 deletions TablePro/Views/WhatsNew/WhatsNewWindowController.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
//
// WhatsNewWindowController.swift
// TablePro
//

import AppKit
import SwiftUI

/// Hosts the What's New notes in an ordinary `NSWindowController`, like every other window in the
/// app. One instance for the app's lifetime, so reopening from the menu raises the same window
/// rather than stacking copies.
///
/// Pull-only. Nothing opens this on launch or after an update installs: updates now apply on quit
/// with no dialog, and a window that appeared after each one would be more than a hundred
/// interruptions a year spent on exactly what background installs remove.
@MainActor
internal final class WhatsNewWindowController: NSWindowController {
private static var shared: WhatsNewWindowController?
Expand Down
6 changes: 2 additions & 4 deletions scripts/build-cassandra.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ set -euo pipefail
#
# Dependencies: cmake, libuv (built automatically), OpenSSL (from Libs/)
#
# Always rebuilds. It used to skip any artifact already present in Libs/, but download-libs.sh
# vendors all eight of them, so the guards were satisfied on every machine and the script did
# nothing: a CASSANDRA_VERSION bump produced no new binary. Publish the result with
# scripts/publish-libs.sh, which is what makes it the version everyone else gets.
# Always rebuilds: download-libs.sh fills Libs/, so a skip-if-present guard would never build.
# Publish the result with scripts/publish-libs.sh.

CASSANDRA_VERSION="2.17.1"
CASSANDRA_SHA256="e6ab5f5c60a916dd6c0dd9a19a883a4a1ab3d6b4e95cab925a186fecff08344e"
Expand Down
17 changes: 6 additions & 11 deletions scripts/build-duckdb.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ set -euo pipefail
# Usage: ./scripts/build-duckdb.sh [arm64|x86_64|both]
#
# This compiles DuckDB from source with CMake so the extensions in
# scripts/duckdb-macos-extensions.cmake link in statically. The previous version
# compiled the single-file amalgamation with plain clang++, and the amalgamation
# carries no extensions: `sum`, `avg`, `round`, `date_trunc`, `current_database`
# and most of the rest live in core_functions, which DuckDB then downloaded from
# extensions.duckdb.org on first use. On a Mac that could not reach the registry
# every one of them failed and a DuckDB connection was unusable.
# scripts/duckdb-macos-extensions.cmake link in statically. The amalgamation carries
# none, so core_functions (`sum`, `avg`, `date_trunc` and most of the rest) would
# download from extensions.duckdb.org on first use and fail offline.
#
# Runtime autoloading stays on, unlike the iOS build, so httpfs and quack still
# download on demand for remote Quack connections.
Expand Down Expand Up @@ -144,11 +141,9 @@ build_arch() {
fi
echo "Verified $core_symbols core_functions symbol(s) in $(basename "$out_lib")"

# The plugin links this with -force_load, which pulls in every member rather than letting
# the linker pick a winner, so a duplicated symbol is a hard link failure. Merging one
# archive too many produced 1821 of them and `libtool -static` reported nothing at all,
# so the only honest check is to link the way the plugin links. Member names are not the
# test: brotli and DuckDB core both ship a `platform.cpp.o` and coexist fine.
# The plugin links this with -force_load, so a duplicated symbol is a hard link failure
# that `libtool -static` never reports: link the way the plugin links. Member names are
# not the test: brotli and DuckDB core both ship a `platform.cpp.o` and coexist fine.
local link_dir
link_dir=$(mktemp -d)
cat > "$link_dir/link_probe.c" <<'PROBE'
Expand Down
6 changes: 0 additions & 6 deletions scripts/build-hiredis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ set -eo pipefail
# OpenSSL is built from source to match the app's deployment target,
# preventing "Symbol not found" crashes from Homebrew-built libraries.
#
# All libraries are built with MACOSX_DEPLOYMENT_TARGET=14.0 to match
# the app's minimum deployment target.
#
# Usage:
# ./scripts/build-hiredis.sh [arm64|x86_64|both]
#
Expand All @@ -28,9 +25,6 @@ HIREDIS_VERSION="1.2.0"
HIREDIS_SHA256="82ad632d31ee05da13b537c124f819eb88e18851d9cb0c30ae0552084811588c"

ARCH="${1:-both}"
# The headers the Redis plugin compiles against. The old path under TablePro/Core/Database was
# deleted when the driver moved into a plugin bundle, so this script was installing them where
# nothing read them and leaving the real ones untouched.
HEADER_DIR="$REPO_ROOT/Plugins/RedisDriverPlugin/CRedis/include/hiredis"
make_build_dir

Expand Down
9 changes: 2 additions & 7 deletions scripts/build-libmongoc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,12 @@ set -eo pipefail
# libbson_arm64.a, libbson_x86_64.a, libbson_universal.a
# libmongoc_arm64.a, libmongoc_x86_64.a, libmongoc_universal.a
#
# TLS backend: OpenSSL (ENABLE_SSL=OPENSSL). The previous Secure Transport
# build broke TLS handshakes against MongoDB Atlas on macOS 26 with
# errSSLPeerInternalError (-9838). OpenSSL handshakes succeed and TablePro
# already bundles OpenSSL 3 dylibs for Redis/MSSQL/MySQL.
# TLS backend: OpenSSL (ENABLE_SSL=OPENSSL). Secure Transport fails the handshake against
# MongoDB Atlas on macOS 26 with errSSLPeerInternalError (-9838).
#
# OpenSSL is rebuilt from source for each arch with the correct deployment
# target instead of relying on Homebrew or system OpenSSL.
#
# All libraries are built with MACOSX_DEPLOYMENT_TARGET=14.0 to match
# the app's minimum deployment target.
#
# Usage:
# ./scripts/build-libmongoc.sh [arm64|x86_64|both]
#
Expand Down
12 changes: 3 additions & 9 deletions scripts/build-libpq.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ set -eo pipefail
# libpgcommon_arm64.a, libpgcommon_x86_64.a, libpgcommon_universal.a
# libpgport_arm64.a, libpgport_x86_64.a, libpgport_universal.a
#
# All libraries are built with MACOSX_DEPLOYMENT_TARGET=14.0 to match
# the app's minimum deployment target. This prevents the "Symbol not found"
# crash (e.g. _strchrnul) that occurs when Homebrew libraries built for
# the host OS are bundled into the app.
# Built for the app's deployment target, not the host's: a Homebrew libpq crashes an older
# macOS with "Symbol not found" (e.g. _strchrnul).
#
# Usage:
# ./scripts/build-libpq.sh [arm64|x86_64|both]
Expand All @@ -29,9 +27,6 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/macos.sh"
PG_SHA256="c4605b73fea11963406699f949b966e5d173a7ee0ccaef8938dec0ca8a995fe7"

ARCH="${1:-both}"
# The PostgreSQL driver became a plugin, and its headers moved with it. This pointed at
# TablePro/Core/Database/CLibPQ/include, which no longer exists, so every rebuild recreated a dead
# directory and never updated the headers the plugin actually compiles against.
HEADER_DIR="$REPO_ROOT/Plugins/PostgreSQLDriverPlugin/CLibPQ/include"
make_build_dir

Expand Down Expand Up @@ -159,8 +154,7 @@ build_for_arch() {
build_openssl "$arch"
build_libpq "$arch"
install_libs "$arch"
# Unconditional. Skipping when a header already exists meant a version bump shipped new
# binaries against the previous release's headers.
# Unconditional, so a version bump never ships new binaries with the old headers.
install_headers "$arch"
}

Expand Down
3 changes: 0 additions & 3 deletions scripts/build-libssh2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ set -eo pipefail
# OpenSSL is built from source to match the app's deployment target,
# preventing "Symbol not found" crashes from Homebrew-built libraries.
#
# All libraries are built with MACOSX_DEPLOYMENT_TARGET=14.0 to match
# the app's minimum deployment target.
#
# Usage:
# ./scripts/build-libssh2.sh [arm64|x86_64|both]
#
Expand Down
8 changes: 2 additions & 6 deletions scripts/ci/check-deployment-target.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
#!/usr/bin/env bash
#
# Fails when a build script writes the macOS deployment target down itself.
#
# The number belongs to project.yml and reaches the scripts through
# scripts/lib/deployment-target.sh. It used to live in five places, and when #2874 moved the app to
# macOS 13 none of them followed: every archive in Libs/ was built for macOS 14 and linked into a
# 13.0 app, which the linker reported 121 times into a build that silenced linker warnings.
# Fails when a build script writes the macOS deployment target down itself. The number belongs
# to project.yml and reaches the scripts through scripts/lib/deployment-target.sh.
#
# Usage: check-deployment-target.sh

Expand Down
13 changes: 2 additions & 11 deletions scripts/ci/check-lib-sourcing.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
#!/usr/bin/env python3
"""Fails when a script calls a scripts/lib function without sourcing the library that defines it.

A missing `source` line is invisible to every tool the repo already runs. bash -n only parses,
shellcheck cannot know whether a bare word is a function or a command on PATH, and the call site
reads exactly like a working one. It surfaces at run time as exit 127, and for the one script that
only runs during a release, that means it surfaces during a release: v0.67.1's DMG job died with
"exit code 127" right after "DMG signed", because create-dmg.sh called notarize_and_staple and
never sourced scripts/lib/notarize.sh.

Usage: check-lib-sourcing.py [scripts-dir]
"""

Expand All @@ -16,8 +9,7 @@
import sys

FUNCTION = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*)\s*\(\)\s*\{", re.M)
# Any .sh named on a source line. Matched by basename so it works whether the script
# writes lib/common.sh or, as macos.sh does from inside lib/, just common.sh.
# Matched by basename: macos.sh, from inside lib/, sources plain common.sh.
SOURCED = re.compile(r"^\s*(?:source|\.)\s+.*?([A-Za-z0-9_.-]+\.sh)", re.M)


Expand All @@ -33,8 +25,7 @@ def main():
for name in FUNCTION.findall(lib.read_text(encoding="utf-8")):
owner.setdefault(name, lib.name)

# Sourcing is transitive: macos.sh sources common.sh, so a script that sources macos.sh gets
# both. Resolving that is the difference between a useful check and five false reports.
# Sourcing is transitive: a script that sources macos.sh also gets common.sh.
def sourced_libs(text):
return {name for name in SOURCED.findall(text) if (lib_dir / name).is_file()}

Expand Down
Loading
Loading