From 3ffa73b4b6b76f814d412a6fc34a1f99be4a92c6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 20 Aug 2026 22:01:39 +0000 Subject: [PATCH] Add Cloud Agent Linux dev environment (Swift toolchain + SwiftLint) Adds .cursor/environment.json and an idempotent .cursor/install.sh that installs Swiftly + Swift 6.3.3 and SwiftLint 0.58.2, and wires LINUX_SOURCEKIT_LIB_PATH into the shell for SwiftLint on Linux. Refreshes AGENTS.md to match the real setup and documents the SwiftLintBaseline.json macOS-absolute-path portability caveat. Co-authored-by: Tim Champ --- .cursor/environment.json | 5 ++ .cursor/install.sh | 115 +++++++++++++++++++++++++++++++++++++++ AGENTS.md | 44 ++++++++++++--- 3 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 .cursor/environment.json create mode 100755 .cursor/install.sh diff --git a/.cursor/environment.json b/.cursor/environment.json new file mode 100644 index 00000000..35206d55 --- /dev/null +++ b/.cursor/environment.json @@ -0,0 +1,5 @@ +{ + "name": "BisonNotes AI (Linux lint & syntax)", + "user": "ubuntu", + "install": "bash .cursor/install.sh" +} diff --git a/.cursor/install.sh b/.cursor/install.sh new file mode 100755 index 00000000..6ec465f5 --- /dev/null +++ b/.cursor/install.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +# Cloud Agent install script for BisonNotes AI. +# +# This is a native iOS/watchOS Xcode project that cannot be built or tested on +# Linux (xcodebuild + the iOS Simulator require macOS). On a Linux Cloud Agent +# VM the supported development experience is: +# - SwiftLint (`swiftlint lint`, filtered by SwiftLintBaseline.json) +# - Syntax check (`swiftc -parse `) +# +# Both require a Swift toolchain (SourceKit). This script installs Swiftly, a +# pinned Swift toolchain, and SwiftLint, then wires the required environment +# variables into ~/.bashrc. It is idempotent: re-running it is a no-op once the +# tools are present. +set -euo pipefail + +SWIFT_VERSION="6.3.3" +SWIFTLINT_VERSION="0.58.2" +SWIFTLY_HOME_DIR="${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}" + +log() { printf '[cloud-install] %s\n' "$*"; } + +# 1. OS dependencies required by the Swift toolchain / SourceKit. +if ! dpkg -s libcurl4-openssl-dev >/dev/null 2>&1; then + log "Installing OS dependencies for Swift..." + sudo apt-get update -qq + sudo apt-get install -y -qq \ + binutils gnupg2 libc6-dev libcurl4-openssl-dev libedit2 libgcc-13-dev \ + libpython3-dev libstdc++-13-dev libxml2-dev libz3-dev pkg-config tzdata \ + unzip zlib1g-dev libncurses-dev +else + log "OS dependencies already present." +fi + +# 2. Swiftly (Swift toolchain manager). +if [ ! -x "$SWIFTLY_HOME_DIR/bin/swiftly" ]; then + log "Installing Swiftly..." + tmp="$(mktemp -d)" + ( + cd "$tmp" + curl -fsSL -O "https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz" + tar zxf "swiftly-$(uname -m).tar.gz" + ./swiftly init --skip-install --quiet-shell-followup --assume-yes + ) + rm -rf "$tmp" +else + log "Swiftly already installed." +fi + +# Make swiftly + the active toolchain available to the rest of this script. +# shellcheck disable=SC1091 +. "$SWIFTLY_HOME_DIR/env.sh" +hash -r + +# 3. Pinned Swift toolchain. Use --global-default so no repo-local +# `.swift-version` file is written into the checkout. +if ! "$SWIFTLY_HOME_DIR/bin/swiftly" list 2>/dev/null | grep -q "Swift $SWIFT_VERSION"; then + log "Installing Swift $SWIFT_VERSION (this downloads ~1 GB)..." + swiftly install "$SWIFT_VERSION" --assume-yes +else + log "Swift $SWIFT_VERSION already installed." +fi +swiftly use --global-default "$SWIFT_VERSION" >/dev/null 2>&1 || true +hash -r + +# Location of libsourcekitdInProc.so inside the installed toolchain; SwiftLint +# needs this on Linux. +SOURCEKIT_LIB_DIR="$(dirname "$(ls "$SWIFTLY_HOME_DIR"/toolchains/*/usr/lib/libsourcekitdInProc.so 2>/dev/null | head -n1)")" +export LINUX_SOURCEKIT_LIB_PATH="$SOURCEKIT_LIB_DIR" + +# 4. SwiftLint (Linux binary release). +current_swiftlint="" +if command -v swiftlint >/dev/null 2>&1; then + current_swiftlint="$(swiftlint version 2>/dev/null || true)" +fi +if [ "$current_swiftlint" != "$SWIFTLINT_VERSION" ]; then + log "Installing SwiftLint $SWIFTLINT_VERSION..." + tmp="$(mktemp -d)" + ( + cd "$tmp" + curl -fsSL -o swiftlint_linux.zip \ + "https://github.com/realm/SwiftLint/releases/download/${SWIFTLINT_VERSION}/swiftlint_linux.zip" + unzip -o -q swiftlint_linux.zip + sudo install -m 0755 swiftlint /usr/local/bin/swiftlint + ) + rm -rf "$tmp" +else + log "SwiftLint $SWIFTLINT_VERSION already installed." +fi + +# 5. Wire environment variables into ~/.bashrc (idempotent, guarded block). +# ~/.profile already sources ~/.bashrc for bash login shells, so a single +# block here covers both login and interactive Cloud Agent shells. +MARK_BEGIN="# >>> bisonnotes-ai cloud-agent env >>>" +MARK_END="# <<< bisonnotes-ai cloud-agent env <<<" +if ! grep -qF "$MARK_BEGIN" "$HOME/.bashrc" 2>/dev/null; then + log "Adding Swift environment block to ~/.bashrc..." + cat >> "$HOME/.bashrc" <<'EOF' + +# >>> bisonnotes-ai cloud-agent env >>> +# Swift toolchain (Swiftly) + SwiftLint SourceKit path for Linux dev. +if [ -f "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" ]; then + . "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" +fi +export LINUX_SOURCEKIT_LIB_PATH="$(dirname "$(ls "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}"/toolchains/*/usr/lib/libsourcekitdInProc.so 2>/dev/null | head -n1)")" +# <<< bisonnotes-ai cloud-agent env <<< +EOF +else + log "~/.bashrc already configured." +fi + +# 6. Report resulting versions. +log "Swift: $(swift --version 2>/dev/null | head -n1)" +log "SwiftLint: $(swiftlint version 2>/dev/null)" +log "LINUX_SOURCEKIT_LIB_PATH=$LINUX_SOURCEKIT_LIB_PATH" +log "Install complete." diff --git a/AGENTS.md b/AGENTS.md index 749363d3..b8956f8d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,13 +18,21 @@ This is a **native iOS/watchOS Xcode project**. It requires **macOS with Xcode 1 - **Code review/editing**: Read, search, and edit all Swift source files, Core Data model XML, plists, entitlements, etc. - **Git operations**: Full git workflow including branching, committing, and pushing. -### Environment setup (already done by update script) +### Environment setup (Cloud Agent) -- **Swift 6.3.x** installed via [Swiftly](https://swift.org/install/linux/) at `~/.local/share/swiftly/`. -- **SwiftLint 0.58.x** installed at `/usr/local/bin/swiftlint`. -- Environment variables sourced from `~/.bashrc`: - - Swiftly env: `. "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh"` - - SourceKit path: `export LINUX_SOURCEKIT_LIB_PATH=...` (auto-computed from installed toolchain) +The Cloud Agent environment is defined by `.cursor/environment.json`, whose +`install` step runs `.cursor/install.sh`. That script is idempotent and: + +- Installs the Swift OS dependencies (libcurl, libpython, etc.). +- Installs **Swift 6.3.3** via [Swiftly](https://swift.org/install/linux/) at `~/.local/share/swiftly/`. +- Installs **SwiftLint 0.58.2** at `/usr/local/bin/swiftlint`. +- Appends a guarded block to `~/.bashrc` (also picked up by login shells because + `~/.profile` sources `~/.bashrc`) that sets: + - Swiftly env: `. "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh"` + - SourceKit path: `export LINUX_SOURCEKIT_LIB_PATH=...` (auto-computed from the installed toolchain) + +To change the pinned Swift or SwiftLint version, edit the `SWIFT_VERSION` / +`SWIFTLINT_VERSION` variables at the top of `.cursor/install.sh`. ### Running SwiftLint @@ -34,7 +42,29 @@ swiftlint lint # Full lint with all details swiftlint lint --reporter summary # Summary table only ``` -SwiftLint runs with default rules (no `.swiftlint.yml` exists in the repo). The codebase has ~9,000 pre-existing violations (mostly `trailing_whitespace` and `line_length`), so lint errors from these rules are expected and not introduced by agents. +SwiftLint runs with default rules. `BisonNotes AI/BisonNotes AI/.swiftlint.yml` +declares a committed baseline (`SwiftLintBaseline.json`) that is meant to +suppress the ~2,600 pre-existing violations (mostly `line_length` and body/type +length rules) so only newly introduced violations surface. + +**Baseline portability caveat:** the committed `SwiftLintBaseline.json` was +generated on macOS and stores absolute file URLs (`file:///Users/champ/...`). +SwiftLint matches baseline entries by file path, so on a Linux checkout (or any +path other than the one it was generated on) the baseline does **not** match and +`swiftlint lint` reports the full ~2,600 pre-existing violations and exits +non-zero. Those violations are pre-existing, not introduced by agents. To get a +clean, baseline-filtered run on Linux, generate a local baseline first and lint +against it: + +```bash +cd "BisonNotes AI/BisonNotes AI" +swiftlint lint --write-baseline /tmp/baseline.json # capture current state +swiftlint lint --baseline /tmp/baseline.json # 0 new violations => clean +``` + +Do not commit a Linux-generated baseline over the macOS one: per-platform rule +behavior can differ slightly, so the maintainer's macOS-generated baseline is +the source of truth for the committed file. ### Running Swift syntax checks