@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.
Files changed (174) hide show
  1. package/README.md +10 -10
  2. package/dist/index.js +8 -7
  3. package/dist/templates/default/.eas/workflows/asc-events.yml +9 -6
  4. package/dist/templates/default/.eas/workflows/deploy-production.yml +28 -15
  5. package/dist/templates/default/.eas/workflows/e2e-tests.yml +3 -2
  6. package/dist/templates/default/.eas/workflows/pr-preview.yml +12 -21
  7. package/dist/templates/default/.eas/workflows/release.yml +3 -7
  8. package/dist/templates/default/.eas/workflows/rollback.yml +54 -28
  9. package/dist/templates/default/.eas/workflows/rollout.yml +27 -33
  10. package/dist/templates/default/.eas/workflows/rotate-apple-jwt.yml +1 -5
  11. package/dist/templates/default/.eas/workflows/testflight.yml +3 -7
  12. package/dist/templates/default/.github/workflows/check.yml +20 -12
  13. package/dist/templates/default/.maestro/launch.yaml +19 -10
  14. package/dist/templates/default/AGENTS.md +25 -8
  15. package/dist/templates/default/DESIGN.md +14 -10
  16. package/dist/templates/default/README.md +83 -78
  17. package/dist/templates/default/SETUP.md +159 -152
  18. package/dist/templates/default/__tests__/convex/_auth-harness.test.ts +112 -0
  19. package/dist/templates/default/__tests__/convex/_harness.ts +132 -0
  20. package/dist/templates/default/__tests__/convex/appAttest.test.ts +172 -0
  21. package/dist/templates/default/__tests__/convex/appAttestStore.test.ts +48 -0
  22. package/dist/templates/default/__tests__/convex/pushTokens-remove.test.ts +106 -0
  23. package/dist/templates/default/__tests__/convex/pushTokens-upsert.test.ts +146 -0
  24. package/dist/templates/default/__tests__/convex/users-deleteAccount.test.ts +140 -0
  25. package/dist/templates/default/__tests__/convex/users-deleteAvatar.test.ts +104 -0
  26. package/dist/templates/default/__tests__/convex/users-getMe.test.ts +98 -0
  27. package/dist/templates/default/__tests__/convex/users-getUser.test.ts +120 -0
  28. package/dist/templates/default/__tests__/convex/users-hardDeleteExpired.test.ts +67 -0
  29. package/dist/templates/default/__tests__/convex/users-restoreAccount.test.ts +96 -0
  30. package/dist/templates/default/__tests__/convex/users-updateAvatar.test.ts +92 -0
  31. package/dist/templates/default/__tests__/convex/users-updateProfile.test.ts +126 -0
  32. package/dist/templates/default/__tests__/convex/webhook.test.ts +31 -0
  33. package/dist/templates/default/__tests__/lib/deep-link.test.ts +51 -6
  34. package/dist/templates/default/__tests__/lib/schemas.test.ts +205 -0
  35. package/dist/templates/default/__tests__/lib/text-style.test.ts +31 -0
  36. package/dist/templates/default/_env.example +7 -7
  37. package/dist/templates/default/_gitattributes +1 -1
  38. package/dist/templates/default/_gitignore +17 -2
  39. package/dist/templates/default/_npmrc +7 -0
  40. package/dist/templates/default/_oxlintrc.json +1 -1
  41. package/dist/templates/default/app-store/accessibility.config.json +20 -0
  42. package/dist/templates/default/app-store/privacy.config.json +27 -0
  43. package/dist/templates/default/app.config.ts +105 -33
  44. package/dist/templates/default/app.json +1 -9
  45. package/dist/templates/default/convex/_generated/api.d.ts +12 -0
  46. package/dist/templates/default/convex/admin.ts +0 -13
  47. package/dist/templates/default/convex/appAttest.ts +467 -0
  48. package/dist/templates/default/convex/appAttestStore.ts +141 -0
  49. package/dist/templates/default/convex/apple.ts +53 -0
  50. package/dist/templates/default/convex/auth.ts +6 -45
  51. package/dist/templates/default/convex/constants.ts +2 -7
  52. package/dist/templates/default/convex/crons.ts +12 -5
  53. package/dist/templates/default/convex/email.ts +4 -24
  54. package/dist/templates/default/convex/env.ts +0 -4
  55. package/dist/templates/default/convex/errors.ts +0 -7
  56. package/dist/templates/default/convex/functions.ts +0 -26
  57. package/dist/templates/default/convex/http.ts +3 -5
  58. package/dist/templates/default/convex/log.ts +2 -25
  59. package/dist/templates/default/convex/pushSender.ts +145 -0
  60. package/dist/templates/default/convex/pushTokens.ts +110 -13
  61. package/dist/templates/default/convex/rateLimit.ts +8 -39
  62. package/dist/templates/default/convex/schema.ts +48 -5
  63. package/dist/templates/default/convex/tsconfig.json +1 -0
  64. package/dist/templates/default/convex/users.ts +143 -61
  65. package/dist/templates/default/convex/validators.ts +1 -38
  66. package/dist/templates/default/convex/webhook.ts +1 -31
  67. package/dist/templates/default/convex.json +1 -2
  68. package/dist/templates/default/metro.config.js +9 -1
  69. package/dist/templates/default/package.json +67 -70
  70. package/dist/templates/default/plugins/README.md +5 -1
  71. package/dist/templates/default/scripts/README.md +9 -9
  72. package/dist/templates/default/scripts/_run.mjs +3 -20
  73. package/dist/templates/default/scripts/clean.ts +81 -69
  74. package/dist/templates/default/scripts/gen-update-cert.mjs +98 -0
  75. package/dist/templates/default/{app → src/app}/(app)/(tabs)/(home)/index.tsx +21 -6
  76. package/dist/templates/default/{app → src/app}/(app)/(tabs)/(home,search)/_layout.tsx +9 -8
  77. package/dist/templates/default/{app → src/app}/(app)/(tabs)/(search)/index.tsx +26 -24
  78. package/dist/templates/default/{app → src/app}/(app)/(tabs)/_layout.tsx +3 -4
  79. package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/_layout.tsx +10 -6
  80. package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/index.tsx +81 -51
  81. package/dist/templates/default/{app → src/app}/(app)/(tabs)/settings/preferences.tsx +72 -12
  82. package/dist/templates/default/src/app/(app)/_layout.tsx +147 -0
  83. package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/_layout.tsx +4 -5
  84. package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/forgot-password.tsx +15 -9
  85. package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/reset-password.tsx +88 -14
  86. package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/sign-in.tsx +65 -35
  87. package/dist/templates/default/{app/(auth) → src/app/(app)/auth}/sign-up.tsx +131 -196
  88. package/dist/templates/default/src/app/(app)/debug.tsx +479 -0
  89. package/dist/templates/default/{app → src/app}/(app)/help.tsx +76 -64
  90. package/dist/templates/default/{app → src/app}/(app)/linked.tsx +21 -27
  91. package/dist/templates/default/{app → src/app}/(app)/privacy.tsx +35 -8
  92. package/dist/templates/default/src/app/(app)/profile/change-password.tsx +264 -0
  93. package/dist/templates/default/{app/(app)/profile.tsx → src/app/(app)/profile/index.tsx} +179 -255
  94. package/dist/templates/default/src/app/(app)/restore-account.tsx +192 -0
  95. package/dist/templates/default/src/app/(app)/sessions.tsx +287 -0
  96. package/dist/templates/default/src/app/(app)/welcome.tsx +194 -0
  97. package/dist/templates/default/src/app/+native-intent.tsx +25 -0
  98. package/dist/templates/default/src/app/+not-found.tsx +43 -0
  99. package/dist/templates/default/{app → src/app}/_layout.tsx +28 -37
  100. package/dist/templates/default/src/components/auth/apple-button.tsx +51 -0
  101. package/dist/templates/default/{components → src/components}/auth/otp-verification.tsx +79 -58
  102. package/dist/templates/default/{components → src/components}/auth/password-field.tsx +74 -18
  103. package/dist/templates/default/src/components/auth/segmented-toggle.tsx +71 -0
  104. package/dist/templates/default/src/components/ui/content-unavailable.tsx +81 -0
  105. package/dist/templates/default/src/components/ui/convex-error.tsx +21 -0
  106. package/dist/templates/default/src/components/ui/error-boundary.tsx +89 -0
  107. package/dist/templates/default/{components → src/components}/ui/loading-screen.tsx +5 -4
  108. package/dist/templates/default/{components → src/components}/ui/material.tsx +50 -17
  109. package/dist/templates/default/src/components/ui/offline-banner.tsx +59 -0
  110. package/dist/templates/default/{components → src/components}/ui/prominent-button.tsx +8 -11
  111. package/dist/templates/default/{components → src/components}/ui/skeleton.tsx +31 -13
  112. package/dist/templates/default/src/components/ui/status-text.tsx +64 -0
  113. package/dist/templates/default/src/components/ui/update-banner.tsx +85 -0
  114. package/dist/templates/default/{constants → src/constants}/layout.ts +0 -6
  115. package/dist/templates/default/{constants → src/constants}/theme.ts +49 -64
  116. package/dist/templates/default/{constants → src/constants}/ui.ts +13 -4
  117. package/dist/templates/default/src/hooks/use-debounce.ts +12 -0
  118. package/dist/templates/default/src/hooks/use-deep-link.ts +51 -0
  119. package/dist/templates/default/src/hooks/use-delete-account.ts +35 -0
  120. package/dist/templates/default/src/hooks/use-motion-screen-options.ts +13 -0
  121. package/dist/templates/default/src/hooks/use-network.ts +34 -0
  122. package/dist/templates/default/{hooks → src/hooks}/use-notifications.ts +39 -30
  123. package/dist/templates/default/src/hooks/use-reduce-transparency.ts +30 -0
  124. package/dist/templates/default/{hooks → src/hooks}/use-theme.ts +0 -5
  125. package/dist/templates/default/src/lib/appAttest.ts +78 -0
  126. package/dist/templates/default/src/lib/assets.ts +9 -0
  127. package/dist/templates/default/src/lib/deep-link.ts +82 -0
  128. package/dist/templates/default/{lib → src/lib}/dev-menu.ts +0 -4
  129. package/dist/templates/default/{lib → src/lib}/device.ts +1 -13
  130. package/dist/templates/default/{lib → src/lib}/dynamic-font.ts +13 -10
  131. package/dist/templates/default/src/lib/dynamic-symbol-size.ts +33 -0
  132. package/dist/templates/default/src/lib/masks.ts +21 -0
  133. package/dist/templates/default/src/lib/native-state.ts +20 -0
  134. package/dist/templates/default/{lib → src/lib}/notifications.ts +7 -45
  135. package/dist/templates/default/{lib → src/lib}/preferences.ts +0 -2
  136. package/dist/templates/default/{lib → src/lib}/schemas.ts +19 -16
  137. package/dist/templates/default/src/lib/text-style.ts +20 -0
  138. package/dist/templates/default/{lib → src/lib}/updates.ts +0 -7
  139. package/dist/templates/default/store.config.json +1 -1
  140. package/dist/templates/default/tsconfig.json +3 -1
  141. package/dist/templates/default/vitest.config.ts +8 -1
  142. package/package.json +5 -5
  143. package/dist/templates/default/app/(app)/_layout.tsx +0 -73
  144. package/dist/templates/default/app/(app)/debug.tsx +0 -389
  145. package/dist/templates/default/app/(app)/sessions.tsx +0 -191
  146. package/dist/templates/default/app/(app)/welcome.tsx +0 -140
  147. package/dist/templates/default/app/+native-intent.tsx +0 -14
  148. package/dist/templates/default/app/+not-found.tsx +0 -51
  149. package/dist/templates/default/bun.lock +0 -1860
  150. package/dist/templates/default/components/auth/segmented-toggle.tsx +0 -47
  151. package/dist/templates/default/components/ui/convex-error.tsx +0 -32
  152. package/dist/templates/default/components/ui/error-boundary.tsx +0 -57
  153. package/dist/templates/default/components/ui/offline-banner.tsx +0 -58
  154. package/dist/templates/default/components/ui/status-text.tsx +0 -49
  155. package/dist/templates/default/components/ui/update-banner.tsx +0 -82
  156. package/dist/templates/default/fingerprint.config.js +0 -9
  157. package/dist/templates/default/hooks/use-debounce.ts +0 -20
  158. package/dist/templates/default/hooks/use-deep-link.ts +0 -43
  159. package/dist/templates/default/hooks/use-network.ts +0 -11
  160. package/dist/templates/default/lib/assets.ts +0 -17
  161. package/dist/templates/default/lib/deep-link.ts +0 -71
  162. package/dist/templates/default/patches/PR-368.patch +0 -91
  163. package/dist/templates/default/patches/convex-dev-better-auth-0.12.2.tgz +0 -0
  164. /package/dist/templates/default/{hooks → src/hooks}/use-navigation-tracking.ts +0 -0
  165. /package/dist/templates/default/{hooks → src/hooks}/use-onboarding.ts +0 -0
  166. /package/dist/templates/default/{hooks → src/hooks}/use-reduced-motion.ts +0 -0
  167. /package/dist/templates/default/{hooks → src/hooks}/use-updates.ts +0 -0
  168. /package/dist/templates/default/{lib → src/lib}/a11y.ts +0 -0
  169. /package/dist/templates/default/{lib → src/lib}/app.ts +0 -0
  170. /package/dist/templates/default/{lib → src/lib}/auth-client.ts +0 -0
  171. /package/dist/templates/default/{lib → src/lib}/convex-auth.tsx +0 -0
  172. /package/dist/templates/default/{lib → src/lib}/env.ts +0 -0
  173. /package/dist/templates/default/{lib → src/lib}/haptics.ts +0 -0
  174. /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
- bunx @ramonclaudio/create-vexpo@latest my-app
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
- bunx vexpo lite # 60 seconds: Convex + Better Auth, simulator-ready
22
- bunx vexpo lite --new # same + Convex signup walkthrough for first-time users
23
- bunx vexpo full # full provisioning: TestFlight-ready (Convex, Better Auth, Resend, Apple Sign In, EAS, rebrand)
24
- bunx vexpo full --new # same + Apple/Convex/Expo/Resend signup walkthrough
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
- `bunx 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.
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
- `bunx 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.
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 `bunx vexpo lite` / `bunx vexpo full` prompt. |
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, swaps `vexpo` workspace ref for the published version), runs `bun install`, and initializes a fresh git repo with `feat: initial commit`.
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 `bunx vexpo <subcommand>` resolves to the local pinned version. Setup commands aren't in `package.json`. They're one-shot CLI invocations, not runtime scripts.
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.0"};
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 `bunx vexpo lite` / `bunx vexpo full` prompt after install").option("-y, --yes", "accept defaults, skip prompts").version(package_default.version, "-v, --version").parse();
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 "bun";
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
- ` bunx vexpo lite ${kleur.dim("# dev mode: Convex + Better Auth, 60s to simulator")}`
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
- ` bunx vexpo full ${kleur.dim("# real setup: TestFlight-ready (add --new if you're new)")}`
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
- branches: ["main"]
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: bun run typecheck
24
+ run: npm run typecheck
26
25
  - name: Lint
27
- run: bun run lint
26
+ run: npm run lint
28
27
  - name: Test
29
- run: bun run test
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: bunx convex deploy
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
- bunx eas-cli update:insights "$GROUP" --json --non-interactive || true
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
- if: ${{ needs.update_ios.outputs.first_update_group_id }}
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
- # Replace with your Slack incoming-webhook URL. EAS does not yet
122
- # support reading this from EAS env (see job comment above). interpolation
123
- # of `${{ env.* }}` for `webhook_url` lands in a future EAS release.
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
- # Builds + auto-posts a comment on the PR with build/update IDs and QR codes
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
- pull_request:
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: bun run typecheck
34
+ run: npm run typecheck
44
35
  - name: Lint
45
- run: bun run lint
36
+ run: npm run lint
46
37
  - name: Test
47
- run: bun run test
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: bun run typecheck
20
+ run: npm run typecheck
25
21
  - name: Lint
26
- run: bun run lint
22
+ run: npm run lint
27
23
  - name: Test
28
- run: bun run test
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
- # Picks the most recent successful update before the
8
- # currently-active one. Use this when a recent OTA broke
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). Effectively makes the next-OTA-check
15
- # ignore the current rollout and serve the binary as-is.
16
- # Use this when a broken OTA shouldn't ship to ANYONE on
17
- # that build. Maps to `eas update:roll-back-to-embedded`.
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: select
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
- cancel_in_progress: false
44
- group: ${{ workflow.filename }}-${{ inputs.branch }}
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
- MSG="${{ inputs.message }}"
61
- MSG_ARG=""
62
- if [ -n "$MSG" ]; then MSG_ARG="--message \"$MSG\""; fi
63
- if [ "${{ inputs.type }}" = "embedded" ]; then
64
- eval bunx eas-cli update:roll-back-to-embedded \
65
- --branch "${{ inputs.branch }}" \
66
- --platform ios \
67
- --non-interactive \
68
- $MSG_ARG
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
- eval bunx eas-cli update:republish \
71
- --branch "${{ inputs.branch }}" \
72
- --platform ios \
73
- --non-interactive \
74
- $MSG_ARG
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
- # Manually run a gradual rollout: publish a fresh OTA at a non-100%
4
- # percentage, or bump/lower the % of the most recent rollout group on a
5
- # branch. Mirrors the canonical EAS Update rollout flow:
6
- # https://docs.expo.dev/eas-update/rollouts/
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: select
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
- cancel_in_progress: false
45
- group: ${{ workflow.filename }}-${{ inputs.branch }}
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 [ "${{ inputs.action }}" = "publish" ]; then
62
- bunx eas-cli update \
63
- --branch "${{ inputs.branch }}" \
52
+ if [ "$ROLLOUT_ACTION" = "publish" ]; then
53
+ npx eas-cli update \
54
+ --branch "$ROLLOUT_BRANCH" \
64
55
  --platform ios \
65
- --rollout-percentage "${{ inputs.percentage }}" \
56
+ --rollout-percentage "$ROLLOUT_PCT" \
66
57
  --non-interactive \
67
- --message "Rollout publish @ ${{ inputs.percentage }}%"
58
+ --message "Rollout publish @ ${ROLLOUT_PCT}%"
68
59
  else
69
- bunx eas-cli update:edit \
70
- --branch "${{ inputs.branch }}" \
71
- --rollout-percentage "${{ inputs.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 # pulls EAS env vars from 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: bun run typecheck
31
+ run: npm run typecheck
36
32
  - name: Lint
37
- run: bun run lint
33
+ run: npm run lint
38
34
  - name: Test
39
- run: bun run test
35
+ run: npm run test
40
36
 
41
37
  build_ios:
42
38
  name: Build iOS