Replace the hardcoded signing nsec with a `nsec1...` placeholder and add a warning to always pass SIGN_WITH inline at runtime, never commit it. Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
6.9 KiB
SkateSpots — Zapstore Publishing Guide
Get SkateSpots on Zapstore as a native Android APK.
🚀 Releasing a new version (the recurring flow)
This is the process to follow every time you ship an update. For a brand-new machine setup (keystore,
android:init, etc.) see First-time setup further down.
Prerequisites
zspinstalled at~/go/bin/zspSIGN_WITHenv var with your nsec- Android SDK + Gradle installed
Step 1 — Bump versions (BOTH files)
Bump the web version in package.json:
"version": "1.0.XX" → "1.0.<XX+1>"
Bump the Android version in android/app/build.gradle:
# Check current version first
grep -n "versionCode\|versionName" android/app/build.gradle
# Bump it (replace OLD/NEW with actual numbers)
sed -i 's/versionCode OLD/versionCode NEW/' android/app/build.gradle
sed -i 's/versionName "1.0.OLD"/versionName "1.0.NEW"/' android/app/build.gradle
# Verify
grep -n "versionCode\|versionName" android/app/build.gradle
⚠️ Both numbers must be bumped —
package.jsonfor the web app,build.gradlefor the APK. Zapstore reads the version from the APK (build.gradle), so if you forget that one you'll getAsset already exists.
Step 2 — Pull latest from git
git pull
Step 3 — Build the APK
npm run android:build
This runs: (1) Vite build → (2) Capacitor sync → (3) Gradle assembleRelease.
✅ Look for BUILD SUCCESSFUL at the end.
Step 4 — Publish to Zapstore
SIGN_WITH=nsec1... ~/go/bin/zsp publish zapstore.yaml
The wizard will: parse & verify the APK → fetch Play Store metadata → sign with
your nsec → publish to wss://relay.zapstore.dev → upload the APK to
cdn.zapstore.dev.
- When prompted "Preview release in browser?" → select No, skip
- When prompted "Choose an option" → select Publish now
Full one-liner (once versions are bumped)
git pull && npm run android:build && SIGN_WITH=nsec1... ~/go/bin/zsp publish zapstore.yaml
Verify it's live
https://zapstore.dev/apps/org.skatespots.app
Troubleshooting
| Problem | Fix |
|---|---|
zapstore: command not found |
Use ~/go/bin/zsp instead |
zsp: command not found |
Run go install github.com/zapstore/zsp@latest |
Asset already exists |
Version not bumped — check build.gradle |
| Vite build fails | Run git pull first to get latest fixes |
Merge conflict on package-lock.json |
git checkout --theirs package-lock.json && git add package-lock.json && git commit |
First-time setup
The sections below are only needed the first time you set up a machine (installing tooling, creating the signing keystore, initializing the Android project). For normal releases, use the flow above.
Prerequisites (install on your machine)
- Node.js 18+ — https://nodejs.org
- Java 17 — https://adoptium.net
- Android Studio — https://developer.android.com/studio
- After install: open Android Studio → SDK Manager → install Android SDK 34
- Go (for
zspCLI) — https://go.dev/dl/
Step 1 — Clone & install
git clone nostr://npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx/git.shakespeare.diy/SkateSpots
cd SkateSpots
npm install
Step 2 — Install Capacitor
npm install @capacitor/core @capacitor/cli @capacitor/android
npm install @capacitor/splash-screen @capacitor/status-bar @capacitor/keyboard
Step 3 — Initialize Android project
npm run android:init
This:
- Builds the web app into
dist/ - Creates the
android/folder with a full Android project - Copies your web build into the Android WebView
Step 4 — Add app icons
Copy your icon files into the Android resources:
# The app already has icons in public/images/
# After android:init, copy them:
cp public/images/icon-1024.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Or use Android Studio: right-click app → New → Image Asset → import public/images/icon-1024.png
Step 5 — Generate a signing keystore (one-time)
keytool -genkey -v \
-keystore skatespots-release.jks \
-alias skatespots \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-dname "CN=SkateSpots, OU=SkateSpots, O=SkateSpots, L=Bangkok, S=Bangkok, C=TH"
Save the keystore file and passwords somewhere safe — you need them for every future release.
Step 6 — Build the release APK
npm run android:build
Or manually with signing:
npm run build
npx cap sync android
cd android
./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=../skatespots-release.jks \
-Pandroid.injected.signing.store.password=YOUR_STORE_PASSWORD \
-Pandroid.injected.signing.key.alias=skatespots \
-Pandroid.injected.signing.key.password=YOUR_KEY_PASSWORD
APK output: android/app/build/outputs/apk/release/app-release.apk
Step 7 — Install zsp (Zapstore CLI)
go install github.com/zapstore/zsp@latest
export PATH=$PATH:$(go env GOPATH)/bin
Step 8 — Publish to Zapstore
SIGN_WITH=nsec1... zsp publish --wizard
⚠️ Never commit your nsec. Always pass it inline at runtime via the
SIGN_WITHenv var, and never paste it into any tracked file.
The wizard will:
- Ask for your APK path → point to
android/app/build/outputs/apk/release/app-release.apk - Link your APK signing certificate to your Nostr identity (one-time NIP-C1 proof)
- Publish the app event to Zapstore's relay
Note: zapstore.yaml is already committed to the repo — Zapstore will use it to verify your identity automatically.
Step 9 — Verify on Zapstore
Visit: https://zapstore.dev/app/org.skatespots.app
Or search "SkateSpots" in the Zapstore app.
Updating the app
For future releases:
# 1. Build new APK
npm run android:build
# 2. Publish new version
SIGN_WITH=nsec1... zsp publish -r git.shakespeare.diy/skatespots/SkateSpots
Android project structure
android/
├── app/
│ ├── src/main/
│ │ ├── assets/public/ ← your web app lives here (auto-synced)
│ │ ├── res/ ← icons, splash screens
│ │ └── AndroidManifest.xml
│ └── build/outputs/apk/release/app-release.apk ← built APK
├── capacitor.settings.gradle
└── build.gradle
Troubleshooting
ANDROID_HOME not set:
export ANDROID_HOME=$HOME/Library/Android/sdk # macOS
export ANDROID_HOME=$HOME/Android/Sdk # Linux
export PATH=$PATH:$ANDROID_HOME/platform-tools
Gradle build fails:
- Make sure Java 17 is active:
java -version - Switch with:
export JAVA_HOME=$(/usr/libexec/java_home -v 17)
WebSocket connections blocked:
- Already handled —
capacitor.config.tssetsallowMixedContent: true