Deploy web app to satsprice.app/app/ and link it from the website
Deploy website to GitHub Pages / deploy (push) Canceled after 0s
Deploy website to GitHub Pages / deploy (push) Canceled after 0s
The GitHub Pages workflow now builds the Compose wasmJs web app and publishes it alongside the marketing site, so it's reachable at /app/. Add nav/hero/download links to it and list Web among the supported platforms. Add website/serve-local.sh to reproduce the production layout locally, and document the setup in README/AGENTS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,14 @@ on:
|
|||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
paths:
|
paths:
|
||||||
- "website/**"
|
- "website/**"
|
||||||
|
- "shared/**"
|
||||||
|
- "webApp/**"
|
||||||
|
- "gradle/**"
|
||||||
|
- "gradlew"
|
||||||
|
- "gradlew.bat"
|
||||||
|
- "build.gradle.kts"
|
||||||
|
- "settings.gradle.kts"
|
||||||
|
- "gradle.properties"
|
||||||
- ".github/workflows/pages.yml"
|
- ".github/workflows/pages.yml"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -26,12 +34,26 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up JDK
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: "21"
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
- name: Build web app
|
||||||
|
run: ./gradlew :webApp:wasmJsBrowserDistribution
|
||||||
|
- name: Assemble site
|
||||||
|
run: |
|
||||||
|
mkdir -p _site/app
|
||||||
|
cp -r website/. _site/
|
||||||
|
cp -r webApp/build/dist/wasmJs/productionExecutable/. _site/app/
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v5
|
uses: actions/configure-pages@v5
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: "./website"
|
path: "./_site"
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v4
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ xcodebuild*.log
|
|||||||
|
|
||||||
.gradle/
|
.gradle/
|
||||||
.idea/
|
.idea/
|
||||||
|
/website/app/
|
||||||
.*.swp
|
.*.swp
|
||||||
.*.swo
|
.*.swo
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ real trial-and-error to get right.
|
|||||||
## What this is
|
## What this is
|
||||||
|
|
||||||
SatsPrice is a Kotlin Multiplatform app, currently released on iOS, macOS,
|
SatsPrice is a Kotlin Multiplatform app, currently released on iOS, macOS,
|
||||||
and Android (see README's Supported Platforms/Download and Install). Web and
|
and Android (see README's Supported Platforms/Download and Install).
|
||||||
Desktop/JVM are real future release targets too — `desktopApp` already has
|
Desktop/JVM is a real future release target — `desktopApp` already has full
|
||||||
full native packaging configured (DMG/MSI/DEB, see its `build.gradle.kts`) —
|
native packaging configured (DMG/MSI/DEB, see its `build.gradle.kts`) — but
|
||||||
but neither has a release/CI pipeline set up yet, so treat them as
|
has no release/CI pipeline set up yet, so treat it as not-yet-shipped rather
|
||||||
not-yet-shipped rather than dev-only. The app converts between BTC, Sats,
|
than dev-only. Web is a step ahead of Desktop: `.github/workflows/pages.yml`
|
||||||
|
builds `:webApp:wasmJsBrowserDistribution` and publishes it alongside the
|
||||||
|
marketing site to `satsprice.app/app/` on every push to `main` — see
|
||||||
|
"Website and web app deployment" below. The app converts between BTC, Sats,
|
||||||
and fiat currencies using live exchange rates (Coinbase, CoinGecko, or a
|
and fiat currencies using live exchange rates (Coinbase, CoinGecko, or a
|
||||||
manually typed-in rate).
|
manually typed-in rate).
|
||||||
|
|
||||||
@@ -149,6 +152,27 @@ fallback instead (`data/db/LocalStorageStores.kt`), which does survive page
|
|||||||
reloads, just scoped to the browser profile/origin (cleared by clearing
|
reloads, just scoped to the browser profile/origin (cleared by clearing
|
||||||
site data, not shared across browsers/devices).
|
site data, not shared across browsers/devices).
|
||||||
|
|
||||||
|
## Website and web app deployment
|
||||||
|
|
||||||
|
`website/` is a static marketing site (plain HTML/CSS/JS, no build step of
|
||||||
|
its own) deployed to `satsprice.app` via GitHub Pages
|
||||||
|
(`.github/workflows/pages.yml`). That workflow does more than upload
|
||||||
|
`website/` as-is: it also runs `./gradlew :webApp:wasmJsBrowserDistribution`
|
||||||
|
and copies the result into `website/app/` before publishing, so the live
|
||||||
|
Compose web app is reachable at `satsprice.app/app/` and linked from the
|
||||||
|
site (nav, hero, download section) via `/app/`.
|
||||||
|
|
||||||
|
- `website/app/` is git-ignored — it's build output, generated fresh by CI
|
||||||
|
(and locally by the script below), never committed.
|
||||||
|
- To reproduce the production layout locally (site + web app together,
|
||||||
|
`/app/` links working): `./website/serve-local.sh [port]`. It builds the
|
||||||
|
wasmJs distribution, copies it into `website/app/`, and serves
|
||||||
|
`website/` with `python3 -m http.server`.
|
||||||
|
- If you change the Pages workflow, remember `paths:` in the trigger
|
||||||
|
includes `webApp/**`/`shared/**`/Gradle files, not just `website/**` —
|
||||||
|
a shared-code change that affects the web app should also redeploy the
|
||||||
|
site.
|
||||||
|
|
||||||
## Testing and verification
|
## Testing and verification
|
||||||
|
|
||||||
- `shared/src/commonTest` holds the real unit test suite (pure Kotlin,
|
- `shared/src/commonTest` holds the real unit test suite (pure Kotlin,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ height="70">](https://github.com/tyiu/sats-price/releases)
|
|||||||
|
|
||||||
## Supported Platforms
|
## Supported Platforms
|
||||||
|
|
||||||
iOS 16.0+ • macOS 13.0+ • Android 7.0+
|
iOS 16.0+ • macOS 13.0+ • Android 7.0+ • [Web](https://satsprice.app/app/) (any modern browser)
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
@@ -75,6 +75,9 @@ options:
|
|||||||
- Wasm target (faster, modern browsers): `./gradlew :webApp:wasmJsBrowserDevelopmentRun`
|
- Wasm target (faster, modern browsers): `./gradlew :webApp:wasmJsBrowserDevelopmentRun`
|
||||||
- JS target (slower, supports older browsers): `./gradlew :webApp:jsBrowserDevelopmentRun`
|
- JS target (slower, supports older browsers): `./gradlew :webApp:jsBrowserDevelopmentRun`
|
||||||
- iOS app: open the [/iosApp](./iosApp) directory in Xcode and run it from there.
|
- iOS app: open the [/iosApp](./iosApp) directory in Xcode and run it from there.
|
||||||
|
- [Website](./website) (with the web app built and served at `/app/`, matching production):
|
||||||
|
`./website/serve-local.sh` (serves at http://localhost:8000 by default; pass a port number to
|
||||||
|
override)
|
||||||
|
|
||||||
### Running tests
|
### Running tests
|
||||||
|
|
||||||
|
|||||||
+20
-2
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>SatsPrice — Bitcoin Price & Sats Converter</title>
|
<title>SatsPrice — Bitcoin Price & Sats Converter</title>
|
||||||
<meta name="description" content="SatsPrice converts Bitcoin, satoshis, and fiat currency in real time, sourced from Coinbase and CoinGecko. Free and open source for Android, iOS, and macOS." />
|
<meta name="description" content="SatsPrice converts Bitcoin, satoshis, and fiat currency in real time, sourced from Coinbase and CoinGecko. Free and open source for Android, iOS, macOS, and Web." />
|
||||||
<link rel="icon" href="assets/app-icon.png" type="image/png" />
|
<link rel="icon" href="assets/app-icon.png" type="image/png" />
|
||||||
<link rel="stylesheet" href="styles.css" />
|
<link rel="stylesheet" href="styles.css" />
|
||||||
</head>
|
</head>
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
<a href="#features">Features</a>
|
<a href="#features">Features</a>
|
||||||
<a href="#platforms">Screenshots</a>
|
<a href="#platforms">Screenshots</a>
|
||||||
<a href="#download">Download</a>
|
<a href="#download">Download</a>
|
||||||
|
<a href="/app/" target="_blank" rel="noopener">Web App</a>
|
||||||
<a href="https://github.com/tyiu/sats-price" target="_blank" rel="noopener">Code</a>
|
<a href="https://github.com/tyiu/sats-price" target="_blank" rel="noopener">Code</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
<a href="#features">Features</a>
|
<a href="#features">Features</a>
|
||||||
<a href="#platforms">Screenshots</a>
|
<a href="#platforms">Screenshots</a>
|
||||||
<a href="#download">Download</a>
|
<a href="#download">Download</a>
|
||||||
|
<a href="/app/" target="_blank" rel="noopener">Web App</a>
|
||||||
<a href="https://github.com/tyiu/sats-price" target="_blank" rel="noopener">GitHub</a>
|
<a href="https://github.com/tyiu/sats-price" target="_blank" rel="noopener">GitHub</a>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -60,6 +62,11 @@
|
|||||||
<a href="#platforms" class="btn btn-ghost">See screenshots</a>
|
<a href="#platforms" class="btn btn-ghost">See screenshots</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p class="hero-webapp">
|
||||||
|
Don't want to install anything?
|
||||||
|
<a href="/app/" target="_blank" rel="noopener">Try it in your browser →</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="platform-pills">
|
<div class="platform-pills">
|
||||||
<span class="pill">
|
<span class="pill">
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17.05 12.536c-.03-3.06 2.5-4.53 2.61-4.6-1.42-2.08-3.63-2.37-4.42-2.4-1.88-.19-3.68 1.11-4.63 1.11-.96 0-2.42-1.09-3.98-1.06-2.05.03-3.94 1.19-5 3.02-2.13 3.7-.55 9.18 1.53 12.18 1.02 1.47 2.23 3.11 3.82 3.05 1.53-.06 2.11-.99 3.96-.99 1.84 0 2.37.99 3.98.96 1.65-.03 2.69-1.5 3.7-2.98 1.17-1.71 1.65-3.37 1.67-3.45-.04-.02-3.2-1.23-3.24-4.84zM14.15 3.6c.84-1.02 1.41-2.43 1.25-3.6-1.22.06-2.69.84-3.55 1.85-.78.9-1.46 2.34-1.28 3.57 1.2.09 2.75-.61 3.58-1.82z"/></svg>
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17.05 12.536c-.03-3.06 2.5-4.53 2.61-4.6-1.42-2.08-3.63-2.37-4.42-2.4-1.88-.19-3.68 1.11-4.63 1.11-.96 0-2.42-1.09-3.98-1.06-2.05.03-3.94 1.19-5 3.02-2.13 3.7-.55 9.18 1.53 12.18 1.02 1.47 2.23 3.11 3.82 3.05 1.53-.06 2.11-.99 3.96-.99 1.84 0 2.37.99 3.98.96 1.65-.03 2.69-1.5 3.7-2.98 1.17-1.71 1.65-3.37 1.67-3.45-.04-.02-3.2-1.23-3.24-4.84zM14.15 3.6c.84-1.02 1.41-2.43 1.25-3.6-1.22.06-2.69.84-3.55 1.85-.78.9-1.46 2.34-1.28 3.57 1.2.09 2.75-.61 3.58-1.82z"/></svg>
|
||||||
@@ -73,6 +80,10 @@
|
|||||||
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17.6 9.48l1.84-3.18c.16-.31.04-.69-.26-.85-.29-.15-.65-.06-.83.22l-1.88 3.24a11.43 11.43 0 0 0-8.94 0L5.65 5.67c-.18-.28-.54-.37-.83-.22-.3.16-.42.54-.26.85L6.4 9.48C3.3 11.25 1.28 14.44 1 18h22c-.28-3.56-2.3-6.75-5.4-8.52zM7 15.25a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5zm10 0a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5z"/></svg>
|
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17.6 9.48l1.84-3.18c.16-.31.04-.69-.26-.85-.29-.15-.65-.06-.83.22l-1.88 3.24a11.43 11.43 0 0 0-8.94 0L5.65 5.67c-.18-.28-.54-.37-.83-.22-.3.16-.42.54-.26.85L6.4 9.48C3.3 11.25 1.28 14.44 1 18h22c-.28-3.56-2.3-6.75-5.4-8.52zM7 15.25a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5zm10 0a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5z"/></svg>
|
||||||
Android
|
Android
|
||||||
</span>
|
</span>
|
||||||
|
<span class="pill">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||||
|
Web
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="trust-row">
|
<div class="trust-row">
|
||||||
@@ -162,7 +173,7 @@
|
|||||||
<div class="feature-card wide">
|
<div class="feature-card wide">
|
||||||
<div class="icon">💻</div>
|
<div class="icon">💻</div>
|
||||||
<h3>Native on every platform</h3>
|
<h3>Native on every platform</h3>
|
||||||
<p>SwiftUI on iOS and macOS, Compose Multiplatform on Android — built with Kotlin Multiplatform to feel right at home on each device, not like a port.</p>
|
<p>SwiftUI on iOS and macOS, Compose Multiplatform on Android and Web — built with Kotlin Multiplatform to feel right at home on each device, not like a port.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
@@ -279,10 +290,17 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="webapp-divider"><span>or</span></div>
|
||||||
|
|
||||||
|
<div class="webapp-cta">
|
||||||
|
<a href="/app/" class="btn btn-ghost" target="_blank" rel="noopener">Open the web app — no install required</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="version-row">
|
<div class="version-row">
|
||||||
<span class="pill">🍎 iOS 16.0+</span>
|
<span class="pill">🍎 iOS 16.0+</span>
|
||||||
<span class="pill">🖥️ macOS 13.0+</span>
|
<span class="pill">🖥️ macOS 13.0+</span>
|
||||||
<span class="pill">🤖 Android 7.0+</span>
|
<span class="pill">🤖 Android 7.0+</span>
|
||||||
|
<span class="pill">🌐 Web — any modern browser</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="oss-note">
|
<p class="oss-note">
|
||||||
|
|||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Builds the web app, drops it into website/app/, and serves the whole
|
||||||
|
# website locally so the /app/ links behave like they do in production.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PORT="${1:-8000}"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
echo "Building web app (wasmJs)..."
|
||||||
|
"$REPO_ROOT/gradlew" -p "$REPO_ROOT" :webApp:wasmJsBrowserDistribution
|
||||||
|
|
||||||
|
echo "Copying web app into website/app/..."
|
||||||
|
mkdir -p "$SCRIPT_DIR/app"
|
||||||
|
cp -r "$REPO_ROOT/webApp/build/dist/wasmJs/productionExecutable/." "$SCRIPT_DIR/app/"
|
||||||
|
|
||||||
|
echo "Serving website at http://localhost:$PORT/ (web app at http://localhost:$PORT/app/)"
|
||||||
|
cd "$SCRIPT_DIR" && python3 -m http.server "$PORT"
|
||||||
@@ -268,6 +268,20 @@ h1, h2, h3, h4 {
|
|||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero-webapp {
|
||||||
|
margin-top: 18px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-faint);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-webapp a {
|
||||||
|
color: var(--gold);
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-webapp a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
.platform-pills {
|
.platform-pills {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -652,6 +666,26 @@ h1, h2, h3, h4 {
|
|||||||
.badge-link.obtainium img,
|
.badge-link.obtainium img,
|
||||||
.badge-link.github img { height: 56px; }
|
.badge-link.github img { height: 56px; }
|
||||||
|
|
||||||
|
.webapp-divider {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 14px;
|
||||||
|
margin-top: 30px;
|
||||||
|
color: var(--text-faint);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.webapp-divider::before,
|
||||||
|
.webapp-divider::after {
|
||||||
|
content: "";
|
||||||
|
height: 1px;
|
||||||
|
width: 80px;
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.webapp-cta { margin-top: 18px; }
|
||||||
|
|
||||||
.version-row {
|
.version-row {
|
||||||
margin-top: 38px;
|
margin-top: 38px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user