Add App Store release pipeline, adopt ISC license, add unit test target
- Scripts/release.sh archives and uploads the macOS app via the appstore-publish skill's API-driven pipeline (xcodebuild + App Store Connect key, no fastlane); ExportOptions-macOS.plist adapts the skill's iOS-centric export options for generic/platform=macOS. Scripts/metadata/ drafts the listing from the README. Canonical asc-*.swift scripts are referenced in place from indie-skills, matching Weight/HIIT. - Replace the all-rights-reserved LICENSE.md with the portfolio-standard ISC license (REVIEW2 D2b), preserving the existing copyright year/holder. - Add an IceGlassTests target (project.yml) with a real smoke test suite covering GameState's progression-rank ordering and polling-interval mapping — logic MainService relies on to reject stale API regressions.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?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>
|
||||
<!-- Mac App Store distribution for the IceGlass 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>manageAppVersionAndBuildNumber</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,58 @@
|
||||
# App Store metadata
|
||||
|
||||
Local source-of-truth for the **IceGlass (macOS)** App Store listing. The
|
||||
appstore-publish skill's `asc-metadata.swift` (invoked in place from
|
||||
`../indie-skills`) reads this folder and pushes it to App Store Connect via
|
||||
the API; only files that are present get pushed. Then `asc-submit.swift`
|
||||
attaches the build and submits.
|
||||
|
||||
```
|
||||
set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-metadata.swift --bundle dev.rzen.indie.IceGlass --version X.Y.Z --dry-run # preview
|
||||
set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-metadata.swift --bundle dev.rzen.indie.IceGlass --version X.Y.Z # push
|
||||
set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-submit.swift --bundle dev.rzen.indie.IceGlass --version X.Y.Z --submit # submit
|
||||
```
|
||||
|
||||
**Note:** this metadata set is for the **macOS** app record only. IceGlass
|
||||
also ships an iOS companion (`IceGlass-iOS`, bundle ID
|
||||
`dev.rzen.indie.IceGlass.iOS`) that isn't wired into the release pipeline
|
||||
yet — when it's ready to publish, give it its own App Store Connect app
|
||||
record and its own sibling metadata directory (e.g. `Scripts/metadata-ios/`),
|
||||
since `asc-metadata.swift` currently queries the `IOS` platform filter when
|
||||
looking up versions, not `MAC_OS`.
|
||||
|
||||
## Layout & character limits
|
||||
|
||||
| File | Field | Limit | API resource |
|
||||
|---|---|---|---|
|
||||
| `app/<locale>/name.txt` | App name | 30 | `appInfoLocalizations` |
|
||||
| `app/<locale>/subtitle.txt` | Subtitle | 30 | `appInfoLocalizations` |
|
||||
| `app/<locale>/privacy_url.txt` | Privacy policy URL | — | `appInfoLocalizations` |
|
||||
| `app/categories.json` | Categories | — | `appInfos` |
|
||||
| `version/<locale>/description.txt` | Description | 4000 | `appStoreVersionLocalizations` |
|
||||
| `version/<locale>/keywords.txt` | Keywords (comma-sep) | 100 | `appStoreVersionLocalizations` |
|
||||
| `version/<locale>/promotional_text.txt` | Promotional text | 170 | `appStoreVersionLocalizations` |
|
||||
| `version/<locale>/whats_new.txt` | What's New | 4000 | `appStoreVersionLocalizations` |
|
||||
| `version/<locale>/support_url.txt` | Support URL | — | `appStoreVersionLocalizations` |
|
||||
| `version/review.json` | Review contact / demo / notes | — | `appStoreReviewDetails` |
|
||||
| `screenshots/<locale>/<DISPLAY_TYPE>/*.png` | Screenshots | — | `appScreenshotSets` |
|
||||
|
||||
`name` + `subtitle` are **app-level**; `description` + `keywords` are
|
||||
**version-level**. `whats_new.txt` only applies after the first version —
|
||||
leave it out for 1.0. No `whats_new.txt` or `screenshots/` are checked in
|
||||
yet — screenshots still need to be captured (see the appstore-publish
|
||||
skill's "Step 3 — Screenshots" for the simulator-capture recipe; macOS
|
||||
screenshots are plain `screencapture`/window captures at the App Store's
|
||||
required pixel sizes, not simulator grabs).
|
||||
|
||||
## Screenshot display types (macOS)
|
||||
|
||||
Common types: `APP_DESKTOP` (1280×800 or larger, 16:10). One folder per
|
||||
device class; filenames sort the order. The local set is authoritative —
|
||||
re-running replaces the remote set for any display type you provide.
|
||||
|
||||
## Not handled here (separate resource families)
|
||||
|
||||
Age rating (`ageRatingDeclarations`), pricing/availability
|
||||
(`appPriceSchedules` / `appAvailabilities`), and App Privacy labels
|
||||
(`appDataUsages`). `asc-submit.swift --submit` (see the appstore-publish
|
||||
skill) reports any of these that block the submission.
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"primary": "SPORTS",
|
||||
"secondary": null
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
IceGlass – NHL Game Tracker
|
||||
@@ -0,0 +1 @@
|
||||
https://indie.rzen.dev/apps/iceglass/privacy
|
||||
@@ -0,0 +1 @@
|
||||
Menu bar scores & schedule
|
||||
@@ -0,0 +1,25 @@
|
||||
IceGlass puts NHL game situational awareness right in your Mac's menu bar. No window to open, no tab to switch to — click the shield icon and see exactly where every game stands.
|
||||
|
||||
ALWAYS-CURRENT SCOREBOARD
|
||||
• Shows games from yesterday, today, and tomorrow, grouped by date
|
||||
• Regular-season rows show the league-wide game number (e.g. "#547 NYR @ WAS")
|
||||
• Live scores update automatically with a compact "0:2 (FINAL)" style format
|
||||
• Choose which days you want to see in Display Options
|
||||
|
||||
PLAYOFF MODE
|
||||
• During the playoffs, every round played so far (Round 1 through the current round) gets its own section
|
||||
• Each series shows its score, the next game number, and the upcoming tip-off time
|
||||
• Completed series are clearly marked once a team is eliminated
|
||||
|
||||
STAY ON TOP OF EVERY GAME
|
||||
• Goal notifications include the scoring team's logo
|
||||
• Game-start and game-ended notifications
|
||||
• Dynamic polling — as often as every 7 seconds while a game is live, backing off automatically when nothing's happening
|
||||
• Click a game to open it in NHL GameCenter; option-click for NHL Videocast
|
||||
|
||||
BUILT FOR THE MENU BAR
|
||||
• No dock icon, no clutter — IceGlass lives quietly in your menu bar
|
||||
• Launch at Login so it's always ready
|
||||
• Refresh Now (⌘R) whenever you want the latest instantly
|
||||
|
||||
IceGlass is for anyone who wants to glance at the score without opening a browser tab — season-long, through every round of the playoffs.
|
||||
@@ -0,0 +1 @@
|
||||
nhl,hockey,scores,schedule,menu bar,live scores,playoffs,scoreboard,standings,game tracker
|
||||
@@ -0,0 +1 @@
|
||||
Live NHL scores in your Mac's menu bar — yesterday, today, and tomorrow's games, full playoff brackets, and goal notifications, at a glance.
|
||||
@@ -0,0 +1 @@
|
||||
https://indie.rzen.dev/apps/iceglass/support
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"contactFirstName": "Rouslan",
|
||||
"contactLastName": "Zenetl",
|
||||
"contactPhone": "+16465840598",
|
||||
"contactEmail": "[email protected]",
|
||||
"demoAccountRequired": false,
|
||||
"notes": "IceGlass is a macOS menu bar app — no window, no dock icon (LSUIElement). After launching, click the NHL shield icon in the menu bar to open the dropdown showing yesterday/today/tomorrow's games. No account or login is required; it reads NHL's public scoreboard API. During the regular season the dropdown groups games by date; during the playoffs it also lists a ROUND section per round played so far. Display Options (which days to show) and Launch at Login are in the app's settings, reachable from the dropdown."
|
||||
}
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# release.sh — archive IceGlass (macOS menu bar app) and upload to
|
||||
# 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. Stamps CFBundleVersion with the git commit count (monotonic build no.)
|
||||
# via Scripts/update_build_number.sh during the build.
|
||||
# 3. xcodebuild archive -> .xcarchive (generic/platform=macOS)
|
||||
# 4. xcodebuild -exportArchive with destination=upload -> App Store Connect.
|
||||
#
|
||||
# NOTE: this project also ships an iOS companion (IceGlass-iOS, its own
|
||||
# bundle ID and — eventually — its own App Store Connect app record). This
|
||||
# script only releases the macOS app. When the iOS companion is ready to
|
||||
# publish, copy this file to release-ios.sh, swap PROJECT/SCHEME/BUNDLE_ID
|
||||
# for the iOS target, destination to "generic/platform=iOS", and point
|
||||
# -exportOptionsPlist at an ExportOptions-iOS.plist (see the appstore-publish
|
||||
# skill's template) — plus a sibling Scripts/metadata/ set for its own listing.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
PROJECT="IceGlass.xcodeproj"
|
||||
SCHEME="IceGlass"
|
||||
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 build by Scripts/update_build_number.sh
|
||||
# (raw commit count). The marketing version lives in project.yml's
|
||||
# MARKETING_VERSION setting for the IceGlass target (Info.plist references it
|
||||
# via $(MARKETING_VERSION), so there's no literal to PlistBuddy-read).
|
||||
BUILD_NUMBER="$(git rev-list HEAD --count)"
|
||||
MARKETING_VERSION="$(grep -m1 'MARKETING_VERSION:' project.yml | sed -E 's/.*"([^"]+)".*/\1/')"
|
||||
echo "📦 Version $MARKETING_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) ..."
|
||||
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 "✅ IceGlass uploaded (build $BUILD_NUMBER)."
|
||||
echo ""
|
||||
echo "🎉 Done. The build appears in App Store Connect > TestFlight after processing (~5–15 min)."
|
||||
echo ""
|
||||
echo "➡️ Push metadata, then stage/submit for review with the canonical scripts"
|
||||
echo " (apps sit as siblings of indie-skills under ~/Documents/indie; --bundle"
|
||||
echo " and --version are required flags — there are no per-app defaults):"
|
||||
echo ""
|
||||
echo " set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-metadata.swift --bundle dev.rzen.indie.IceGlass --version $MARKETING_VERSION --dry-run"
|
||||
echo " set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-metadata.swift --bundle dev.rzen.indie.IceGlass --version $MARKETING_VERSION"
|
||||
echo " set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-submit.swift --bundle dev.rzen.indie.IceGlass --version $MARKETING_VERSION # stage only"
|
||||
echo " set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-submit.swift --bundle dev.rzen.indie.IceGlass --version $MARKETING_VERSION --submit # stage + submit"
|
||||
Reference in New Issue
Block a user