fix(ios): close final release review blockers
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
27
.github/workflows/publish-app-store.yml
vendored
27
.github/workflows/publish-app-store.yml
vendored
@@ -7,7 +7,9 @@
|
||||
# ── HUMAN GATE (one-time, after Apple Developer Program enrollment) ──────────────
|
||||
# Complete ALL of the following before releasing. Do NOT invent any of these IDs.
|
||||
#
|
||||
# 1. Fill and commit the eas.json placeholders (see eas.json.README.md for click paths):
|
||||
# 1. Link the app to an Expo project and add its UUID as the GitHub Actions
|
||||
# repository variable EAS_PROJECT_ID (see eas.json.README.md), then fill and
|
||||
# commit the eas.json placeholders:
|
||||
# submit.production.ios.ascAppId REPLACE_WITH_APP_STORE_CONNECT_APP_ID → numeric App Store Connect App ID
|
||||
# submit.production.ios.appleTeamId REPLACE_WITH_APPLE_TEAM_ID → 10-char Apple Team ID
|
||||
#
|
||||
@@ -26,7 +28,8 @@
|
||||
# SENTRY_AUTH_TOKEN, SENTRY_ORG, and SENTRY_PROJECT in Expo before releasing.
|
||||
#
|
||||
# Build number is managed remotely by EAS (eas.json: cli.appVersionSource=remote,
|
||||
# build.production.ios.autoIncrement=buildNumber). No app.json mutation happens here.
|
||||
# build.production.ios.autoIncrement=buildNumber). The workflow only injects the
|
||||
# EAS project linkage into its temporary runner copy of app.json.
|
||||
|
||||
name: Publish to App Store (TestFlight)
|
||||
|
||||
@@ -54,6 +57,7 @@ jobs:
|
||||
env:
|
||||
EAS_CLI_VERSION: "21.0.0"
|
||||
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
|
||||
EAS_PROJECT_ID: ${{ vars.EAS_PROJECT_ID }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@@ -80,6 +84,13 @@ jobs:
|
||||
need "APPLE_APP_STORE_CONNECT_API_KEY_ID" "${ASC_KEY_ID:-}"
|
||||
need "APPLE_APP_STORE_CONNECT_ISSUER_ID" "${ASC_ISSUER_ID:-}"
|
||||
need "APPLE_APP_STORE_CONNECT_API_KEY" "${ASC_KEY_B64:-}"
|
||||
if [ -z "${EAS_PROJECT_ID:-}" ]; then
|
||||
echo "::error::Missing required repository variable: EAS_PROJECT_ID"
|
||||
fail=1
|
||||
elif ! printf '%s' "$EAS_PROJECT_ID" | grep -Eq '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'; then
|
||||
echo "::error::EAS_PROJECT_ID must be an Expo project UUID"
|
||||
fail=1
|
||||
fi
|
||||
asc_app_id=$(node -p "require('./eas.json').submit.production.ios.ascAppId || ''")
|
||||
team_id=$(node -p "require('./eas.json').submit.production.ios.appleTeamId || ''")
|
||||
case "$asc_app_id" in
|
||||
@@ -113,6 +124,18 @@ jobs:
|
||||
- name: Install dependencies (deterministic)
|
||||
run: npm ci --legacy-peer-deps
|
||||
|
||||
- name: Configure EAS project linkage
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const j = require('./app.json');
|
||||
j.expo.extra = { ...j.expo.extra, eas: { ...j.expo.extra?.eas, projectId: process.env.EAS_PROJECT_ID } };
|
||||
fs.writeFileSync('app.json', JSON.stringify(j, null, 2) + '\n');
|
||||
"
|
||||
test "$(node -p "require('./app.json').expo.extra.eas.projectId")" = "$EAS_PROJECT_ID"
|
||||
echo "Linked build to Expo project $EAS_PROJECT_ID."
|
||||
|
||||
- name: Configure App Store Connect API key
|
||||
env:
|
||||
ASC_KEY_ID: ${{ secrets.APPLE_APP_STORE_CONNECT_API_KEY_ID }}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from "../../src/lib/notifications"
|
||||
import type { Category } from "../../src/lib/notifications"
|
||||
import { hasTelemetryConsent, setTelemetryConsent } from "../../src/lib/telemetry"
|
||||
import { PRIVACY_POLICY_URL } from "../../src/lib/links"
|
||||
|
||||
function SettingRow({
|
||||
icon,
|
||||
@@ -215,7 +216,7 @@ export default function SettingsScreen() {
|
||||
label="Privacy Policy"
|
||||
description="What data we collect and how"
|
||||
isDark={isDark}
|
||||
onPress={() => Linking.openURL("https://agentlabs.cc/opencode/privacy")}
|
||||
onPress={() => Linking.openURL(PRIVACY_POLICY_URL)}
|
||||
right={<Ionicons name="open-outline" size={20} color={isDark ? "#666666" : "#999999"} />}
|
||||
/>
|
||||
</SettingSection>
|
||||
|
||||
@@ -141,13 +141,15 @@ While waiting for Apple's verification call and approval:
|
||||
- Note: Key ID and Issuer ID
|
||||
- Base64-encode the .p8 and store in GitHub secret `APPLE_APP_STORE_CONNECT_API_KEY`
|
||||
|
||||
4. Configure the EAS `production` environment for optional crash reporting:
|
||||
4. Run `eas init` once, then add the generated `extra.eas.projectId` UUID as the GitHub Actions repository variable `EAS_PROJECT_ID`.
|
||||
|
||||
5. Configure the EAS `production` environment for optional crash reporting:
|
||||
- `EXPO_PUBLIC_SENTRY_DSN`
|
||||
- `SENTRY_AUTH_TOKEN` (secret visibility)
|
||||
- `SENTRY_ORG`
|
||||
- `SENTRY_PROJECT`
|
||||
|
||||
5. Create an internal TestFlight group and add yourself as tester
|
||||
6. Create an internal TestFlight group and add yourself as tester
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ Because the answer is "No", no ERN (Encryption Registration Number) is required
|
||||
| 9 | Export compliance | ✅ Done | `ITSAppUsesNonExemptEncryption: false` added to `app.json`. Answers + rationale in this doc (see Export Compliance section above) and `distribution/app-store-listing.md`. |
|
||||
| 10 | ATS justification in App Review notes | ✅ Done | Full justification text in `distribution/app-store-listing.md` under "App Review Notes — ATS Justification" |
|
||||
| 11 | Reviewer test instructions | ✅ Done | Updated with correct command (`opencode serve --hostname 0.0.0.0`) in `distribution/app-store-listing.md` |
|
||||
| 12 | GitHub secrets: `EXPO_TOKEN`, `APPLE_APP_STORE_CONNECT_API_KEY_ID`, `APPLE_APP_STORE_CONNECT_ISSUER_ID`, `APPLE_APP_STORE_CONNECT_API_KEY` (base64 .p8) | User | 🟡 post-enrollment — see `.github/workflows/publish-app-store.yml` header |
|
||||
| 12 | GitHub variable `EAS_PROJECT_ID`; secrets: `EXPO_TOKEN`, `APPLE_APP_STORE_CONNECT_API_KEY_ID`, `APPLE_APP_STORE_CONNECT_ISSUER_ID`, `APPLE_APP_STORE_CONNECT_API_KEY` (base64 .p8) | User | 🟡 post-enrollment — run `eas init`, then see `.github/workflows/publish-app-store.yml` |
|
||||
| 13 | Update `eas.json` placeholders: `ascAppId` + `appleTeamId` | User | 🟡 post-enrollment — see `eas.json.README.md` for click paths |
|
||||
| 14 | CI workflow validated | CI | 🟡 Linux checks pass; PR must prove the macOS Simulator build |
|
||||
| 15 | TestFlight release notes | ✅ Done | `distribution/whatsnew-ios/release-notes-en-US.txt` — polished, 1658 chars (limit 4000) |
|
||||
@@ -102,7 +102,7 @@ Because the answer is "No", no ERN (Encryption Registration Number) is required
|
||||
1. (manual) Sign in to App Store Connect, create app with bundle id `cc.agentlabs.opencode`.
|
||||
2. (manual) Generate App Store Connect API key (App Manager role) → download `.p8` → base64 encode → add as GitHub secret.
|
||||
3. (manual) Update `eas.json` placeholders (Team ID, ASC App ID).
|
||||
4. (manual) `eas login` + `eas build:configure` for first-time setup (managed signing).
|
||||
4. (manual) `eas login` + `eas init` + `eas build:configure`; add the generated project UUID as repository variable `EAS_PROJECT_ID`.
|
||||
5. (automated) Publish a GitHub Release for the version tag → CI calls EAS Build → EAS Submit → IPA lands in TestFlight.
|
||||
6. (manual, first time) Add internal testers in App Store Connect → distribute via TestFlight.
|
||||
7. (manual) After internal testing OK → submit for App Store review (production).
|
||||
|
||||
@@ -152,7 +152,7 @@ When the opencode AI agent requests a file-access permission, the notification b
|
||||
**Files:** `app.json:29`, `app.json:38-39`
|
||||
**Description:** Both platforms allow HTTP connections, which is required for local/LAN servers. This is intentional and correct for the use case. However, neither the Play Store listing, App Store listing, nor a privacy policy document currently explains that HTTP connections may be made to user-provided servers. Google Play's Data Safety section and Apple's App Privacy report will flag arbitrary network access if not documented.
|
||||
**Remediation:**
|
||||
1. Update the privacy policy at `agentlabs.cc/opencode/privacy` to explain that the app connects to user-configured server addresses that may use HTTP.
|
||||
1. Update the canonical privacy policy at `https://dzianisv.github.io/opencode-mobile/privacy/` to explain that the app connects to user-configured server addresses that may use HTTP.
|
||||
2. In Play Store Data Safety: disclose "Other app performance data" collected (crash reports via Sentry — opt-in).
|
||||
**Status:** Open
|
||||
|
||||
|
||||
@@ -73,10 +73,12 @@ Alternatively, in App Store Connect:
|
||||
|
||||
## After filling in the placeholders
|
||||
|
||||
1. Commit the updated `eas.json` to the repo.
|
||||
2. Add the `EXPO_TOKEN` and App Store Connect API GitHub Actions secrets (see `.github/workflows/publish-app-store.yml` for the exact list).
|
||||
3. Publish a GitHub Release for the version tag (or manually dispatch the App Store workflow).
|
||||
4. The release event triggers CI to build the IPA via EAS and submit that exact build to TestFlight.
|
||||
1. Run `eas init` once to create/link the Expo project.
|
||||
2. Copy the generated `extra.eas.projectId` UUID and add it as the GitHub Actions repository variable `EAS_PROJECT_ID`. The release workflow injects it into `app.json` only on the runner.
|
||||
3. Commit the updated `eas.json` to the repo.
|
||||
4. Add the `EXPO_TOKEN` and App Store Connect API GitHub Actions secrets (see `.github/workflows/publish-app-store.yml` for the exact list).
|
||||
5. Publish a GitHub Release for the version tag (or manually dispatch the App Store workflow).
|
||||
6. The release event triggers CI to build the IPA via EAS and submit that exact build to TestFlight.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { View, Text, TouchableOpacity, StyleSheet, useColorScheme, Modal, Linking } from "react-native"
|
||||
import { Ionicons } from "@expo/vector-icons"
|
||||
import { PRIVACY_POLICY_URL } from "../lib/links"
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
@@ -45,9 +46,7 @@ export function TelemetryConsentModal({ visible, onAllow, onDecline }: Props) {
|
||||
</View>
|
||||
|
||||
{/* Privacy policy link */}
|
||||
<TouchableOpacity
|
||||
onPress={() => Linking.openURL("https://agentlabs.cc/opencode/privacy")}
|
||||
>
|
||||
<TouchableOpacity onPress={() => Linking.openURL(PRIVACY_POLICY_URL)}>
|
||||
<Text style={styles.privacyLink}>Read our full privacy policy</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
|
||||
1
src/lib/links.ts
Normal file
1
src/lib/links.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const PRIVACY_POLICY_URL = "https://dzianisv.github.io/opencode-mobile/privacy/"
|
||||
@@ -232,7 +232,10 @@ export function createClient(config: ClientConfig) {
|
||||
try {
|
||||
yield JSON.parse(data)
|
||||
} catch (err) {
|
||||
console.warn("[SSE] Failed to parse event:", data.slice(0, 200), err)
|
||||
console.warn("[SSE] Failed to parse event", {
|
||||
length: data.length,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ export function initSentry() {
|
||||
return scrubEvent(event)
|
||||
},
|
||||
beforeBreadcrumb(crumb) {
|
||||
// Console output can contain malformed server payloads, prompts, or code.
|
||||
if (crumb.category === "console") return null
|
||||
if (crumb.data && typeof crumb.data === "object") {
|
||||
crumb.data = redactObject(crumb.data as Record<string, unknown>)
|
||||
}
|
||||
@@ -149,6 +151,7 @@ function scrubEvent<T extends Sentry.Event>(event: T): T {
|
||||
}
|
||||
}
|
||||
if (event.breadcrumbs) {
|
||||
event.breadcrumbs = event.breadcrumbs.filter((crumb) => crumb.category !== "console")
|
||||
for (const crumb of event.breadcrumbs) {
|
||||
if (typeof crumb.message === "string") crumb.message = redactString(crumb.message)
|
||||
if (crumb.data && typeof crumb.data === "object") {
|
||||
|
||||
Reference in New Issue
Block a user