@ramonclaudio/create-vexpo 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/index.js +8 -7
- package/dist/templates/default/.eas/workflows/asc-events.yml +9 -6
- package/dist/templates/default/.eas/workflows/deploy-production.yml +28 -15
- package/dist/templates/default/.eas/workflows/e2e-tests.yml +3 -2
- package/dist/templates/default/.eas/workflows/pr-preview.yml +12 -21
- package/dist/templates/default/.eas/workflows/release.yml +3 -7
- package/dist/templates/default/.eas/workflows/rollback.yml +54 -28
- package/dist/templates/default/.eas/workflows/rollout.yml +27 -33
- package/dist/templates/default/.eas/workflows/rotate-apple-jwt.yml +1 -5
- package/dist/templates/default/.eas/workflows/testflight.yml +3 -7
- package/dist/templates/default/.github/workflows/check.yml +20 -12
- package/dist/templates/default/.maestro/launch.yaml +19 -10
- package/dist/templates/default/AGENTS.md +25 -8
- package/dist/templates/default/DESIGN.md +14 -10
- package/dist/templates/default/README.md +83 -78
- package/dist/templates/default/SETUP.md +159 -152
- package/dist/templates/default/__tests__/convex/_auth-harness.test.ts +112 -0
- package/dist/templates/default/__tests__/convex/_harness.ts +132 -0
- package/dist/templates/default/__tests__/convex/appAttest.test.ts +172 -0
- package/dist/templates/default/__tests__/convex/appAttestStore.test.ts +48 -0
- package/dist/templates/default/__tests__/convex/pushTokens-remove.test.ts +106 -0
- package/dist/templates/default/__tests__/convex/pushTokens-upsert.test.ts +146 -0
- package/dist/templates/default/__tests__/convex/users-deleteAccount.test.ts +140 -0
- package/dist/templates/default/__tests__/convex/users-deleteAvatar.test.ts +104 -0
- package/dist/templates/default/__tests__/convex/users-getMe.test.ts +98 -0
- package/dist/templates/default/__tests__/convex/users-getUser.test.ts +120 -0
- package/dist/templates/default/__tests__/convex/users-hardDeleteExpired.test.ts +67 -0
- package/dist/templates/default/__tests__/convex/users-restoreAccount.test.ts +96 -0
- package/dist/templates/default/__tests__/convex/users-updateAvatar.test.ts +92 -0
- package/dist/templates/default/__tests__/convex/users-updateProfile.test.ts +126 -0
- package/dist/templates/default/__tests__/convex/webhook.test.ts +31 -0
- package/dist/templates/default/__tests__/lib/deep-link.test.ts +51 -6
- package/dist/templates/default/__tests__/lib/schemas.test.ts +205 -0
- package/dist/templates/default/__tests__/lib/text-style.test.ts +31 -0
- package/dist/templates/default/_env.example +7 -7
- package/dist/templates/default/_gitattributes +1 -1
- package/dist/templates/default/_gitignore +17 -2
- package/dist/templates/default/_npmrc +7 -0
- package/dist/templates/default/_oxlintrc.json +1 -1
- package/dist/templates/default/app-store/accessibility.config.json +20 -0
- package/dist/templates/default/app-store/privacy.config.json +27 -0
- package/dist/templates/default/app.config.ts +105 -33
- package/dist/templates/default/app.json +1 -9
- package/dist/templates/default/convex/_generated/api.d.ts +12 -0
- package/dist/templates/default/convex/admin.ts +0 -13
- package/dist/templates/default/convex/appAttest.ts +467 -0
- package/dist/templates/default/convex/appAttestStore.ts +141 -0
- package/dist/templates/default/convex/apple.ts +53 -0
- package/dist/templates/default/convex/auth.ts +6 -45
- package/dist/templates/default/convex/constants.ts +2 -7
- package/dist/templates/default/convex/crons.ts +12 -5
- package/dist/templates/default/convex/email.ts +4 -24
- package/dist/templates/default/convex/env.ts +0 -4
- package/dist/templates/default/convex/errors.ts +0 -7
- package/dist/templates/default/convex/functions.ts +0 -26
- package/dist/templates/default/convex/http.ts +3 -5
- package/dist/templates/default/convex/log.ts +2 -25
- package/dist/templates/default/convex/pushSender.ts +145 -0
- package/dist/templates/default/convex/pushTokens.ts +110 -13
- package/dist/templates/default/convex/rateLimit.ts +8 -39
- package/dist/templates/default/convex/schema.ts +48 -5
- package/dist/templates/default/convex/tsconfig.json +1 -0
- package/dist/templates/default/convex/users.ts +143 -61
- package/dist/templates/default/convex/validators.ts +1 -38
- package/dist/templates/default/convex/webhook.ts +1 -31
- package/dist/templates/default/convex.json +1 -2
- package/dist/templates/default/metro.config.js +9 -1
- package/dist/templates/default/package.json +67 -70
- package/dist/templates/default/plugins/README.md +5 -1
- package/dist/templates/default/scripts/README.md +9 -9
- package/dist/templates/default/scripts/_run.mjs +3 -20
- package/dist/templates/default/scripts/clean.ts +81 -69
- package/dist/templates/default/scripts/gen-update-cert.mjs +98 -0
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/(home)/index.tsx +21 -6
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/(home,search)/_layout.tsx +9 -8
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/(search)/index.tsx +26 -24
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/_layout.tsx +3 -4
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/_layout.tsx +10 -6
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/index.tsx +81 -51
- package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/preferences.tsx +72 -12
- package/dist/templates/default/src/app/(app)/_layout.tsx +147 -0
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/_layout.tsx +4 -5
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/forgot-password.tsx +15 -9
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/reset-password.tsx +88 -14
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/sign-in.tsx +65 -35
- package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/sign-up.tsx +131 -196
- package/dist/templates/default/src/app/(app)/debug.tsx +479 -0
- package/dist/templates/default/{app → src/app}/(app)/help.tsx +76 -64
- package/dist/templates/default/{app → src/app}/(app)/linked.tsx +21 -27
- package/dist/templates/default/{app → src/app}/(app)/privacy.tsx +35 -8
- package/dist/templates/default/src/app/(app)/profile/change-password.tsx +264 -0
- package/dist/templates/default/{app/(app)/profile.tsx → src/app/(app)/profile/index.tsx} +179 -255
- package/dist/templates/default/src/app/(app)/restore-account.tsx +192 -0
- package/dist/templates/default/src/app/(app)/sessions.tsx +287 -0
- package/dist/templates/default/src/app/(app)/welcome.tsx +194 -0
- package/dist/templates/default/src/app/+native-intent.tsx +25 -0
- package/dist/templates/default/src/app/+not-found.tsx +43 -0
- package/dist/templates/default/{app → src/app}/_layout.tsx +28 -37
- package/dist/templates/default/src/components/auth/apple-button.tsx +51 -0
- package/dist/templates/default/{components → src/components}/auth/otp-verification.tsx +79 -58
- package/dist/templates/default/{components → src/components}/auth/password-field.tsx +74 -18
- package/dist/templates/default/src/components/auth/segmented-toggle.tsx +71 -0
- package/dist/templates/default/src/components/ui/content-unavailable.tsx +81 -0
- package/dist/templates/default/src/components/ui/convex-error.tsx +21 -0
- package/dist/templates/default/src/components/ui/error-boundary.tsx +89 -0
- package/dist/templates/default/{components → src/components}/ui/loading-screen.tsx +5 -4
- package/dist/templates/default/{components → src/components}/ui/material.tsx +50 -17
- package/dist/templates/default/src/components/ui/offline-banner.tsx +59 -0
- package/dist/templates/default/{components → src/components}/ui/prominent-button.tsx +8 -11
- package/dist/templates/default/{components → src/components}/ui/skeleton.tsx +31 -13
- package/dist/templates/default/src/components/ui/status-text.tsx +64 -0
- package/dist/templates/default/src/components/ui/update-banner.tsx +85 -0
- package/dist/templates/default/{constants → src/constants}/layout.ts +0 -6
- package/dist/templates/default/{constants → src/constants}/theme.ts +49 -64
- package/dist/templates/default/{constants → src/constants}/ui.ts +13 -4
- package/dist/templates/default/src/hooks/use-debounce.ts +12 -0
- package/dist/templates/default/src/hooks/use-deep-link.ts +51 -0
- package/dist/templates/default/src/hooks/use-delete-account.ts +35 -0
- package/dist/templates/default/src/hooks/use-motion-screen-options.ts +13 -0
- package/dist/templates/default/src/hooks/use-network.ts +34 -0
- package/dist/templates/default/{hooks → src/hooks}/use-notifications.ts +39 -30
- package/dist/templates/default/src/hooks/use-reduce-transparency.ts +30 -0
- package/dist/templates/default/{hooks → src/hooks}/use-theme.ts +0 -5
- package/dist/templates/default/src/lib/appAttest.ts +78 -0
- package/dist/templates/default/src/lib/assets.ts +9 -0
- package/dist/templates/default/src/lib/deep-link.ts +82 -0
- package/dist/templates/default/{lib → src/lib}/dev-menu.ts +0 -4
- package/dist/templates/default/{lib → src/lib}/device.ts +1 -13
- package/dist/templates/default/{lib → src/lib}/dynamic-font.ts +13 -10
- package/dist/templates/default/src/lib/dynamic-symbol-size.ts +33 -0
- package/dist/templates/default/src/lib/masks.ts +21 -0
- package/dist/templates/default/src/lib/native-state.ts +20 -0
- package/dist/templates/default/{lib → src/lib}/notifications.ts +7 -45
- package/dist/templates/default/{lib → src/lib}/preferences.ts +0 -2
- package/dist/templates/default/{lib → src/lib}/schemas.ts +19 -16
- package/dist/templates/default/src/lib/text-style.ts +20 -0
- package/dist/templates/default/{lib → src/lib}/updates.ts +0 -7
- package/dist/templates/default/store.config.json +1 -1
- package/dist/templates/default/tsconfig.json +3 -1
- package/dist/templates/default/vitest.config.ts +8 -1
- package/package.json +5 -5
- package/dist/templates/default/app/(app)/_layout.tsx +0 -73
- package/dist/templates/default/app/(app)/debug.tsx +0 -389
- package/dist/templates/default/app/(app)/sessions.tsx +0 -191
- package/dist/templates/default/app/(app)/welcome.tsx +0 -140
- package/dist/templates/default/app/+native-intent.tsx +0 -14
- package/dist/templates/default/app/+not-found.tsx +0 -51
- package/dist/templates/default/bun.lock +0 -1860
- package/dist/templates/default/components/auth/segmented-toggle.tsx +0 -47
- package/dist/templates/default/components/ui/convex-error.tsx +0 -32
- package/dist/templates/default/components/ui/error-boundary.tsx +0 -57
- package/dist/templates/default/components/ui/offline-banner.tsx +0 -58
- package/dist/templates/default/components/ui/status-text.tsx +0 -49
- package/dist/templates/default/components/ui/update-banner.tsx +0 -82
- package/dist/templates/default/fingerprint.config.js +0 -9
- package/dist/templates/default/hooks/use-debounce.ts +0 -20
- package/dist/templates/default/hooks/use-deep-link.ts +0 -43
- package/dist/templates/default/hooks/use-network.ts +0 -11
- package/dist/templates/default/lib/assets.ts +0 -17
- package/dist/templates/default/lib/deep-link.ts +0 -71
- package/dist/templates/default/patches/PR-368.patch +0 -91
- package/dist/templates/default/patches/convex-dev-better-auth-0.12.2.tgz +0 -0
- /package/dist/templates/default/{hooks → src/hooks}/use-navigation-tracking.ts +0 -0
- /package/dist/templates/default/{hooks → src/hooks}/use-onboarding.ts +0 -0
- /package/dist/templates/default/{hooks → src/hooks}/use-reduced-motion.ts +0 -0
- /package/dist/templates/default/{hooks → src/hooks}/use-updates.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/a11y.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/app.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/auth-client.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/convex-auth.tsx +0 -0
- /package/dist/templates/default/{lib → src/lib}/env.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/haptics.ts +0 -0
- /package/dist/templates/default/{lib → src/lib}/storage.ts +0 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Scaffold a new [vexpo](https://github.com/ramonclaudio/vexpo) project: Expo SDK
|
|
|
10
10
|
```bash
|
|
11
11
|
npm create @ramonclaudio/vexpo@latest my-app
|
|
12
12
|
# or
|
|
13
|
-
|
|
13
|
+
npx @ramonclaudio/create-vexpo@latest my-app
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
After scaffold:
|
|
@@ -18,15 +18,15 @@ After scaffold:
|
|
|
18
18
|
```bash
|
|
19
19
|
cd my-app
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
npx vexpo lite # 60 seconds: Convex + Better Auth, simulator-ready
|
|
22
|
+
npx vexpo lite --new # same + Convex signup walkthrough for first-time users
|
|
23
|
+
npx vexpo full # full provisioning: TestFlight-ready (Convex, Better Auth, Resend, Apple Sign In, EAS, rebrand)
|
|
24
|
+
npx vexpo full --new # same + Apple/Convex/Expo/Resend signup walkthrough
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
`
|
|
27
|
+
`npx vexpo lite` is the dev-mode shortcut. No Apple Developer account, no domain, no EAS, no Resend. Boots in the iOS Simulator in ~60 seconds. Add `--new` if you don't have a Convex account yet.
|
|
28
28
|
|
|
29
|
-
`
|
|
29
|
+
`npx vexpo full` walks Apple Developer / Expo / Convex / Resend signups (with `--new`), validates each, provisions everything in order. ~30 minutes hands-on plus Apple-side wait times. Prints the canonical `eas build` command at the end. You run it when ready.
|
|
30
30
|
|
|
31
31
|
## Options
|
|
32
32
|
|
|
@@ -35,15 +35,15 @@ bunx vexpo full --new # same + Apple/Convex/Expo/Resend signup walkthrough
|
|
|
35
35
|
| `[directory]` | Project directory name (positional). Defaults to `my-vexpo-app` with `-y`, otherwise prompts. |
|
|
36
36
|
| `--no-install` | Skip running `<pm> install` after copying the template. |
|
|
37
37
|
| `--no-git` | Skip `git init` after install. |
|
|
38
|
-
| `--no-setup` | Skip the post-install `
|
|
38
|
+
| `--no-setup` | Skip the post-install `npx vexpo lite` / `npx vexpo full` prompt. |
|
|
39
39
|
| `-y, --yes` | Accept defaults, skip prompts. |
|
|
40
40
|
| `-v, --version` | Print version, exit. |
|
|
41
41
|
|
|
42
42
|
## What gets scaffolded
|
|
43
43
|
|
|
44
|
-
The CLI copies `templates/default/` from the published tarball, restores npm-stripped dotfiles (`.gitignore`, `.env.example`, etc.), rewrites `package.json` (project name, version, drops monorepo metadata,
|
|
44
|
+
The CLI copies `templates/default/` from the published tarball, restores npm-stripped dotfiles (`.gitignore`, `.env.example`, `.npmrc`, etc.), rewrites `package.json` (project name, version, drops monorepo metadata), installs dependencies via the detected package manager (`npm`, `bun`, `pnpm`, or `yarn`, sniffed from `npm_config_user_agent`; defaults to `npm`), and initializes a fresh git repo with `feat: initial commit`. No lockfile ships in the tarball: the first install resolves the template's ranges fresh (including the latest in-range `vexpo` CLI) and the generated lockfile lands in the initial commit.
|
|
45
45
|
|
|
46
|
-
After that, the project is standalone. The operational CLI (`vexpo`) is installed as a devDependency, so `
|
|
46
|
+
After that, the project is standalone. The operational CLI (`vexpo`) is installed as a devDependency, so `npx vexpo <subcommand>` resolves to the local pinned version. Setup commands aren't in `package.json`. They're one-shot CLI invocations, not runtime scripts.
|
|
47
47
|
|
|
48
48
|
## Repo
|
|
49
49
|
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import prompts from 'prompts';
|
|
|
11
11
|
|
|
12
12
|
// package.json
|
|
13
13
|
var package_default = {
|
|
14
|
-
version: "0.1.
|
|
14
|
+
version: "0.1.1"};
|
|
15
15
|
|
|
16
16
|
// src/index.ts
|
|
17
17
|
var here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -19,7 +19,7 @@ var TEMPLATE_DIR = join(here, "templates", "default");
|
|
|
19
19
|
async function main() {
|
|
20
20
|
const program = new Command().name("create-vexpo").description(
|
|
21
21
|
"Scaffold a new vexpo project. Expo SDK 56 + Convex + Better Auth + Resend, wired for iOS."
|
|
22
|
-
).argument("[directory]", "project directory name").option("--no-install", "skip installing dependencies").option("--no-git", "skip git init").option("--no-setup", "skip the `
|
|
22
|
+
).argument("[directory]", "project directory name").option("--no-install", "skip installing dependencies").option("--no-git", "skip git init").option("--no-setup", "skip the `npx vexpo lite` / `npx vexpo full` prompt after install").option("-y, --yes", "accept defaults, skip prompts").version(package_default.version, "-v, --version").parse();
|
|
23
23
|
const flags = program.opts();
|
|
24
24
|
const argDir = program.args[0];
|
|
25
25
|
intro();
|
|
@@ -66,7 +66,7 @@ Target ${target} already exists. Pick a different name.`));
|
|
|
66
66
|
gitSpin.warn("Git init skipped");
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
nextSteps(target, flags, pm);
|
|
69
|
+
if (flags.setup) nextSteps(target, flags, pm);
|
|
70
70
|
}
|
|
71
71
|
var NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
72
72
|
var NAME_HINT = "lowercase letters, numbers, dashes; must start alphanumeric";
|
|
@@ -114,7 +114,8 @@ async function restoreStrippedDotfiles(target) {
|
|
|
114
114
|
["_gitattributes", ".gitattributes"],
|
|
115
115
|
["_easignore", ".easignore"],
|
|
116
116
|
["_fingerprintignore", ".fingerprintignore"],
|
|
117
|
-
["_env.convex.local", ".env.convex.local"]
|
|
117
|
+
["_env.convex.local", ".env.convex.local"],
|
|
118
|
+
["_npmrc", ".npmrc"]
|
|
118
119
|
];
|
|
119
120
|
for (const [from, to] of renames) {
|
|
120
121
|
const src = join(target, from);
|
|
@@ -147,7 +148,7 @@ function detectPackageManager() {
|
|
|
147
148
|
if (ua.startsWith("pnpm")) return "pnpm";
|
|
148
149
|
if (ua.startsWith("yarn")) return "yarn";
|
|
149
150
|
if (ua.startsWith("npm")) return "npm";
|
|
150
|
-
return "
|
|
151
|
+
return "npm";
|
|
151
152
|
}
|
|
152
153
|
function intro() {
|
|
153
154
|
console.log();
|
|
@@ -161,12 +162,12 @@ function nextSteps(target, flags, pm) {
|
|
|
161
162
|
if (!flags.install) console.log(kleur.gray(` ${pm} install`));
|
|
162
163
|
console.log(
|
|
163
164
|
kleur.gray(
|
|
164
|
-
`
|
|
165
|
+
` npx vexpo lite ${kleur.dim("# dev mode: Convex + Better Auth, 60s to simulator")}`
|
|
165
166
|
)
|
|
166
167
|
);
|
|
167
168
|
console.log(
|
|
168
169
|
kleur.gray(
|
|
169
|
-
`
|
|
170
|
+
` npx vexpo full ${kleur.dim("# real setup: TestFlight-ready (add --new if you're new)")}`
|
|
170
171
|
)
|
|
171
172
|
);
|
|
172
173
|
console.log();
|
|
@@ -41,13 +41,16 @@ jobs:
|
|
|
41
41
|
# link straight to App Store Connect rather than interpolating fields that
|
|
42
42
|
# may not resolve.
|
|
43
43
|
|
|
44
|
+
# All four notify_* jobs ship disabled. Paste your Slack incoming-webhook URL
|
|
45
|
+
# from https://api.slack.com/messaging/webhooks into the `webhook_url:` field,
|
|
46
|
+
# then drop `false &&` from the `if:` guard to enable. EAS workflows do not
|
|
47
|
+
# yet support interpolating `${{ env.* }}` into `webhook_url`.
|
|
48
|
+
|
|
44
49
|
notify_review_state:
|
|
45
50
|
name: Slack notify on review state change
|
|
46
|
-
if: ${{ app_store_connect.app_version }}
|
|
51
|
+
if: ${{ false && app_store_connect.app_version }}
|
|
47
52
|
type: slack
|
|
48
53
|
params:
|
|
49
|
-
# `webhook_url` only accepts hardcoded strings today; EAS env-stored
|
|
50
|
-
# interpolation for this field is upcoming. Paste your URL below.
|
|
51
54
|
webhook_url: https://hooks.slack.com/services/REPLACE/ME/HERE
|
|
52
55
|
message: |
|
|
53
56
|
App Store version state changed. App Store Connect → My Apps → App Store tab.
|
|
@@ -55,7 +58,7 @@ jobs:
|
|
|
55
58
|
|
|
56
59
|
notify_upload_state:
|
|
57
60
|
name: Slack notify on build upload
|
|
58
|
-
if: ${{ app_store_connect.build_upload }}
|
|
61
|
+
if: ${{ false && app_store_connect.build_upload }}
|
|
59
62
|
type: slack
|
|
60
63
|
params:
|
|
61
64
|
webhook_url: https://hooks.slack.com/services/REPLACE/ME/HERE
|
|
@@ -65,7 +68,7 @@ jobs:
|
|
|
65
68
|
|
|
66
69
|
notify_beta_state:
|
|
67
70
|
name: Slack notify on external beta state
|
|
68
|
-
if: ${{ app_store_connect.external_beta }}
|
|
71
|
+
if: ${{ false && app_store_connect.external_beta }}
|
|
69
72
|
type: slack
|
|
70
73
|
params:
|
|
71
74
|
webhook_url: https://hooks.slack.com/services/REPLACE/ME/HERE
|
|
@@ -75,7 +78,7 @@ jobs:
|
|
|
75
78
|
|
|
76
79
|
notify_crash:
|
|
77
80
|
name: Slack notify on TestFlight crash
|
|
78
|
-
if: ${{ app_store_connect.beta_feedback }}
|
|
81
|
+
if: ${{ false && app_store_connect.beta_feedback }}
|
|
79
82
|
type: slack
|
|
80
83
|
params:
|
|
81
84
|
webhook_url: https://hooks.slack.com/services/REPLACE/ME/HERE
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
name: Deploy to production
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
push:
|
|
5
|
-
|
|
4
|
+
# Manual trigger only. A push:main trigger here deploys Convex prod, builds
|
|
5
|
+
# iOS, and submits to the App Store on every merge, including docs/CI commits.
|
|
6
|
+
# Ship intentionally: `eas workflow:run .eas/workflows/deploy-production.yml`.
|
|
7
|
+
# This is the only workflow that runs `convex deploy`; release.yml's `v*` tag
|
|
8
|
+
# builds and submits but does NOT deploy Convex, so deploy here first.
|
|
6
9
|
workflow_dispatch: {}
|
|
7
10
|
|
|
8
11
|
concurrency:
|
|
9
12
|
cancel_in_progress: true
|
|
10
13
|
group: ${{ workflow.filename }}-${{ github.ref }}
|
|
11
14
|
|
|
12
|
-
defaults:
|
|
13
|
-
tools:
|
|
14
|
-
bun: latest
|
|
15
|
-
|
|
16
15
|
jobs:
|
|
17
16
|
precheck:
|
|
18
17
|
name: Precheck
|
|
@@ -22,11 +21,11 @@ jobs:
|
|
|
22
21
|
- uses: eas/checkout
|
|
23
22
|
- uses: eas/install_node_modules
|
|
24
23
|
- name: Typecheck
|
|
25
|
-
run:
|
|
24
|
+
run: npm run typecheck
|
|
26
25
|
- name: Lint
|
|
27
|
-
run:
|
|
26
|
+
run: npm run lint
|
|
28
27
|
- name: Test
|
|
29
|
-
run:
|
|
28
|
+
run: npm run test
|
|
30
29
|
|
|
31
30
|
deploy_convex:
|
|
32
31
|
name: Deploy Convex
|
|
@@ -43,7 +42,7 @@ jobs:
|
|
|
43
42
|
- uses: eas/checkout
|
|
44
43
|
- uses: eas/install_node_modules
|
|
45
44
|
- name: Deploy Convex backend
|
|
46
|
-
run:
|
|
45
|
+
run: npx convex deploy
|
|
47
46
|
|
|
48
47
|
fingerprint:
|
|
49
48
|
name: Fingerprint
|
|
@@ -85,6 +84,17 @@ jobs:
|
|
|
85
84
|
params:
|
|
86
85
|
platform: ios
|
|
87
86
|
branch: production
|
|
87
|
+
# End-to-end code signing for the OTA bundle. `EAS_UPDATE_PRIVATE_KEY`
|
|
88
|
+
# is the matching private key for `templates/default/certs/certificate.pem`,
|
|
89
|
+
# stored as a production file-type secret:
|
|
90
|
+
# eas env:create --environment production --visibility secret \
|
|
91
|
+
# --type file --name EAS_UPDATE_PRIVATE_KEY \
|
|
92
|
+
# --value ../keys/private-key.pem
|
|
93
|
+
# When the env var is unset (no cert generated yet), `eas update`
|
|
94
|
+
# skips signing without erroring. Run `npm run updates:gen-cert` once
|
|
95
|
+
# to wire both halves.
|
|
96
|
+
# https://docs.expo.dev/eas-update/code-signing/
|
|
97
|
+
private_key_path: "$EAS_UPDATE_PRIVATE_KEY"
|
|
88
98
|
|
|
89
99
|
# Post-deploy notification with EAS Insights link. `update_ios` exposes
|
|
90
100
|
# `first_update_group_id` (https://docs.expo.dev/eas/workflows/pre-packaged-jobs/#update-outputs);
|
|
@@ -110,17 +120,20 @@ jobs:
|
|
|
110
120
|
run: |
|
|
111
121
|
GROUP="${{ needs.update_ios.outputs.first_update_group_id }}"
|
|
112
122
|
echo "Update group: $GROUP"
|
|
113
|
-
|
|
123
|
+
npx eas-cli update:insights "$GROUP" --json --non-interactive || true
|
|
114
124
|
|
|
115
125
|
notify_update_published:
|
|
116
126
|
name: Notify Slack with insights link
|
|
117
127
|
needs: [update_ios]
|
|
118
|
-
|
|
128
|
+
# Disabled until you paste a real `webhook_url` below. Drop the `false &&`
|
|
129
|
+
# once configured. EAS workflows do not yet support interpolating
|
|
130
|
+
# `${{ env.* }}` into `webhook_url`, so the URL has to be hardcoded.
|
|
131
|
+
if: ${{ false && needs.update_ios.outputs.first_update_group_id }}
|
|
119
132
|
type: slack
|
|
120
133
|
params:
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
134
|
+
# Paste your Slack incoming-webhook URL from
|
|
135
|
+
# https://api.slack.com/messaging/webhooks. Leaving the placeholder will
|
|
136
|
+
# 404 once you remove the `if: false` gate above.
|
|
124
137
|
webhook_url: https://hooks.slack.com/services/REPLACE/ME/HERE
|
|
125
138
|
message: |
|
|
126
139
|
:rocket: OTA update published to production.
|
|
@@ -12,9 +12,10 @@ name: E2E tests
|
|
|
12
12
|
# environment (populated by `vexpo full` or `eas env:create`). The flow
|
|
13
13
|
# yaml at .maestro/launch.yaml reads it as `appId: ${MAESTRO_APP_ID}`.
|
|
14
14
|
|
|
15
|
+
# Auto-run on PRs is OFF by default to conserve EAS build credits (each run
|
|
16
|
+
# builds an iOS Simulator binary). Trigger manually, or restore the
|
|
17
|
+
# `pull_request` trigger below to run it on every PR.
|
|
15
18
|
on:
|
|
16
|
-
pull_request:
|
|
17
|
-
branches: ["main"]
|
|
18
19
|
workflow_dispatch: {}
|
|
19
20
|
|
|
20
21
|
concurrency:
|
|
@@ -1,36 +1,27 @@
|
|
|
1
1
|
name: PR preview
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
# via the `github-comment` job. Mirrors the canonical EAS workflow pattern at
|
|
3
|
+
# Mirrors the canonical EAS workflow pattern at
|
|
5
4
|
# https://docs.expo.dev/eas/workflows/pre-packaged-jobs/#github-comment.
|
|
6
5
|
#
|
|
7
|
-
# Flow (canonical fingerprint-gated branch model):
|
|
8
|
-
# 1. fingerprint → compute the iOS fingerprint for the PR's dev env.
|
|
9
|
-
# 2. get_existing_build → check if any development:simulator build with
|
|
10
|
-
# that fingerprint already exists (returns build_id if so).
|
|
11
|
-
# 3a. Path A (no compatible build): build a fresh dev-client simulator
|
|
12
|
-
# binary + publish a preview OTA to branch `pr-<n>` so the build
|
|
13
|
-
# picks it up on launch.
|
|
14
|
-
# 3b. Path B (compatible build exists): publish a preview OTA only.
|
|
15
|
-
# 4. github-comment → post the QR / build / update IDs.
|
|
16
|
-
#
|
|
17
6
|
# Either path publishes an update so a) the PR is always testable via a QR
|
|
18
7
|
# scan and b) `pr-<n>` is a real EAS branch from the first commit, not the
|
|
19
8
|
# second. Uses development:simulator (subscribed to the `development`
|
|
20
9
|
# channel). vexpo doesn't ship a separate `preview` build profile.
|
|
21
10
|
|
|
11
|
+
# Auto-build on PRs is OFF by default to conserve EAS build credits: a full
|
|
12
|
+
# iOS build + update on every PR adds up fast. Ships manual-only. To restore
|
|
13
|
+
# automatic previews, swap the trigger back to:
|
|
14
|
+
#
|
|
15
|
+
# on:
|
|
16
|
+
# pull_request:
|
|
17
|
+
# branches: ["main"]
|
|
22
18
|
on:
|
|
23
|
-
|
|
24
|
-
branches: ["main"]
|
|
19
|
+
workflow_dispatch: {}
|
|
25
20
|
|
|
26
21
|
concurrency:
|
|
27
22
|
cancel_in_progress: true
|
|
28
23
|
group: ${{ workflow.filename }}-${{ github.ref }}
|
|
29
24
|
|
|
30
|
-
defaults:
|
|
31
|
-
tools:
|
|
32
|
-
bun: latest
|
|
33
|
-
|
|
34
25
|
jobs:
|
|
35
26
|
precheck:
|
|
36
27
|
name: Precheck
|
|
@@ -40,11 +31,11 @@ jobs:
|
|
|
40
31
|
- uses: eas/checkout
|
|
41
32
|
- uses: eas/install_node_modules
|
|
42
33
|
- name: Typecheck
|
|
43
|
-
run:
|
|
34
|
+
run: npm run typecheck
|
|
44
35
|
- name: Lint
|
|
45
|
-
run:
|
|
36
|
+
run: npm run lint
|
|
46
37
|
- name: Test
|
|
47
|
-
run:
|
|
38
|
+
run: npm run test
|
|
48
39
|
|
|
49
40
|
fingerprint:
|
|
50
41
|
name: Fingerprint
|
|
@@ -8,10 +8,6 @@ concurrency:
|
|
|
8
8
|
cancel_in_progress: true
|
|
9
9
|
group: ${{ workflow.filename }}-${{ github.ref }}
|
|
10
10
|
|
|
11
|
-
defaults:
|
|
12
|
-
tools:
|
|
13
|
-
bun: latest
|
|
14
|
-
|
|
15
11
|
jobs:
|
|
16
12
|
precheck:
|
|
17
13
|
name: Precheck
|
|
@@ -21,11 +17,11 @@ jobs:
|
|
|
21
17
|
- uses: eas/checkout
|
|
22
18
|
- uses: eas/install_node_modules
|
|
23
19
|
- name: Typecheck
|
|
24
|
-
run:
|
|
20
|
+
run: npm run typecheck
|
|
25
21
|
- name: Lint
|
|
26
|
-
run:
|
|
22
|
+
run: npm run lint
|
|
27
23
|
- name: Test
|
|
28
|
-
run:
|
|
24
|
+
run: npm run test
|
|
29
25
|
|
|
30
26
|
build_ios:
|
|
31
27
|
name: Build iOS
|
|
@@ -4,17 +4,21 @@ name: Rollback
|
|
|
4
4
|
# canonical eas-cli surface:
|
|
5
5
|
#
|
|
6
6
|
# "previous" → republish an earlier update group on the same branch.
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# Requires the `group` input (the update-group ID): the
|
|
8
|
+
# workflow runner has no TTY, and `update:republish --branch`
|
|
9
|
+
# only works interactively, so `--group` is mandatory under
|
|
10
|
+
# `--non-interactive`. Use this when a recent OTA broke
|
|
9
11
|
# something users on previous builds didn't see.
|
|
10
12
|
# Maps to `eas update:republish`.
|
|
11
13
|
# https://docs.expo.dev/eas-update/rollbacks/
|
|
12
14
|
#
|
|
13
15
|
# "embedded" → roll back to the build's embedded bundle (the JS that
|
|
14
|
-
# shipped with the .ipa).
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
16
|
+
# shipped with the .ipa). Requires the `runtime_version` input
|
|
17
|
+
# (`--runtime-version` is mandatory under `--non-interactive`).
|
|
18
|
+
# Effectively makes the next-OTA-check ignore the current
|
|
19
|
+
# rollout and serve the binary as-is. Use this when a broken
|
|
20
|
+
# OTA shouldn't ship to ANYONE on that build.
|
|
21
|
+
# Maps to `eas update:roll-back-to-embedded`.
|
|
18
22
|
# https://docs.expo.dev/eas-update/rollbacks/
|
|
19
23
|
#
|
|
20
24
|
# Pair with the asc-events workflow's Slack notify so the team knows.
|
|
@@ -28,7 +32,7 @@ on:
|
|
|
28
32
|
required: true
|
|
29
33
|
type:
|
|
30
34
|
description: Rollback flavor
|
|
31
|
-
type:
|
|
35
|
+
type: choice
|
|
32
36
|
required: true
|
|
33
37
|
default: previous
|
|
34
38
|
options:
|
|
@@ -38,45 +42,67 @@ on:
|
|
|
38
42
|
description: Optional message stored on the update group
|
|
39
43
|
type: string
|
|
40
44
|
required: false
|
|
45
|
+
group:
|
|
46
|
+
description: Update group ID to republish (required for the "previous" flavor)
|
|
47
|
+
type: string
|
|
48
|
+
required: false
|
|
49
|
+
runtime_version:
|
|
50
|
+
description: Build runtimeVersion to roll back to (required for the "embedded" flavor)
|
|
51
|
+
type: string
|
|
52
|
+
required: false
|
|
41
53
|
|
|
42
|
-
concurrency:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
defaults:
|
|
47
|
-
tools:
|
|
48
|
-
bun: latest
|
|
54
|
+
# No concurrency block on purpose. EAS only accepts `cancel_in_progress: true`
|
|
55
|
+
# (and no custom group yet), but cancelling a half-applied rollback is worse than
|
|
56
|
+
# letting two idempotent runs finish, so the no-block default (don't cancel) is
|
|
57
|
+
# the correct behaviour here.
|
|
49
58
|
|
|
50
59
|
jobs:
|
|
51
60
|
rollback:
|
|
52
61
|
name: Roll back ${{ inputs.branch }} (${{ inputs.type }})
|
|
53
62
|
environment: ${{ inputs.branch == 'production' && 'production' || 'development' }}
|
|
54
63
|
runs_on: linux-medium
|
|
64
|
+
# Inputs reach the script ONLY as env vars, never interpolated into the
|
|
65
|
+
# script body, so a branch/message containing shell metacharacters is treated
|
|
66
|
+
# as data, not code. argv is built as an array; no eval.
|
|
67
|
+
env:
|
|
68
|
+
ROLLBACK_BRANCH: ${{ inputs.branch }}
|
|
69
|
+
ROLLBACK_TYPE: ${{ inputs.type }}
|
|
70
|
+
ROLLBACK_MSG: ${{ inputs.message }}
|
|
71
|
+
ROLLBACK_GROUP: ${{ inputs.group }}
|
|
72
|
+
ROLLBACK_RUNTIME: ${{ inputs.runtime_version }}
|
|
55
73
|
steps:
|
|
56
74
|
- uses: eas/checkout
|
|
57
75
|
- uses: eas/install_node_modules
|
|
58
76
|
- name: Roll back
|
|
59
77
|
run: |
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
if [ "$ROLLBACK_TYPE" = "embedded" ]; then
|
|
79
|
+
# --runtime-version and --message are both required in non-interactive
|
|
80
|
+
# mode; the interactive runtime picker never runs on a TTY-less runner.
|
|
81
|
+
if [ -z "$ROLLBACK_RUNTIME" ]; then
|
|
82
|
+
echo "embedded rollback requires a 'runtime_version' input (the build's runtimeVersion)" >&2
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
msg="${ROLLBACK_MSG:-roll back to embedded}"
|
|
86
|
+
args=(--branch "$ROLLBACK_BRANCH" --platform ios --runtime-version "$ROLLBACK_RUNTIME" --message "$msg" --non-interactive)
|
|
87
|
+
npx eas-cli update:roll-back-to-embedded "${args[@]}"
|
|
69
88
|
else
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
# update:republish --branch is interactive-only; a TTY-less runner
|
|
90
|
+
# must pass the update-group ID via --group.
|
|
91
|
+
if [ -z "$ROLLBACK_GROUP" ]; then
|
|
92
|
+
echo "previous rollback requires a 'group' input (the update-group ID to republish)" >&2
|
|
93
|
+
exit 1
|
|
94
|
+
fi
|
|
95
|
+
args=(--group "$ROLLBACK_GROUP" --platform ios --non-interactive)
|
|
96
|
+
if [ -n "$ROLLBACK_MSG" ]; then args+=(--message "$ROLLBACK_MSG"); fi
|
|
97
|
+
npx eas-cli update:republish "${args[@]}"
|
|
75
98
|
fi
|
|
76
99
|
|
|
77
100
|
notify:
|
|
78
101
|
name: Notify Slack
|
|
79
102
|
needs: [rollback]
|
|
103
|
+
# Paste a real `webhook_url` below and drop `false &&` to enable. EAS does
|
|
104
|
+
# not yet support interpolating `${{ env.* }}` into `webhook_url`.
|
|
105
|
+
if: ${{ false }}
|
|
80
106
|
type: slack
|
|
81
107
|
params:
|
|
82
108
|
webhook_url: https://hooks.slack.com/services/REPLACE/ME/HERE
|
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
name: Rollout
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# Two paths, controlled by `action`:
|
|
9
|
-
#
|
|
10
|
-
# "publish" → `eas update --branch <b> --rollout-percentage <%>`
|
|
11
|
-
# Publishes a fresh OTA from the current HEAD at the given %.
|
|
12
|
-
# Use to start a new rollout (e.g. 5% → 25% → 100%).
|
|
13
|
-
#
|
|
14
|
-
# "edit" → `eas update:edit --branch <b> --rollout-percentage <%>`
|
|
15
|
-
# Bumps the percentage of the currently-active rollout group
|
|
16
|
-
# on the branch. Use to ramp an existing rollout.
|
|
17
|
-
#
|
|
18
|
-
# After confirming adoption looks healthy on the Insights dashboard, set
|
|
19
|
-
# the rollout to 100. To kill a bad rollout, use the `rollback` workflow.
|
|
3
|
+
# Two paths, controlled by `action`, mirroring the EAS Update rollout flow
|
|
4
|
+
# (https://docs.expo.dev/eas-update/rollouts/):
|
|
5
|
+
# "publish" → `eas update` publishes a fresh OTA from HEAD at the given %.
|
|
6
|
+
# "edit" → `eas update:edit` bumps the % of the active rollout group.
|
|
7
|
+
# To kill a bad rollout, use the `rollback` workflow.
|
|
20
8
|
|
|
21
9
|
on:
|
|
22
10
|
workflow_dispatch:
|
|
@@ -28,7 +16,7 @@ on:
|
|
|
28
16
|
default: production
|
|
29
17
|
action:
|
|
30
18
|
description: Publish a new rollout, or edit the % of the existing one
|
|
31
|
-
type:
|
|
19
|
+
type: choice
|
|
32
20
|
required: true
|
|
33
21
|
default: edit
|
|
34
22
|
options:
|
|
@@ -40,41 +28,47 @@ on:
|
|
|
40
28
|
required: true
|
|
41
29
|
default: 25
|
|
42
30
|
|
|
43
|
-
concurrency:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
defaults:
|
|
48
|
-
tools:
|
|
49
|
-
bun: latest
|
|
31
|
+
# No concurrency block on purpose. EAS only accepts `cancel_in_progress: true`
|
|
32
|
+
# (and no custom group yet), but cancelling an in-flight rollout mid-publish is
|
|
33
|
+
# worse than letting it finish, so the no-block default (don't cancel) is the
|
|
34
|
+
# correct behaviour here.
|
|
50
35
|
|
|
51
36
|
jobs:
|
|
52
37
|
rollout:
|
|
53
38
|
name: ${{ inputs.action }} → ${{ inputs.percentage }}% on ${{ inputs.branch }}
|
|
54
39
|
environment: ${{ inputs.branch == 'production' && 'production' || 'development' }}
|
|
55
40
|
runs_on: linux-medium
|
|
41
|
+
# Inputs reach the script only as env vars (never interpolated into the body)
|
|
42
|
+
# so a branch with shell metacharacters is treated as data, not code.
|
|
43
|
+
env:
|
|
44
|
+
ROLLOUT_BRANCH: ${{ inputs.branch }}
|
|
45
|
+
ROLLOUT_ACTION: ${{ inputs.action }}
|
|
46
|
+
ROLLOUT_PCT: ${{ inputs.percentage }}
|
|
56
47
|
steps:
|
|
57
48
|
- uses: eas/checkout
|
|
58
49
|
- uses: eas/install_node_modules
|
|
59
50
|
- name: Roll out
|
|
60
51
|
run: |
|
|
61
|
-
if [ "$
|
|
62
|
-
|
|
63
|
-
--branch "$
|
|
52
|
+
if [ "$ROLLOUT_ACTION" = "publish" ]; then
|
|
53
|
+
npx eas-cli update \
|
|
54
|
+
--branch "$ROLLOUT_BRANCH" \
|
|
64
55
|
--platform ios \
|
|
65
|
-
--rollout-percentage "$
|
|
56
|
+
--rollout-percentage "$ROLLOUT_PCT" \
|
|
66
57
|
--non-interactive \
|
|
67
|
-
--message "Rollout publish @ ${
|
|
58
|
+
--message "Rollout publish @ ${ROLLOUT_PCT}%"
|
|
68
59
|
else
|
|
69
|
-
|
|
70
|
-
--branch "$
|
|
71
|
-
--rollout-percentage "$
|
|
60
|
+
npx eas-cli update:edit \
|
|
61
|
+
--branch "$ROLLOUT_BRANCH" \
|
|
62
|
+
--rollout-percentage "$ROLLOUT_PCT" \
|
|
72
63
|
--non-interactive
|
|
73
64
|
fi
|
|
74
65
|
|
|
75
66
|
notify:
|
|
76
67
|
name: Notify Slack
|
|
77
68
|
needs: [rollout]
|
|
69
|
+
# Paste a real `webhook_url` below and drop `false &&` to enable. EAS does
|
|
70
|
+
# not yet support interpolating `${{ env.* }}` into `webhook_url`.
|
|
71
|
+
if: ${{ false }}
|
|
78
72
|
type: slack
|
|
79
73
|
params:
|
|
80
74
|
webhook_url: https://hooks.slack.com/services/REPLACE/ME/HERE
|
|
@@ -25,16 +25,12 @@ on:
|
|
|
25
25
|
- cron: "0 12 1 */3 *" # 12:00 UTC on the 1st, every 3rd month
|
|
26
26
|
workflow_dispatch: {}
|
|
27
27
|
|
|
28
|
-
defaults:
|
|
29
|
-
tools:
|
|
30
|
-
bun: latest
|
|
31
|
-
|
|
32
28
|
jobs:
|
|
33
29
|
rotate:
|
|
34
30
|
name: Sign + push fresh client_secret
|
|
35
31
|
type: custom
|
|
36
32
|
runs_on: linux-medium
|
|
37
|
-
environment: production
|
|
33
|
+
environment: production
|
|
38
34
|
steps:
|
|
39
35
|
- uses: eas/checkout
|
|
40
36
|
- uses: eas/install_node_modules
|
|
@@ -19,10 +19,6 @@ concurrency:
|
|
|
19
19
|
cancel_in_progress: true
|
|
20
20
|
group: ${{ workflow.filename }}-${{ github.ref }}
|
|
21
21
|
|
|
22
|
-
defaults:
|
|
23
|
-
tools:
|
|
24
|
-
bun: latest
|
|
25
|
-
|
|
26
22
|
jobs:
|
|
27
23
|
precheck:
|
|
28
24
|
name: Precheck
|
|
@@ -32,11 +28,11 @@ jobs:
|
|
|
32
28
|
- uses: eas/checkout
|
|
33
29
|
- uses: eas/install_node_modules
|
|
34
30
|
- name: Typecheck
|
|
35
|
-
run:
|
|
31
|
+
run: npm run typecheck
|
|
36
32
|
- name: Lint
|
|
37
|
-
run:
|
|
33
|
+
run: npm run lint
|
|
38
34
|
- name: Test
|
|
39
|
-
run:
|
|
35
|
+
run: npm run test
|
|
40
36
|
|
|
41
37
|
build_ios:
|
|
42
38
|
name: Build iOS
|