Set up distribution and release
Records the channel decision: the Mac App Store, as version 2.0 of the existing dev.rzen.indie.Kanban record - the 1.x app already listed there under the Lanework name. The question the card held open was settled by three prior rulings (the edition split's separate App Store targets with base keeping the shipping bundle id, the 2.0 marketing version, and the account's App-Store-shaped tooling); RELEASE.md records the reasoning and why notarized-direct is not pursued for 2.0. Pipeline carried over from the 1.x repo: scripts/release.sh (archive + destination=upload export, --platform macos reminders in its epilogue), ExportOptions-macOS.plist, .env.release.example (+ .gitignore entry for the real credentials). scripts/metadata/ holds the fresh 2.0 listing - rewritten description/what's-new for the rewrite (plain-files boards, live outside edits, card windows, trash, undo, templates, agent guide, accessibility), carried keywords/URLs/categories/pricing (free)/ availability (ALL), and review notes updated to the 2.0 UI. All fields within API limits; asc-metadata --dry-run validates end-to-end against the live record (version 2.0 would be created; pricing and availability already correct). Local Release archive verified: Lanework 2.0 build 165, signed, correct identity. Remaining steps are deliberately user-gated at RELEASE.md's PUSH POINT: screenshots need a real display, and everything past the archive writes to the live record of a shipping 1.x app. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# Copy to .env.release (gitignored) in the app's repo root and fill in.
|
||||
# Used by Scripts/release.sh and the asc-*.swift scripts.
|
||||
#
|
||||
# These are account-level — the same Apple developer account and API key
|
||||
# serve every indie project, so copy this file (as .env.release) from an
|
||||
# existing sibling project rather than filling it out from scratch.
|
||||
#
|
||||
# Apple Developer team that signs the build (same as DEVELOPMENT_TEAM).
|
||||
APPLE_TEAM_ID=C32Z8JNLG6
|
||||
|
||||
# App Store Connect API key (App Store Connect > Users and Access > Integrations > App Store Connect API).
|
||||
# Create a key with "App Manager" access, download the .p8 ONCE, and store it somewhere safe.
|
||||
ASC_KEY_ID=XXXXXXXXXX
|
||||
ASC_ISSUER_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
# Absolute path to the downloaded AuthKey_XXXXXXXXXX.p8 file.
|
||||
ASC_KEY_PATH=/Users/rzen/.appstoreconnect/private_keys/AuthKey_XXXXXXXXXX.p8
|
||||
@@ -9,3 +9,6 @@ xcuserdata/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# App Store credentials (appstore-publish skill)
|
||||
.env.release
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Distribution and release
|
||||
|
||||
## The channel decision (recorded 2026-07-29)
|
||||
|
||||
**Lanework ships on the Mac App Store**, as version 2.0 of the existing `dev.rzen.indie.Kanban` App Store record — the 1.x app already listed there under the name Lanework. This was not an open question by the time this document was written; three prior rulings settle it:
|
||||
|
||||
- The 2026-07-27 edition split (DESIGN/12-editions.md) defines the editions as **separate App Store targets** and gave base Lanework the existing `dev.rzen.indie.Kanban` bundle id precisely so it ships as the update to the existing record; Lanework Pro (`dev.rzen.indie.KanbanPro`) becomes its own record when it ships after pro-m1/pro-m2.
|
||||
- The marketing version has been 2.0 since m3 — the successor numbering of the shipped 1.x.
|
||||
- The portfolio's whole release tooling (appstore-publish skill, `asc-*.swift` scripts, the account's API key) is App-Store-shaped, and the 1.x pipeline for this very record already exists and works.
|
||||
|
||||
Notarized direct distribution is deliberately **not** pursued for 2.0: a second channel means a second update path and a second set of listing assets for no current audience, and nothing in the app requires escaping the App Store's sandbox rules (the app is already sandboxed with user-selected file access; base has no network entitlement at all). Revisit only if App Review rejects something architectural.
|
||||
|
||||
## The pipeline
|
||||
|
||||
Carried over from the 1.x release (same record, same scheme names — the pathfinder repo's pipeline, now living here):
|
||||
|
||||
- `scripts/release.sh` — XcodeGen regenerate, `xcodebuild archive` (Release, generic/platform=macOS), `-exportArchive` with `scripts/ExportOptions-macOS.plist` (`destination=upload` — straight to App Store Connect). Build number = git commit count, stamped by the existing post-build phase.
|
||||
- `.env.release` (gitignored; `.env.release.example` committed) — account-level App Store Connect API credentials.
|
||||
- `scripts/metadata/` — the listing source of truth (name, subtitle, description, keywords, what's-new, URLs, categories, pricing, availability, review notes). Pushed with the canonical scripts, **always with `--platform macos`**:
|
||||
|
||||
```sh
|
||||
set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-metadata.swift --bundle dev.rzen.indie.Kanban --version 2.0 --platform macos --dry-run
|
||||
```
|
||||
|
||||
- Screenshots: `scripts/metadata/screenshots/en-US/APP_DESKTOP/` — needs fresh 2.0 captures (see checklist).
|
||||
|
||||
## First-release (2.0) checklist
|
||||
|
||||
Run top to bottom; nothing below "PUSH POINT" may run without a deliberate decision — every step after it writes to the live App Store record of a shipping 1.x app.
|
||||
|
||||
1. [ ] All milestone work committed and pushed (build number = commit count).
|
||||
2. [ ] Both unit suites green: `xcodebuild test … -scheme Kanban` and `… -scheme LaneworkPro` (`-only-testing:KanbanTests` / `KanbanProTests`).
|
||||
3. [ ] `scripts/verify-editions.sh` — 26/26.
|
||||
4. [ ] UI test pass on a real display (KanbanUITests: accessibility audits + end-to-end golden flows), plus the manual checklists (`KanbanUITests/AccessibilityVerification.md`, toolbar/undo/trash/drag-preview lists).
|
||||
5. [ ] CHANGELOG.md current (it is the in-app release notes) — What's New (`scripts/metadata/version/en-US/whats_new.txt`) tells the same story.
|
||||
6. [ ] Screenshots: capture the 2.0 board on a real display (APP_DESKTOP sizes come straight from screenshots of the running app; numeric filename prefixes set the store order).
|
||||
7. [ ] Archive dry run: `scripts/release.sh` at least through the archive step completes locally.
|
||||
8. **— PUSH POINT (everything below touches the live record) —**
|
||||
9. [ ] `scripts/release.sh` — archive + upload the build.
|
||||
10. [ ] Metadata dry run, then push (`asc-metadata.swift --platform macos`, commands above).
|
||||
11. [ ] Verify pricing (currently free, carried from 1.x) and availability (ALL territories) still match intent — Pro will be the paid tier later.
|
||||
12. [ ] Stage: `asc-submit.swift --bundle dev.rzen.indie.Kanban --version 2.0 --platform macos` (waits for build processing, attaches, stages the review submission — does not submit).
|
||||
13. [ ] Review the staged listing at appstoreconnect.apple.com (age rating, privacy labels, and content rights carry over from 1.x — the stage step's errors will name anything missing).
|
||||
14. [ ] Submit: the same command with `--submit`.
|
||||
|
||||
## Lanework Pro (later)
|
||||
|
||||
Pro ships after pro-m1 (git history) and pro-m2 (remote sync) as a **new** App Store record: bundle id `dev.rzen.indie.KanbanPro`, its own metadata folder and pricing (paid), created by hand in App Store Connect first (the API cannot create app records). The `scripts/release.sh` config block parameterizes to the `LaneworkPro` scheme at that point; everything else in the pipeline is shared.
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- TestFlight / App Store distribution for the macOS target. -->
|
||||
<key>method</key>
|
||||
<string>app-store-connect</string>
|
||||
<key>teamID</key>
|
||||
<string>C32Z8JNLG6</string>
|
||||
<!-- destination=upload makes -exportArchive push the build straight to
|
||||
App Store Connect instead of writing a local .pkg. -->
|
||||
<key>destination</key>
|
||||
<string>upload</string>
|
||||
<key>signingStyle</key>
|
||||
<string>automatic</string>
|
||||
<key>uploadSymbols</key>
|
||||
<true/>
|
||||
<key>stripSwiftSymbols</key>
|
||||
<true/>
|
||||
<key>manageAppVersionAndBuildNumber</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1 @@
|
||||
{"availableInNewTerritories": true, "territories": "ALL"}
|
||||
@@ -0,0 +1 @@
|
||||
{"primary": "PRODUCTIVITY", "secondary": null}
|
||||
@@ -0,0 +1 @@
|
||||
Lanework
|
||||
@@ -0,0 +1 @@
|
||||
https://indie.rzen.dev/apps/lanework/privacy
|
||||
@@ -0,0 +1 @@
|
||||
Kanban boards in plain files
|
||||
@@ -0,0 +1 @@
|
||||
{"baseTerritory": "USA", "price": "0.00"}
|
||||
@@ -0,0 +1,25 @@
|
||||
Lanework is a native macOS kanban board whose boards are plain folders and Markdown files. Fast, fluid, and honest about your data: the filesystem is the source of truth, and the app is a beautiful live view over it.
|
||||
|
||||
MADE FOR THE MAC
|
||||
Lanework feels like it shipped with macOS. One window per board, real menus and toolbars you can customize, system-native undo, and every size derived from your text settings. No web wrapper, no cross-platform chrome.
|
||||
|
||||
BOARDS ARE PLAIN FILES
|
||||
Every board, lane, and card is a folder with a Markdown file inside — readable in any editor, synced by anything that syncs files, versioned however you like, and yours forever. There is no database, no account, and no server. Delete the app and your boards are still just folders.
|
||||
|
||||
WORKS WITH YOUR TOOLS — AND YOUR AI AGENTS
|
||||
Because boards are files, anything that can write files is a first-class user of your board: text editors, scripts, Finder, and AI agents. Edit a board from outside and the open window updates live, with changes announced and nothing overwritten. Lanework even writes a guide file into each board that teaches AI agents the format, so an agent dropped into the folder already knows how to add and move cards correctly.
|
||||
|
||||
CARDS WITH REAL CONTENT
|
||||
Open any card in its own window: a formatted Markdown preview with clickable checklists, a fast editor with syntax highlighting, file attachments with Quick Look, and a raw-source view that shows you the literal file — nothing is hidden.
|
||||
|
||||
FAST TO DRIVE
|
||||
Drag cards and lanes with buttery reflow, or never touch the mouse: the whole app works from the keyboard, from arrow-key navigation to drag-free card moves. Cut, copy, and paste cards across boards. Search filters the board as you type. Deleted cards land in a per-board trash you can open, restore from, or empty. Undo covers everything.
|
||||
|
||||
TEMPLATES
|
||||
Start from ten bundled templates or save any board as your own. A template is itself just a board folder — drop one in, and it's a template.
|
||||
|
||||
ACCESSIBLE, PROPERLY
|
||||
Full VoiceOver support with polite live announcements, Full Keyboard Access on every control, Reduce Motion and contrast accommodations throughout, and text colors computed for real readability on any board background.
|
||||
|
||||
PRIVATE BY DESIGN
|
||||
No account, no analytics, no network access. Your boards are local files that never leave your Mac unless you move them.
|
||||
@@ -0,0 +1 @@
|
||||
kanban,board,tasks,todo,markdown,folders,project,cards,columns,notes,files,planning,productivity
|
||||
@@ -0,0 +1 @@
|
||||
https://indie.rzen.dev/apps/lanework
|
||||
@@ -0,0 +1 @@
|
||||
A simple, delightful kanban board that feels right at home on your Mac. Fluid drag and drop, full keyboard control — and your boards are yours to keep.
|
||||
@@ -0,0 +1 @@
|
||||
https://indie.rzen.dev/apps/lanework/support
|
||||
@@ -0,0 +1,11 @@
|
||||
Lanework 2.0 is a ground-up rewrite around one idea: your boards are plain folders and Markdown files, and the app is a live view over them.
|
||||
|
||||
• Boards now update live when files change — edit from any app, script, or AI agent and the window follows, with changes announced.
|
||||
• Cards open in their own window: formatted Markdown preview, checklists, a fast editor, file attachments, and a raw-source view.
|
||||
• A per-board trash: deleted cards are recoverable until you empty it.
|
||||
• System-native undo and redo across every board action.
|
||||
• Ten bundled board templates, plus save-any-board-as-template.
|
||||
• Cut, copy, and paste cards and lanes — within a board, across boards, or as text.
|
||||
• Live search that filters the board as you type.
|
||||
• Full keyboard control, VoiceOver support with live announcements, and system accessibility accommodations throughout.
|
||||
• Every board gets a guide file that teaches AI agents the folder format.
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"contactFirstName": "Rouslan",
|
||||
"contactLastName": "Zenetl",
|
||||
"contactEmail": "[email protected]",
|
||||
"contactPhone": "+16465840598",
|
||||
"demoAccountRequired": false,
|
||||
"notes": "Lanework is a native macOS app whose boards are plain folders — each board, lane, and card is a folder containing an index.md (YAML frontmatter + Markdown body). There is no account or login of any kind, and no network access: all data lives in local files the user picks. To test: launch the app, create a new board from the welcome window (File > New Board opens a template chooser; picking one creates a .kanban folder where you choose). Add cards with Cmd-N and lanes with the New Lane command or toolbar item; cards rename inline, drag within and between lanes, and open in their own window on double-click (Markdown preview, editor, attachments). Deleting a card moves it to the board's own trash (View > Show Trash); Empty Trash removes permanently. The app uses user-selected file access (App Sandbox) to read and write the board folders the user chooses. Version 2.0 is a ground-up rewrite of the 1.x app under the same name."
|
||||
}
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# release.sh — archive Kanban (macOS) and upload to TestFlight / App Store Connect.
|
||||
#
|
||||
# Usage: Scripts/release.sh
|
||||
#
|
||||
# No third-party tooling: pure xcodebuild + the App Store Connect API key.
|
||||
# Credentials live in .env.release (gitignored); see .env.release.example.
|
||||
#
|
||||
# What it does:
|
||||
# 1. Regenerates the Xcode project with XcodeGen.
|
||||
# 2. CFBundleVersion is stamped with the git commit count during the archive
|
||||
# by the postBuildScripts phase (app-versioning skill) — this script does
|
||||
# not touch the build number itself, so commit everything before releasing.
|
||||
# 3. xcodebuild archive -> .xcarchive (Release config; the full app-sandbox
|
||||
# entitlements come from project.yml's Release config).
|
||||
# 4. xcodebuild -exportArchive with destination=upload -> App Store Connect.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
APP_NAME="Kanban"
|
||||
PROJECT="Kanban.xcodeproj"
|
||||
SCHEME="Kanban"
|
||||
BUNDLE_ID="dev.rzen.indie.Kanban"
|
||||
BUILD_DIR="$ROOT/build"
|
||||
|
||||
# ---- credentials ------------------------------------------------------------
|
||||
ENV_FILE="$ROOT/.env.release"
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
set -a; . "$ENV_FILE"; set +a
|
||||
fi
|
||||
: "${APPLE_TEAM_ID:?Set APPLE_TEAM_ID (copy .env.release.example -> .env.release)}"
|
||||
: "${ASC_KEY_ID:?Set ASC_KEY_ID in .env.release}"
|
||||
: "${ASC_ISSUER_ID:?Set ASC_ISSUER_ID in .env.release}"
|
||||
: "${ASC_KEY_PATH:?Set ASC_KEY_PATH in .env.release}"
|
||||
[ -f "$ASC_KEY_PATH" ] || { echo "❌ API key not found at: $ASC_KEY_PATH"; exit 1; }
|
||||
|
||||
AUTH=(
|
||||
-authenticationKeyPath "$ASC_KEY_PATH"
|
||||
-authenticationKeyID "$ASC_KEY_ID"
|
||||
-authenticationKeyIssuerID "$ASC_ISSUER_ID"
|
||||
-allowProvisioningUpdates
|
||||
)
|
||||
|
||||
# ---- regenerate project -----------------------------------------------------
|
||||
if command -v xcodegen >/dev/null 2>&1; then
|
||||
echo "🧩 Generating $PROJECT ..."
|
||||
xcodegen generate
|
||||
elif [ ! -d "$PROJECT" ]; then
|
||||
echo "❌ $PROJECT missing and xcodegen not installed."; exit 1
|
||||
fi
|
||||
|
||||
# ---- versioning -------------------------------------------------------------
|
||||
# CFBundleVersion is stamped during the archive by the postBuildScripts phase
|
||||
# (app-versioning skill). MARKETING_VERSION is read here only for the log line.
|
||||
BUILD_NUMBER="$(git rev-list HEAD --count)"
|
||||
VERSION="$(grep -m1 'MARKETING_VERSION:' project.yml | sed -E 's/.*"([^"]+)".*/\1/')"
|
||||
echo "📦 $APP_NAME $VERSION — build $BUILD_NUMBER"
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
ARCHIVE_PATH="$BUILD_DIR/$SCHEME.xcarchive"
|
||||
EXPORT_PATH="$BUILD_DIR/$SCHEME-export"
|
||||
rm -rf "$ARCHIVE_PATH" "$EXPORT_PATH"
|
||||
|
||||
echo ""
|
||||
echo "🛠 Archiving $SCHEME (macOS, Release) ..."
|
||||
xcodebuild archive \
|
||||
-project "$PROJECT" \
|
||||
-scheme "$SCHEME" \
|
||||
-configuration Release \
|
||||
-destination "generic/platform=macOS" \
|
||||
-archivePath "$ARCHIVE_PATH" \
|
||||
CODE_SIGN_STYLE=Automatic \
|
||||
DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
|
||||
"${AUTH[@]}"
|
||||
|
||||
echo "🚀 Exporting + uploading to App Store Connect ..."
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath "$ARCHIVE_PATH" \
|
||||
-exportPath "$EXPORT_PATH" \
|
||||
-exportOptionsPlist "$SCRIPT_DIR/ExportOptions-macOS.plist" \
|
||||
"${AUTH[@]}"
|
||||
|
||||
echo "✅ $APP_NAME uploaded (build $BUILD_NUMBER)."
|
||||
echo ""
|
||||
echo "🎉 Done. The build appears in App Store Connect > TestFlight after processing (~5–15 min)."
|
||||
echo ""
|
||||
echo "➡️ App Store listing + review (macOS). The asc scripts default to iOS, so"
|
||||
echo " pass --platform macos here — REQUIRED, or they create a spurious iOS version:"
|
||||
echo " set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-metadata.swift --bundle $BUNDLE_ID --version $VERSION --platform macos --dry-run"
|
||||
echo " set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-submit.swift --bundle $BUNDLE_ID --version $VERSION --platform macos"
|
||||
echo " Use the version matching the macOS App Store version in ASC (see Scripts/metadata/README.md)."
|
||||
Reference in New Issue
Block a user