Skip to content

Commit ebcfb8c

Browse files
authored
Merge pull request #130 from eurosky-social/eurosky/fork
deploy
2 parents ae3abad + 9085d76 commit ebcfb8c

66 files changed

Lines changed: 2861 additions & 645 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,39 @@ EXPO_PUBLIC_ENABLE_APP_CONFIG=false
8383
# Disabled for Eurosky; GrowthBook feature-flag fetching is unaffected, so we
8484
# still pick up upstream's gated features when syncing.
8585
EXPO_PUBLIC_ENABLE_METRICS=false
86+
87+
#
88+
#
89+
# Live sports widget (Discover tab). See src/features/liveSports/README.md.
90+
#
91+
#
92+
93+
# Data source: football-data.org (EU-based; free tier covers the World Cup).
94+
# Get a free token at https://www.football-data.org/client/register
95+
#
96+
# Production: leave the token empty and point the API URL at the first-party
97+
# proxy (services/footballData/), which holds the token server-side. The web
98+
# deploy already sets this to the mu.social proxy; the proxy's own server-side
99+
# vars (FOOTBALLDATA_TOKEN etc.) are documented in services/footballData/README.md,
100+
# not here - they are not EXPO_PUBLIC app vars.
101+
# EXPO_PUBLIC_FOOTBALLDATA_API_URL=https://sports.mu.social/v4
102+
# Local/dev: set the token here to call football-data.org directly (no proxy).
103+
104+
# football-data.org API token. Direct mode only - the token is sent from the
105+
# client, so leave this empty in production and use the proxy instead.
106+
EXPO_PUBLIC_FOOTBALLDATA_TOKEN=
107+
108+
# Base URL for football-data.org, or a first-party proxy in front of it. Set
109+
# this to the proxy host in production. Defaults to https://api.football-data.org/v4.
110+
EXPO_PUBLIC_FOOTBALLDATA_API_URL=
111+
112+
# Comma-separated football-data.org competition codes. Defaults to "WC" (World
113+
# Cup). e.g. "WC,CL,PL".
114+
EXPO_PUBLIC_SPORTS_COMPETITION_CODES=
115+
116+
# Optional module title override. Unset uses a localized default ("Football").
117+
EXPO_PUBLIC_SPORTS_TITLE=
118+
119+
# Kill switch. The widget auto-hides when no token/proxy is configured; set to
120+
# 'false' to force it off even when one is.
121+
EXPO_PUBLIC_ENABLE_SPORTS=

.github/workflows/deploy-web-bunny.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ jobs:
5959
# First-party Bunny proxy for Plausible (see services/plausible/). Needs
6060
# the scheme: the tracker posts to ${API_HOST}/api/event.
6161
EXPO_PUBLIC_PLAUSIBLE_API_HOST: https://events.mu.social
62+
# First-party Bunny proxy for the live sports widget (see
63+
# services/footballData/). Holds the football-data.org token
64+
# server-side; the app sends none. Renders null until the worker is
65+
# deployed, so it is safe to set ahead of time.
66+
EXPO_PUBLIC_FOOTBALLDATA_API_URL: https://sports.mu.social/v4
6267
- name: Upload to Bunny + purge cache
6368
run: bash ./scripts/bunny_upload.sh
6469
env:

.github/workflows/deploy-web-staging-bunny.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ jobs:
7777
# First-party Bunny proxy for Plausible (see services/plausible/). Needs
7878
# the scheme: the tracker posts to ${API_HOST}/api/event.
7979
EXPO_PUBLIC_PLAUSIBLE_API_HOST: https://events.mu.social
80+
# Shares the prod live-sports proxy (see services/footballData/); the
81+
# endpoint is read-only and cached, so staging can reuse it.
82+
EXPO_PUBLIC_FOOTBALLDATA_API_URL: https://sports.mu.social/v4
8083
- name: Upload to Bunny + purge cache
8184
run: bash ./scripts/bunny_upload.sh
8285
env:

app.config.js

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
// @ts-check
22
const pkg = require('./package.json')
33
/**
4-
* Brand colours, shared with the in-app theme (src/config/brand-theme.ts) and
5-
* the web pre-boot codegen (scripts/sync-brand-web.mjs). Native chrome renders
6-
* before any per-user accent preference is known, so it always uses the org's
7-
* shipped default accent.
8-
* @type {{accents: Record<string, Record<string, string>>, defaultAccent: string}}
4+
* Single brand config (src/config/brand.json; see brand.schema.json). Shared
5+
* with the in-app adapters, the web codegen and the Workers. Native chrome
6+
* renders before any per-user accent is known, so it uses the shipped default
7+
* accent.
8+
* @type {{name: string, hosts: string[], socialHandle: string, colors: {accents: Record<string, Record<string, string>>, defaultAccent: string}}}
99
*/
10-
const brandColors = require('./src/config/brand-colors.json')
11-
const BRAND_ACCENT = brandColors.accents[brandColors.defaultAccent]
12-
/**
13-
* Brand text identity, shared with brand.ts (BRAND.name / web.hosts) and the
14-
* web codegen, so the display name / host never drift across runtimes.
15-
* @type {{name: string, hosts: string[], socialHandle: string}}
16-
*/
17-
const brandMeta = require('./src/config/brand-meta.json')
10+
const brand = require('./src/config/brand.json')
11+
const brandMeta = brand
12+
const BRAND_ACCENT = brand.colors.accents[brand.colors.defaultAccent]
1813

1914
/**
2015
* @param {import('@expo/config-types').ExpoConfig} _config
@@ -246,7 +241,7 @@ module.exports = function (_config) {
246241
// dark browser themes. Standard Expo mechanism, same as upstream, so
247242
// no custom <link> wiring and minimal merge surface.
248243
favicon: './assets/favicon.png',
249-
// Brand default-accent primary_500 (from src/config/brand-colors.json).
244+
// Brand default-accent primary_500 (from src/config/brand.json).
250245
// Drives the PWA manifest theme_color and the <meta name="theme-color">,
251246
// i.e. the Chrome Custom Tab toolbar when opening links from the
252247
// installed PWA on Android. Web-only override; native keeps primaryColor

assets/brand/hero.svg

Lines changed: 7 additions & 0 deletions
Loading

assets/brand/mark.svg

Lines changed: 3 additions & 0 deletions
Loading

docs/rebranding.md

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,112 @@
11
# Rebranding / white-labeling
22

3-
Brand identity is data + two scripts. The visual + text rebrand happy path:
3+
Brand identity is **one JSON file + a folder of SVGs + one command**. The visual
4+
+ text rebrand happy path:
45

5-
## 1. Edit the config (`src/config/`)
6+
## 1. Edit `src/config/brand.json`
67

7-
- **`brand-meta.json`**`name`, `hosts` (your web domains), `socialHandle`.
8-
- **`brand-colors.json`**`neutral` ramp, `accents` (the selectable families), `defaultAccent`.
9-
- **`brand-logo.json`**`flat` wordmark (required). Set `dimensional: null` if you have no 3D logo; hero surfaces fall back to the flat wordmark in the accent.
10-
- **`brand.ts`**`links` (legal/support/download URLs).
8+
The single source of truth, editor-validated by `brand.schema.json` (your IDE
9+
flags missing keys, bad hex values, an unknown `defaultAccent`, etc.). Sections:
1110

12-
## 2. Drop in the app icon
11+
- **identity**`name`, `hosts` (your web domains; `hosts[0]` is primary),
12+
`socialHandle` (X/Twitter @, empty to omit).
13+
- **`colors`**`neutral` (the contrast_ ramp), `accents` (the selectable
14+
primary_ families), `defaultAccent` (the one this build ships).
15+
- **`oauth`**`defaultBaseUrl` (your domain), `clientName`, `scope` /
16+
`declaredScope`, `handleResolver`, `signupPdsHost`.
17+
- **`links`** — legal / support / download URLs (point them at your own pages).
18+
- **`services`** — atproto endpoints; default to the public Bluesky network,
19+
override only what you self-host.
20+
- **`verification`**, **`ageAssurance`** — trust roots / age backend.
1321

14-
Replace `assets/app-icons/ios_icon_default_next.png` with your 1024×1024 square tile.
22+
Build-time `EXPO_PUBLIC_*` env vars override a few of these per deployment (see
23+
`.env.example`); keep those in the env, not the JSON.
24+
25+
## 2. Drop in your logos (`assets/brand/`)
26+
27+
One SVG per role. Only `mark.svg` is **required**; the rest are optional and fall
28+
back to it, so a single-glyph brand and a symbol+wordmark brand both work:
29+
30+
| file | role |
31+
| ------------- | ----------------------------------------------- |
32+
| `mark.svg` | the symbol/glyph (required; fallback for all) |
33+
| `wordmark.svg`| the styled brand name |
34+
| `lockup.svg` | symbol + word together |
35+
| `hero.svg` | large / marketing (a multi-tone "3D" logo lives here) |
36+
| `icon.svg` | app-icon-specific art (else the mark on a tile) |
37+
38+
**Theming convention** so one SVG follows light/dark + the accent picker:
39+
40+
- `fill="currentColor"` → the primary tint (driven by the component).
41+
- `fill="theme:<paletteKey>"` (e.g. `theme:primary_900`) → substituted from the
42+
active palette at render. This is how a two-tone `hero.svg` follows the accent;
43+
there is no special "dimensional" data shape any more.
44+
45+
Plain colours (hex/rgb) render as-authored (for genuinely multi-colour logos).
1546

1647
## 3. Run the generators
1748

1849
```bash
19-
pnpm brand:gen-favicons # favicons + safari mask, from the icon tile
20-
pnpm brand:sync-web # writes colors/splash/og/title into web/index.html + bskyweb/templates/base.html
21-
pnpm brand:check-web # CI guard: non-zero if the above is out of sync
50+
pnpm brand # gen-logo + sync-web + favicons - the full visual rebrand
51+
pnpm brand:check # CI guard: non-zero if the generated artifacts are stale
2252
```
2353

24-
That's the full visual + text rebrand. `BRAND.name` propagates through the UI automatically; the upstream "Bluesky" → name swap lives in `src/config/brandStrings.ts` (add message IDs there if any slip through).
54+
Under the hood (run individually if you like):
55+
56+
- `pnpm brand:gen-logo``assets/brand/*.svg``src/config/brand-logo.generated.json`
57+
(committed; consumed by `<BrandLogo>`, the splash, and the favicon mask).
58+
- `pnpm brand:sync-web` — writes the colours / splash / og / title into the
59+
pre-boot HTML (`web/index.html`, `bskyweb/templates/base.html`).
60+
- `pnpm brand:gen-favicons` — favicons + Safari mask from the icon master PNG.
61+
62+
That's the full visual + text rebrand. `BRAND.name` propagates through the UI
63+
automatically; the upstream "Bluesky" → name swap lives in
64+
`src/config/brandStrings.ts` (add message IDs there if any slip through).
65+
66+
## App icon + OG image (optional `gen-raster`)
67+
68+
Favicons are generated from the icon master at
69+
`assets/app-icons/ios_icon_default_next.png` — replace it with your 1024×1024
70+
square tile, or generate it from SVG:
71+
72+
```bash
73+
pnpm brand:gen-raster # from assets/brand/{icon,og}.svg if present
74+
pnpm brand:gen-raster --compose # else compose defaults from your mark
75+
```
76+
77+
`gen-raster` is **optional and not a project dependency**. It auto-detects a
78+
rasterizer — `@resvg/resvg-js` (install it yourself, never committed) →
79+
`rsvg-convert` (`brew install librsvg`) → `magick` (ImageMagick) — and skips with
80+
a message if none is present. It is **non-destructive**: it only writes an output
81+
when its SVG source exists (`icon.svg` → icon master + favicons; `og.svg`
82+
`web/og-image.jpg`), or composes a default under `--compose`. A brand with
83+
hand-designed raster art ships no source SVGs and is never touched.
84+
85+
## OAuth (per deployment)
86+
87+
You run your own OAuth confidential client (`services/oauth`); the `client_id` is
88+
your domain. Generate a signing keypair once (do **not** commit the private half):
89+
90+
```bash
91+
node scripts/gen-oauth-keypair.js # writes the public JWKS (commit it);
92+
# prints the private JWK -> Worker secret
93+
```
94+
95+
Deploy with `scripts/deploy-cloudflare.sh` (see `services/oauth/` and
96+
`services/web/`). Note: the OAuth assertion host must be a **1-level subdomain**
97+
(`oauth-brand.example.com`) **or** added as a Worker **Custom Domain** — a
98+
2-level subdomain (`oauth.brand.example.com`) is not covered by Cloudflare's
99+
Universal SSL and the TLS handshake will fail.
25100

26101
## Beyond the basics
27102

28-
- **Services** — edge services live in `services/` (Bunny scripts). Network read APIs default to Bluesky's (until eurosky appview is available). You **must** run your own **OAuth** (`services/oauth` — the client_id is your domain); optionally your own OG / analytics / geolocation / age. Point the app at them via `EXPO_PUBLIC_*` env vars (see `.env.example`). Bluesky's GrowthBook flags + product metrics are toggled by the `EXPO_PUBLIC_ENABLE_*` flags.
29-
- **Native build** (separate pass) — bundle IDs, app name, scheme, app groups, associated domains, EAS / App Store / Sentry / Firebase IDs: `app.config.js`, `eas.json`, `ios/`, `android/`.
30-
- **Content** (optional, still eurosky-specific) — onboarding packs, suggested follows, default feeds, news sources, verifier DIDs: `src/screens/Onboarding/`, `src/features/newsFeed/sources.ts`, `src/lib/constants.ts`.
103+
- **Services** — edge services live in `services/`. Read APIs default to
104+
Bluesky's; override per build via `EXPO_PUBLIC_*` (see `.env.example`).
105+
GrowthBook flags + product metrics toggle via `EXPO_PUBLIC_ENABLE_*`.
106+
- **Native build** (separate pass) — bundle IDs, app name, scheme, app groups,
107+
associated domains, EAS / App Store / Sentry / Firebase IDs live in
108+
`app.config.js`, `eas.json`, `ios/`, `android/`.
109+
- **Content** (optional) — onboarding packs, suggested follows, default feeds,
110+
news sources, verifier DIDs: `src/screens/Onboarding/`,
111+
`src/features/newsFeed/sources.ts`, `src/lib/constants.ts`.
112+
```

eslint.config.mjs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ export default defineConfig(
3737
'bskyembed/**',
3838
'bskyogcard/**',
3939
// Eurosky fork: first-party edge services (OAuth client-assertion,
40-
// Plausible proxy, IP geolocation, per-route Open Graph injector) live
41-
// under services/. They are Bunny/Cloudflare Edge Scripts (Deno runtime +
42-
// URL imports), not app code, so the whole tree is excluded.
40+
// Plausible proxy, IP geolocation, per-route Open Graph injector,
41+
// football-data.org proxy for the live sports widget) live under
42+
// services/. They are Bunny/Cloudflare Edge Scripts (Deno runtime + URL
43+
// imports), not app code, so the whole tree is excluded.
4344
'services/**',
4445
'src/locale/locales/_build/**',
4546
'src/locale/locales/**/*.js',

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,14 @@
6464
"lint-native": "swiftlint ./modules && ktlint ./modules",
6565
"lint-native:fix": "swiftlint --fix ./modules && ktlint --format ./modules",
6666
"typecheck": "tsgo --project ./tsconfig.check.json",
67+
"brand:gen-logo": "node scripts/gen-logo.mjs",
68+
"brand:check-logo": "node scripts/gen-logo.mjs --check",
6769
"brand:sync-web": "node scripts/sync-brand-web.mjs",
6870
"brand:check-web": "node scripts/sync-brand-web.mjs --check",
6971
"brand:gen-favicons": "node scripts/gen-brand-favicons.mjs",
72+
"brand:gen-raster": "node scripts/gen-raster.mjs",
73+
"brand": "node scripts/gen-logo.mjs && node scripts/sync-brand-web.mjs && node scripts/gen-brand-favicons.mjs",
74+
"brand:check": "node scripts/gen-logo.mjs --check && node scripts/sync-brand-web.mjs --check",
7075
"e2e:mock-server": "cd dev-env && pnpm start",
7176
"e2e:build": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios",
7277
"e2e:build-android": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:android",

scripts/deploy-cloudflare.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# (matching the Bunny edge feature set). It is distinct from scripts/bunny_build.sh,
99
# the pre-existing Cloudflare *Pages* build (static only, no OG/geolocation).
1010
#
11-
# Brand name + host come from src/config/brand-meta.json (the same source the
11+
# Brand name + host come from src/config/brand.json (the same source the
1212
# app and Workers read). This script only wires the deployment-specific bits:
1313
# build env, Worker names, and the OAuth assertion URL.
1414
#
@@ -45,8 +45,8 @@ done
4545

4646
# Brand identity from the single source. Slugify the display name for the CF
4747
# Worker resource names (lowercase, alphanumeric + hyphens).
48-
BRAND_SLUG="$(node -p 'require("./src/config/brand-meta.json").name.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")')"
49-
PRIMARY_HOST="$(node -p 'require("./src/config/brand-meta.json").hosts[0]')"
48+
BRAND_SLUG="$(node -p 'require("./src/config/brand.json").name.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")')"
49+
PRIMARY_HOST="$(node -p 'require("./src/config/brand.json").hosts[0]')"
5050
SITE_URL="${SITE_URL:-https://${PRIMARY_HOST}}"
5151
WEB_WORKER="${BRAND_SLUG}-web"
5252
OAUTH_WORKER="${BRAND_SLUG}-oauth-assertion"

0 commit comments

Comments
 (0)