@imagina-build/ias-cli 0.2.2 → 0.3.0
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 +184 -21
- package/package.json +1 -1
- package/schema/ias.schema.json +46 -1
- package/shared/vault/index.mjs +25 -0
- package/shared/vault/policy-vectors.mjs +237 -0
- package/shared/vault/policy.mjs +427 -0
- package/shared/vault/test-vectors/ias-policy-1/policy.json +4016 -0
- package/shared/vault/vectors.mjs +2 -0
- package/src/apple/capabilities.mjs +96 -0
- package/src/apple/inventory.mjs +75 -0
- package/src/apple/signing.mjs +134 -0
- package/src/applications.mjs +114 -0
- package/src/cli.mjs +28 -10
- package/src/commands/apps/index.mjs +61 -11
- package/src/commands/apps/store.mjs +367 -0
- package/src/commands/auth/index.mjs +3 -3
- package/src/commands/build/index.mjs +2 -0
- package/src/commands/builds/index.mjs +20 -0
- package/src/commands/credentials/apple.mjs +1118 -0
- package/src/commands/credentials/generate.mjs +122 -0
- package/src/commands/credentials/index.mjs +114 -44
- package/src/commands/credentials/seal.mjs +44 -0
- package/src/commands/devices/asc.mjs +50 -8
- package/src/commands/devices/index.mjs +2 -1
- package/src/commands/devices/sync.mjs +57 -4
- package/src/commands/dist/index.mjs +12 -0
- package/src/commands/github/index.mjs +13 -3
- package/src/commands/policy/index.mjs +583 -0
- package/src/commands/policy/policy.mjs +182 -0
- package/src/commands/policy/progress.mjs +20 -0
- package/src/commands/releases/index.mjs +225 -0
- package/src/commands/rollout/index.mjs +186 -0
- package/src/commands/runners/index.mjs +19 -0
- package/src/commands/sdk/index.mjs +31 -4
- package/src/commands/setup/index.mjs +184 -0
- package/src/commands/submit/index.mjs +135 -19
- package/src/commands/testflight/index.mjs +309 -0
- package/src/commands/tokens/index.mjs +99 -0
- package/src/commands/update/index.mjs +26 -5
- package/src/commands/vault/approvers.mjs +5 -1
- package/src/commands/vault/index.mjs +26 -6
- package/src/commands/vault/runners.mjs +48 -2
- package/src/commands/webhooks/index.mjs +19 -5
- package/src/config.mjs +16 -1
- package/src/errors.mjs +52 -6
- package/src/op.mjs +83 -0
- package/src/ota-key.mjs +53 -0
- package/src/provisioning.mjs +281 -0
- package/src/schema.mjs +3 -0
- package/src/setup-state.mjs +76 -0
- package/src/setup.mjs +698 -0
- package/src/team-vault.mjs +2 -2
- package/src/token-store.mjs +1 -1
package/README.md
CHANGED
|
@@ -5,25 +5,26 @@
|
|
|
5
5
|
- Node 22 o posterior, sin dependencias: `util.parseArgs`, `fetch`, `node:crypto` y `node:test`;
|
|
6
6
|
- la configuración de cada app en `ias.json`;
|
|
7
7
|
- las actualizaciones OTA firmadas (`ias update`) y la distribución interna (`ias dist`), que antes eran `tools/expo-ota` y `tools/expo-dist`;
|
|
8
|
+
- la checklist de alta (`ias setup`): comprueba cada paso contra los ficheros y el servidor, hace los que puede aquí (`ias init`, `ias sdk install`, `ias login`, `ias vault init`, la aplicación con `--create-app`, los proyectos de los servicios y el código del runner) y deja los de una persona con su enlace y su comando exacto;
|
|
8
9
|
- el acceso a la API de gestión (`ias login`, pista 1D-2) y la bóveda sellada: las claves del equipo (`ias vault`), las credenciales de firma (`ias credentials`) y las variables (`ias env`), con el formato `ias-vault-2` de [docs/technical/vault.md](../../docs/technical/vault.md);
|
|
9
10
|
- las builds en los runners de IAS o en este Mac (`ias build`, `ias builds`, `ias version`), los envíos a las tiendas (`ias submit`) y lo que el equipo firma para que los runners se fíen (`ias vault approvers|grant|revoke`), con el contrato de [docs/technical/runner-api.md](../../docs/technical/runner-api.md) (pista 2C);
|
|
10
11
|
- salir de EAS paso a paso (`ias migrate eas`, pista 4D).
|
|
11
12
|
|
|
12
13
|
## 1. Instalación
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Se publica en npm como `@imagina-build/ias-cli`, y desde este repositorio se puede usar sin instalar nada:
|
|
15
16
|
|
|
16
17
|
```bash
|
|
17
18
|
node packages/cli/bin/ias.mjs --help
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
Para tenerlo como `ias` en el Mac:
|
|
21
|
+
Para tenerlo como `ias` en el Mac desde el clon:
|
|
21
22
|
|
|
22
23
|
```bash
|
|
23
24
|
npm install --global ./packages/cli
|
|
24
25
|
```
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
En npm, cada push a `main` que toca `packages/cli`, `packages/sdk` o `packages/shared` publica la versión de su `package.json` si aún no está ([`.github/workflows/packages.yml`](../../.github/workflows/packages.yml) y [`scripts/publish.mjs`](scripts/publish.mjs)). Se publican como `@imagina-build/ias-cli` y `@imagina-build/ias`, en la organización `imagina-build` de npm. Instalar no pide token:
|
|
27
28
|
|
|
28
29
|
```bash
|
|
29
30
|
npm install --global @imagina-build/ias-cli
|
|
@@ -43,36 +44,45 @@ npm run pack:release --prefix packages/cli -- ./release
|
|
|
43
44
|
|
|
44
45
|
| Comando | Qué hace |
|
|
45
46
|
| --- | --- |
|
|
47
|
+
| `ias setup [status\|next\|check\|services\|reset] [--create-app] [--name "…"] [--ios-bundle-id <id>] [--android-package <id>] [--apple-team-id <id>] [--app <código>] [--services <lista>] [--team <código>] [--local-builds] [--all] [--build <perfil>] [--publish] [--message "…"] [--wait] [--open] [--item <op://…>] [--dry-run]` | La checklist de alta de una app: comprueba cada paso contra los ficheros y el servidor, hace los que puede aquí (`ias init`, `ias login`, `ias sdk install`, `ias vault init`, la aplicación con `--create-app`, los proyectos OTA y de distribución y el código de alta del runner) y dice qué tiene que hacer una persona. Un paso no avanza mientras el anterior no esté hecho. `next` hace el siguiente; `check` sale con 1 mientras falte algo; `services` elige `updates`, `distribution` y `push`; `reset` olvida el estado local. |
|
|
46
48
|
| `ias init [--app <código>] [--force] [--dry-run]` | Escribe `ias.json` a partir de `app.json`, `eas.json`, `ota.config.json` y `dist.config.json`. Dice de dónde sale cada parte y qué se queda fuera. |
|
|
47
49
|
| `ias doctor` | Comprueba Node, `git`, `op`, Xcode y sus herramientas (`plutil`, `security`, `sips`, `unzip`), `aapt2` del SDK de Android y la configuración de la app. En una app con `package.json`, también el SDK `@imagina-build/ias`: instalado, de una versión compatible con la de `ias` (la misma *major* y, mientras es 0, la misma *minor*) y con su plugin en `app.json`; si la app no lo usa, es un aviso. No abre ningún secreto. |
|
|
48
50
|
| `ias sdk install [--from <origen>] [--dry-run]` | Instala `@imagina-build/ias` con el gestor de paquetes de la app (`packageManager` de `package.json` o su lockfile: npm, yarn, pnpm o bun), añade su plugin al final de los `plugins` de `app.json` sin tocar lo demás (con `app.config.*` no lo toca y dice qué añadir) y dice qué falta: `ias init`, `expo-updates`, `expo-notifications` y `expo-task-manager`, y un prebuild o una build nueva. El paquete sale de `--from` (un `.tgz`, su URL, una carpeta o una versión), de `IAS_SDK_SOURCE`, del `packages/sdk` del repositorio si `ias` corre desde un clon (compilado), o de npm como `@imagina-build/ias@^<versión de ias>`. |
|
|
49
|
-
| `ias update publish\|republish\|rollback-to-embedded\|retire\|restore\|list\|check` | Las actualizaciones OTA, con el comportamiento de `tools/expo-ota` ([README](../../tools/expo-ota/README.md)). `rollback` vale por `rollback-to-embedded`. `--channel` firma con la clave de ese canal y `--rollout <1-100>` empieza con ese porcentaje de las instalaciones (§3.1). |
|
|
51
|
+
| `ias update init\|publish\|republish\|rollback-to-embedded\|retire\|restore\|list\|check` | Las actualizaciones OTA, con el comportamiento de `tools/expo-ota` ([README](../../tools/expo-ota/README.md)). `init` crea el proyecto OTA, su clave de firma y su token en 1Password y escribe `ias.json` (Fase 2). `rollback` vale por `rollback-to-embedded`. `--channel` firma con la clave de ese canal y `--rollout <1-100>` empieza con ese porcentaje de las instalaciones (§3.1). `check` comprueba el canal por defecto de `app.json` y, con `--channel <canal>`, pide el manifiesto como una build de ese canal y lo verifica con su certificado de `ias.json`. |
|
|
50
52
|
| `ias update promote <grupo> --to <canal>` | Publica un grupo en otro canal: comprueba sus bytes con el certificado de su canal y vuelve a firmar el contenido en este Mac con la clave del de destino, con un `id` y un `createdAt` nuevos. Nunca copia una firma (API.md §36.11). |
|
|
51
53
|
| `ias update rollout <grupo> <porcentaje>\|pause\|complete` | Sube o baja el porcentaje de un despliegue (de 1 a 99; 100 lo completa), lo pausa o lo completa (API.md §36.9). |
|
|
52
54
|
| `ias update channels` | Los canales del proyecto con su keyid y su certificado, y si este Mac firma para cada uno. |
|
|
53
|
-
| `ias dist upload\|list\|retire\|restore` | La distribución interna, con el comportamiento de `tools/expo-dist` ([README](../../tools/expo-dist/README.md)). |
|
|
54
|
-
| `ias login [--team <código>]` | Autoriza este ordenador con una passkey (autorización de dispositivo, RFC 8628) y guarda su token personal (`iasp_…`), que es de un equipo (§4). |
|
|
55
|
+
| `ias dist upload\|list\|retire\|restore` | La distribución interna, con el comportamiento de `tools/expo-dist` ([README](../../tools/expo-dist/README.md)). `ias dist init` crea el proyecto y su token en 1Password (Fase 2). |
|
|
56
|
+
| `ias login [--name "…"] [--team <código>] [--no-browser]` | Autoriza este ordenador con una passkey (autorización de dispositivo, RFC 8628) y guarda su token personal (`iasp_…`), que es de un equipo (§4). |
|
|
55
57
|
| `ias logout` | Revoca el token guardado (`DELETE /me/token`) y lo borra. No toca `IAS_TOKEN`. |
|
|
56
58
|
| `ias whoami` | De quién es el token, de qué equipo, de dónde sale y sus alcances. |
|
|
57
59
|
| `ias teams` | Los equipos de la persona, con su rol en cada uno, y cuál es el del token (`GET /teams`). |
|
|
58
|
-
| `ias apps [<código>]` | Las aplicaciones que ve el token,
|
|
60
|
+
| `ias apps [<código>]` | Las aplicaciones que ve el token, una en detalle, una nueva (`--create`) o sus servicios (`--services`). `--create --dry-run` enseña el mismo plan que `ias setup --create-app --dry-run` y no crea nada. |
|
|
61
|
+
| `ias apps store <código> [--check] [--register-bundle-id] [--set-asc-app-id <id>]` | La identidad de la app en App Store Connect: sin flags enseña el bundle id, el app record que IAS guarda y el formulario del alta; `--register-bundle-id` registra el App ID si falta, `--check` lee Apple y guarda el Apple ID, y `--set-asc-app-id` lo guarda a mano. La clave de App Store Connect sale de la bóveda y se abre en este Mac. |
|
|
59
62
|
| `ias usage [--month AAAA-MM]` | Lo que el equipo del token ha usado en un mes (minutos de build, artefactos, OTA, notificaciones, sincronización, dispositivos), por aplicación y con sus límites; con un límite agotado, las builds, las notificaciones o los dispositivos que cubre se rechazan con `limit_exceeded` (`GET /team/usage`, manage-api §17). |
|
|
60
|
-
| `ias vault init\|status` | Crea la clave de recuperación y la de firma del equipo en 1Password y registra sus públicas, con una aprobación con passkey; o compara las del servidor con las de 1Password y verifica la lista firmada de runners (§5). |
|
|
61
|
-
| `ias vault approvers` | Firma con la clave del equipo las passkeys de los `owner` que pueden aprobar una build de tienda (`ias-approvers-1`), después de enseñarlas con su huella y lo que cambia desde la versión firmada, y la sube (§5.2). |
|
|
62
|
-
| `ias vault grant\|revoke <runner>` | Deja a un runner abrir las credenciales del equipo (la lista firmada y una envoltura en cada sobre) o se lo quita cuando el panel ya lo revocó (§5.2). |
|
|
63
|
-
| `ias
|
|
63
|
+
| `ias vault init\|status` | Crea la clave de recuperación y la de firma del equipo en 1Password (`--item` elige el ítem) y registra sus públicas, con una aprobación con passkey; o compara las del servidor con las de 1Password y verifica la lista firmada de runners (§5). `init --dry-run` no crea el ítem ni registra nada. |
|
|
64
|
+
| `ias vault approvers` | Firma con la clave del equipo las passkeys de los `owner` que pueden aprobar una build de tienda (`ias-approvers-1`), después de enseñarlas con su huella y lo que cambia desde la versión firmada, y la sube (§5.2). `--dry-run` enseña la versión que firmaría y no firma ni sube. |
|
|
65
|
+
| `ias vault grant\|revoke <runner>` | Deja a un runner abrir las credenciales del equipo (la lista firmada y una envoltura en cada sobre) o se lo quita cuando el panel ya lo revocó (§5.2). `--dry-run` dice qué lista firmaría y qué sobres cambiaría, sin firmar, subir ni tocar un sobre. |
|
|
66
|
+
| `ias policy init\|check\|sign\|show\|status\|diff\|simulate` | La política firmada de quién aprueba cada build o envío y cuántos: `init` escribe `ias.policy.json` equivalente a hoy, `sign` la firma con la clave del equipo y pide una passkey, y el resto la comprueban, enseñan, comparan o simulan (§5.3). El fichero se da como argumento (`ias policy simulate ias.policy.json …`) o con `--file`; `check`, `show`, `status`, `diff` y `sign` aceptan `--item`. El servidor la aplica al crear y al aprobar trabajos de tienda, y el runner la verifica antes de abrirlos. |
|
|
67
|
+
| `ias credentials generate\|import\|list\|show\|rotate\|retire\|revoke` | Sella en este ordenador y sube las credenciales de firma, con sus metadatos sin secreto; las lista, enseña quién puede abrirlas y quién las firmó, las sustituye por una versión nueva y las retira (`retire` toma el `id` o el nombre, y `--dry-run` no retira). `generate ios` hace o reutiliza el certificado de distribución y el perfil de App Store de la app; `revoke` quita el certificado en Apple con la aprobación de un propietario y lo retira; `generate android_upload_keystore` crea la clave de subida de Android con `keytool` y la guarda en 1Password (§5). |
|
|
64
68
|
| `ias env list\|set\|pull\|delete` | Las variables de cada entorno. `EXPO_PUBLIC_*` siempre en claro; las demás, selladas salvo `--plain`. `pull` escribe `.env.local` sin los secretos. |
|
|
65
|
-
| `ias build [--platform ios\|android\|all] [--profile <perfil>] [--message "…"] [--wait] [--local]` | Manda el commit de un árbol limpio a un runner de IAS con el perfil de `ias.json`, abre la aprobación de una build de tienda y sigue su registro hasta que acaba; con `--wait` baja los artefactos. Con `--local`, la hace en este Mac con `@imagina-build/ias-runner` (§6). |
|
|
69
|
+
| `ias build [--platform ios\|android\|all] [--profile <perfil>] [--message "…"] [--wait] [--out <carpeta>] [--detach] [--local] [--credentials <op://…\|carpeta>]` | Manda el commit de un árbol limpio a un runner de IAS con el perfil de `ias.json`, abre la aprobación de una build de tienda y sigue su registro hasta que acaba; con `--wait` baja los artefactos. Con `--local`, la hace en este Mac con `@imagina-build/ias-runner` (§6). |
|
|
66
70
|
| `ias builds list\|view\|logs\|cancel\|download` | Las builds de la aplicación: la lista, una en detalle, su registro (`--follow`), cancelarla y bajar sus artefactos comprobados (§6). |
|
|
67
|
-
| `ias submit <build> [--track <pista>] [--wait]` | Manda una build de tienda a App Store Connect o a Google Play,
|
|
71
|
+
| `ias submit <build> [--track <pista>] [--release-status draft\|inProgress\|completed] [--rollout <1-99>] [--changes-not-sent-for-review] [--wait]` | Manda una build de tienda a App Store Connect o a Google Play, después de que un `owner` lo apruebe con su passkey (§6). En Play sale **como borrador** por defecto; `inProgress` con `--rollout` y `completed` solo si la política de la pista lo permite (docs/plans/gaps/03-google-play-rollout.md). `--track` acepta `internal`, `alpha`, `beta`, `production`, una pista cerrada o una de un factor de forma (`wear:production`). `--changes-not-sent-for-review` aplica el commit sin enviarlo a revisión: una persona lo envía desde Play Console, y hasta entonces no llega a testers ni a usuarios (con un borrador es redundante). |
|
|
72
|
+
| `ias releases list\|view <pista>\|policy <pista>` | `list` y `view` son solo lectura del estado de cada pista de Google Play (sin flags; `--refresh` encola un trabajo que solo lee Play y espera). `policy` enseña la política de la pista; con `--mode`, `--max-percent`, `--step` o `--min-minutes` la cambia un owner. |
|
|
73
|
+
| `ias rollout <pista> [--percent <1-99> \| --halt \| --resume [--percent <1-99>] \| --complete] [--version-code <n>] [--wait] [--dry-run]` | Mueve la release que ya está en una pista de Google Play: sin flags solo lee y dice el siguiente paso; cada acción es un trabajo con su passkey, sin artefacto. Nunca baja el porcentaje (para parar, `--halt`). |
|
|
74
|
+
| `ias testflight <build> [--groups <nombre>…] [--what-to-test "…"] [--locale <locale>] [--notes-file <json>] [--notify] [--wait] [--dry-run]` | Reparte a grupos de TestFlight una build que ya está en App Store Connect, con su «What to Test» y su aprobación; sin `--groups` usa `ias.json` › `submit.<perfil>.ios.testflight.groups` y, si no hay, lista los grupos que la app tiene y da el comando exacto (nunca crea uno) (§6). |
|
|
75
|
+
| `ias testflight distribute\|review\|status <build>` | `distribute` es la forma explícita de `ias testflight <build>`: lo mismo, con las mismas opciones. `review` envía la build a Beta App Review: es otro comando, con su propia passkey y su aviso, y solo hace falta para testers externos. `status` es solo lectura: procesamiento, grupos, idiomas y estado de la revisión (§6). |
|
|
68
76
|
| `ias version get\|set` | Los números de build que guarda el servidor por plataforma, los que toma `autoIncrement`; `set` los trae de EAS o de `app.json` y solo los deja subir (§6). |
|
|
69
77
|
| `ias push send\|messages\|cancel\|devices\|credentials` | Las notificaciones push: envía una ahora, la programa con `--at` o cuenta a quién llegaría con `--dry-run`; el historial con sus cifras; los dispositivos, con solo el principio y el final de su token; y las credenciales de APNs y FCM, que aprueba un propietario con su passkey (§5.1). |
|
|
70
78
|
| `ias devices list [--status <estado>]` | Los iPhone y iPad de quien prueba la app, con su estado (pendiente, aprobado, en Apple, rechazado), la versión del perfil que los lista y el enlace de registro (§6.1). |
|
|
71
|
-
| `ias devices sync [--yes] [--no-resign] [--detach]` | Da de alta en Apple los dispositivos aprobados, regenera el perfil ad hoc con todos ellos, lo sella y lo firma en la bóveda, y pide que un runner firme de nuevo la última build interna de iOS, sin compilarla (§6.1). |
|
|
72
|
-
| `ias webhooks list\|create\|delete\|test` | Los webhooks salientes de la aplicación: la lista, uno nuevo con sus eventos (`--events build.failed,device.pending` o `all`; imprime su secreto, una vez), borrarlo y mandarle una prueba. Cada evento llega como un POST firmado con `IAS-Signature` (docs/technical/manage-api.md §16.1). |
|
|
73
|
-
| `ias github link <owner/name>` | Vincula el repositorio de GitHub de la aplicación: sus etiquetas, ramas y pull requests lanzan los flujos de `ias.json`. La primera vez imprime lo que hay que poner en el webhook de GitHub (el secreto, una vez); el token de GitHub lo carga un propietario en el panel, con una passkey (§16.2). |
|
|
79
|
+
| `ias devices sync [--yes] [--force] [--no-resign] [--detach] [--dry-run]` | Da de alta en Apple los dispositivos aprobados, regenera el perfil ad hoc con todos ellos, lo sella y lo firma en la bóveda, y pide que un runner firme de nuevo la última build interna de iOS, sin compilarla (§6.1). `--dry-run` enseña los dispositivos, el perfil y la nueva firma que encolaría, sin llamar a Apple, escribir en la bóveda ni crear un trabajo. |
|
|
80
|
+
| `ias webhooks list\|create\|delete\|test` | Los webhooks salientes de la aplicación: la lista, uno nuevo con sus eventos (`--events build.failed,device.pending` o `all`; imprime su secreto, una vez), borrarlo y mandarle una prueba. `create --dry-run` y `delete --dry-run` dicen qué harían sin crear, borrar ni enseñar un secreto. Cada evento llega como un POST firmado con `IAS-Signature` (docs/technical/manage-api.md §16.1). |
|
|
81
|
+
| `ias github link <owner/name>` | Vincula el repositorio de GitHub de la aplicación: sus etiquetas, ramas y pull requests lanzan los flujos de `ias.json`. `--dry-run` dice qué vincularía, sin vincular ni enseñar el secreto. La primera vez imprime lo que hay que poner en el webhook de GitHub (el secreto, una vez); el token de GitHub lo carga un propietario en el panel, con una passkey (§16.2). |
|
|
74
82
|
| `ias workflows list\|run` | Los flujos de `ias.json` en un commit del repositorio, y lanzar uno a mano (`ias workflows run release --ref main [--wait]`) (§16.3). |
|
|
75
83
|
| `ias migrate eas [profiles\|prepare\|import <carpeta>\|version\|env\|checklist]` | Sale de EAS paso a paso: los perfiles de `eas.json` a `ias.json`, una carpeta fuera del proyecto para `eas credentials`, las credenciales selladas en la bóveda, los números de build y las variables de EAS en el servidor, y lo que queda por hacer en expo.dev y en las tiendas. Sin paso, dice en qué punto está (§8). |
|
|
84
|
+
| `ias tokens create\|list\|revoke` | Los tokens de robot de la aplicación para la CI: crea uno y lo guarda en 1Password (sin imprimirlo), los lista sin su valor y revoca uno por su `id` o su nombre (`--dry-run` no revoca). Un `owner` con token personal; la API ya no obliga a crearlo en el panel (§2.1). |
|
|
85
|
+
| `ias runners code` | Un código de alta de runner, de un solo uso y 30 minutos, con la orden para el Mac del runner. Un `owner`; después, `ias vault grant`. |
|
|
76
86
|
|
|
77
87
|
Cada comando tiene su ayuda: `ias update --help` o `ias update publish --help`. `--project <carpeta>` elige la app; si no, la carpeta actual.
|
|
78
88
|
|
|
@@ -84,6 +94,116 @@ Cada comando tiene su ayuda: `ias update --help` o `ias update publish --help`.
|
|
|
84
94
|
|
|
85
95
|
**Códigos de salida**: `0` si funciona, `1` si falla y `2` si la línea de órdenes no se entiende (un comando o una opción que no existen, o falta un argumento).
|
|
86
96
|
|
|
97
|
+
### 2.1 `ias setup`: la checklist de alta
|
|
98
|
+
|
|
99
|
+
`ias setup` lleva una app de Expo de cero a su primera build y su primera actualización. No es un asistente que
|
|
100
|
+
pregunta: es una lista de pasos que se comprueba contra los ficheros del proyecto y el servidor, así que se puede
|
|
101
|
+
repetir en cualquier momento y desde cualquier ordenador.
|
|
102
|
+
|
|
103
|
+
| Acción | Qué hace |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `ias setup` | El estado: cada paso con lo que ha encontrado, lo que falta y cuál es el siguiente. |
|
|
106
|
+
| `ias setup next` | Hace el siguiente paso automático, o explica qué tiene que hacer una persona (con el enlace del panel y el comando exacto). `--all` encadena los automáticos hasta el primero que necesita a alguien; `--open` abre su enlace. |
|
|
107
|
+
| `ias setup next --build <perfil>` | Hace la primera build con ese perfil (`preview` por ejemplo) y se detiene; es explícito por coste y cuota. |
|
|
108
|
+
| `ias setup next --publish` | Publica la primera actualización OTA después de una build correcta. |
|
|
109
|
+
| `ias setup check [paso]` | Vuelve a comprobar todo (o un paso) y sale con `1` mientras falte algo obligatorio. Es la puerta de la CI. |
|
|
110
|
+
| `ias setup services updates,distribution,push` | Los servicios de la app; se guardan para las siguientes ejecuciones. Sin argumentos, los enseña. |
|
|
111
|
+
| `ias setup reset` | Olvida el estado local. No borra nada del proyecto. |
|
|
112
|
+
|
|
113
|
+
Los pasos, en orden: `environment` (Node, `git`, `op`), `project` (`ias.json`), `login`, `application` (la crea una
|
|
114
|
+
persona en el panel; con `--create-app`, el CLI), `services` (crea el proyecto OTA y el de distribución, enciende push y
|
|
115
|
+
escribe su install key), `stores` (opcional: el bundle id, el app record y el paquete de Play que IAS guarda; nunca
|
|
116
|
+
llama a Apple), `sdk` (`ias sdk install` con los módulos de Expo de los servicios), `vault` (`ias vault init`),
|
|
117
|
+
`credentials` (`ias credentials generate ios` para iOS, `generate android_upload_keystore` para Android, e `ias credentials
|
|
118
|
+
import` para lo que venga de fuera), `signing` (opcional: comprueba, solo con las credenciales del servidor, si el
|
|
119
|
+
certificado de distribución y el perfil de App Store existen y son válidos, y propone `ias credentials generate ios`),
|
|
120
|
+
`runner` (crea el código de alta y espera al registro), `build`, `testflight` (opcional: si hay una build de tienda de iOS
|
|
121
|
+
en App Store Connect, propone `ias testflight <build>`; `--all` no lo ejecuta nunca), `updates`, `distribution`, `push`
|
|
122
|
+
y `ci`.
|
|
123
|
+
`stores`, `signing`, `testflight`, `push` y `ci` son opcionales; `runner` lo es con `--local-builds`. Un paso con servicio solo
|
|
124
|
+
cuenta si ese servicio está elegido.
|
|
125
|
+
|
|
126
|
+
- **Se deriva, no se recuerda.** Que `ias.json` exista, que `GET /vault` tenga sus claves o que haya una build
|
|
127
|
+
terminada se comprueba cada vez. Lo único local es un fichero de estado fuera del repositorio
|
|
128
|
+
(`$XDG_STATE_HOME/ias/setup/`, o `~/Library/Application Support/ias/setup/`), sin secretos: `ias build` exige un
|
|
129
|
+
árbol limpio y un `.ias/` en el proyecto lo ensuciaría.
|
|
130
|
+
- **Nada fuera de orden.** Un paso bloqueado no se ejecuta y lo dice: `[ ] sdk finish application first`.
|
|
131
|
+
- **Lo que ya existía se reutiliza.** `ias setup` llama a `ias init`, `ias sdk install`, `ias login` y `ias vault init`;
|
|
132
|
+
no cambia sus reglas ni sus salidas.
|
|
133
|
+
- **`--json`** devuelve `{ app, server, services, complete, counts, steps, next }`; cada paso trae `status` (`done`,
|
|
134
|
+
`ready`, `locked` u `optional`), `detail`, `fix`, `command` y `url` cuando los hay.
|
|
135
|
+
|
|
136
|
+
El plan y las decisiones están en [`docs/plans/PLAN-ONBOARDING.md`](../../docs/plans/PLAN-ONBOARDING.md). Con la Fase 2,
|
|
137
|
+
`services` crea el proyecto OTA y el de distribución (con su clave y su token en 1Password) y `runner` crea el código
|
|
138
|
+
de alta; el alta ya solo necesita a una persona para los ficheros de Apple y Google y para aprobar con passkey.
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
### 2.2 Alta de los proyectos, de la clave y de los tokens
|
|
142
|
+
|
|
143
|
+
La Fase 2 quita artisan y el panel del alta. Un `owner` con su token personal (no un token de robot) crea la
|
|
144
|
+
aplicación, sus proyectos y sus tokens; el CLI lo guarda en 1Password y deja en `ias.json` solo la referencia `op://`:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
ias apps --create --code mi-app --name "Mi app" --services updates,distribution,push
|
|
148
|
+
ias apps mi-app --services updates,distribution,push # cambia los servicios; no apaga uno con piezas
|
|
149
|
+
ias apps store mi-app # la identidad en App Store Connect; si falta el app record, el formulario y el comando
|
|
150
|
+
ias update init # clave RSA + certificado, proyecto OTA, token
|
|
151
|
+
ias dist init # proyecto de distribución y su token
|
|
152
|
+
ias credentials generate ios # certificado de distribución y perfil de App Store (reutiliza lo válido)
|
|
153
|
+
ias credentials generate android_upload_keystore # clave de subida de Android
|
|
154
|
+
ias tokens create --name "GitHub Actions" --scopes apps:read,builds:create --days 90
|
|
155
|
+
ias runners code # un código de alta de runner, para ias-runner register
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
- **`ias apps --create`** crea la aplicación con el código, el nombre y los identificadores que se le den (o los de
|
|
159
|
+
`app.json` desde `ias setup --create-app`). `--services` enciende servicios. No crea secretos.
|
|
160
|
+
- **`ias update init`** hace la clave RSA y un certificado autofirmado de 10 años en `certs/ota-certificate.pem` (va en
|
|
161
|
+
git), crea el proyecto con `POST /apps/<app>/updates` y guarda la clave y el token en el ítem de 1Password
|
|
162
|
+
(`op://IAS/<app> OTA` por defecto). Nunca imprime la clave ni el token; con `--dry-run` no hace nada. Si el
|
|
163
|
+
proyecto ya existe con el mismo certificado, no lo toca ni rota el token. Si existe con otro, para: rotar el
|
|
164
|
+
certificado pide una build nueva.
|
|
165
|
+
- **`ias dist init`** hace lo mismo con el proyecto de distribución y su token (`op://IAS/<app> distribution`).
|
|
166
|
+
- **`ias credentials generate android_upload_keystore`** hace un PKCS12 con contraseñas aleatorias con `keytool`, lo
|
|
167
|
+
guarda como adjunto de `op://IAS/<app> Android upload` (con las contraseñas en campos `CONCEALED`) y lo sella en la
|
|
168
|
+
bóveda por el mismo camino que `ias credentials import`.
|
|
169
|
+
- **`ias credentials generate ios`** hace lo mismo con la firma de iOS, en una sola orden: reutiliza el certificado de
|
|
170
|
+
distribución del equipo que la bóveda tiene y Apple sigue listando, y el perfil de App Store del bundle id que lo
|
|
171
|
+
contiene; si faltan, crea aquí la clave RSA-2048 y el CSR, pide el certificado a Apple con la clave de App Store
|
|
172
|
+
Connect de la bóveda, monta y valida el `.p12`, lo sella y deja la copia de recuperación en
|
|
173
|
+
`op://IAS/<equipo> Apple Distribution <serie>`. El perfil de tienda se sella sin dispositivos (`devices: 0`). Si hay
|
|
174
|
+
varios certificados pide `--certificate <sha1|id>`; `--rotate` hace uno nuevo **con la aprobación de un propietario**
|
|
175
|
+
(conserva el `id` de la credencial y sube la versión; Apple no deja tener dos y el comando no revoca nada salvo que se
|
|
176
|
+
le pida con `revoke`); `--if-expiring <días>` decide cuándo renovar; `--no-profiles` deja el perfil de App Store para
|
|
177
|
+
después y la salida dice el comando que falta (`ias credentials generate ios`); `--dry-run` no escribe nada ni pide
|
|
178
|
+
aprobaciones. Si Apple tiene **varios perfiles de App Store utilizables** del bundle id, no elige: los lista y pide
|
|
179
|
+
`--profile <uuid|nombre>`; con uno válido lo reutiliza. `--sync-capabilities` habilita en el App ID lo que pide
|
|
180
|
+
`app.json` › `expo › ios › entitlements` (solo lo que falta, nunca deshabilita) y avisa de que Apple invalida todos
|
|
181
|
+
los perfiles del App ID, el ad hoc incluido: después, `ias devices sync --force`. `--delete-previous` borra en Apple,
|
|
182
|
+
tras sellar y subir el nuevo, el perfil de App Store que este sustituye (nunca el ad hoc, ni el de otra credencial ni en un
|
|
183
|
+
`--dry-run`), preguntando antes. Los comandos `generate apple_distribution_certificate` y `generate apple_provisioning_profile` repiten
|
|
184
|
+
solo una parte; el ad hoc sigue siendo de `ias devices sync`.
|
|
185
|
+
- **`ias credentials revoke <id>`** es para un certificado comprometido, no para renovar. Lo hace un `owner`: lee Apple,
|
|
186
|
+
enseña la serie, el SHA-1, los perfiles y las apps afectadas, pide la aprobación con passkey, y **solo entonces**
|
|
187
|
+
hace `DELETE /v1/certificates/{id}` y retira la credencial. Un rechazo o una caducidad no borran nada en Apple; si el
|
|
188
|
+
certificado ya no está, retira igual y lo dice. Después hay que regenerar los perfiles con
|
|
189
|
+
`ias credentials generate ios` (y `ias devices sync --force` para las builds internas). `--json` da un documento
|
|
190
|
+
estable, `--dry-run` solo el inventario.
|
|
191
|
+
- **`ias tokens create`** crea un token de robot (`iasr_…`) con sus alcances y días y lo deja en
|
|
192
|
+
`op://IAS/<app> CI`; `list` los enseña sin su valor y `revoke` los mata. En la CI, `IAS_TOKEN` se lee de ese ítem.
|
|
193
|
+
- **`ias runners code`** pide el código de alta (30 minutos, un solo uso) y enseña la orden para el Mac del runner; el
|
|
194
|
+
runner queda pendiente hasta `ias vault grant`.
|
|
195
|
+
|
|
196
|
+
**Nombres en 1Password.** Los ítems que crea el CLI van al vault `IAS` (se puede cambiar con `--item`), con el código
|
|
197
|
+
de la app y un propósito: `<app> OTA`, `<app> distribution`, `<app> CI`, `<app> Android upload`. Los campos nuevos son
|
|
198
|
+
kebab-case (`private-key`, `certificate`, `key-id`, `token`, `project`, `server`; `keystore`, `store-password`,
|
|
199
|
+
`key-alias`, `key-password`). Los ítems y referencias de antes (`op://Private/...`, `private-key.pem`, `credential`)
|
|
200
|
+
siguen leyéndose: no se renombra nada. La bóveda del equipo también usa `op://IAS/IAS vault <host>` por defecto; si ya
|
|
201
|
+
existe en `Private`, se apunta con `vault.item`, `IAS_VAULT_ITEM` o `--item`.
|
|
202
|
+
|
|
203
|
+
Nada de esto imprime un secreto: la clave, las contraseñas y los tokens viajan por la entrada estándar de `op` y se leen
|
|
204
|
+
con `op read`. El servidor solo recibe el certificado público; el registro de acciones guarda el proyecto y el keyid,
|
|
205
|
+
nunca el token.
|
|
206
|
+
|
|
87
207
|
## 3. `ias.json`
|
|
88
208
|
|
|
89
209
|
Va en la raíz de la app, junto a `app.json`, y en git: no lleva secretos. Su esquema es [`schema/ias.schema.json`](schema/ias.schema.json) (JSON Schema 2020-12), que también usarán el runner y el SDK. `src/schema.mjs` lo valida sin dependencias.
|
|
@@ -112,12 +232,12 @@ Va en la raíz de la app, junto a `app.json`, y en git: no lleva secretos. Su es
|
|
|
112
232
|
| Campo | Qué es |
|
|
113
233
|
| --- | --- |
|
|
114
234
|
| `app` | El código de la aplicación en IAS (`apps:create`). También es el del proyecto de distribución, salvo que `distribution.project` diga otro. |
|
|
115
|
-
| `server` | El origen de IAS. Por defecto, `https://ias.imagina.build`, que todavía
|
|
235
|
+
| `server` | El origen de IAS. Por defecto, `https://ias.imagina.build`, ya en servicio. Mientras dure la migración de dominio, `sync.domibi.com` responde la misma API sin redirección, así que sirve cualquiera de los dos; la aprobación del login (§4) se hace en el panel del dominio de `APP_URL`, todavía `sync.domibi.com`. Las OTA van al servidor de `updates.url` de `app.json`, que es al que preguntan las builds; sin ella, al de este campo (§3.2). |
|
|
116
236
|
| `build`, `submit` | Los perfiles de `eas.json`, sin credenciales. La Fase 2 (`ias build`) los completa. |
|
|
117
237
|
| `updates` | La firma del canal por defecto (`keyId`, `certificate` y `privateKey`), el `token` del proyecto OTA, `envFile` y `bundleEnv`. El servidor, el proyecto, el certificado y el keyid salen de `app.json` o, si no los tiene, de `ias.json`, como los escribe el plugin de `@imagina-build/ias` (§3.2). `channels`, la firma de los demás canales (§3.1). |
|
|
118
238
|
| `distribution` | El `token` del proyecto de distribución y, si no es el de `app`, su `project`. |
|
|
119
239
|
| `workflows` | Lo que lanza GitHub (una etiqueta, una rama o un pull request) y en qué pasos: `build` y `submit` (§3.0). |
|
|
120
|
-
| `vault` | `item`: el ítem de 1Password de la bóveda (`op://<bóveda>/<ítem>`), si no es `op://
|
|
240
|
+
| `vault` | `item`: el ítem de 1Password de la bóveda (`op://<bóveda>/<ítem>`), si no es `op://IAS/IAS vault <host del servidor>`. |
|
|
121
241
|
|
|
122
242
|
### 3.0 Flujos (`workflows`)
|
|
123
243
|
|
|
@@ -211,7 +331,7 @@ Con el plugin de `@imagina-build/ias` en los `plugins` de `app.json` (`packages/
|
|
|
211
331
|
|
|
212
332
|
El material de firma se sella **en este ordenador** con `ias-vault-2` ([docs/technical/vault.md](../../docs/technical/vault.md)): para la clave de recuperación del equipo y para los runners de la lista que firmó el equipo, y firmado con la clave de firma del equipo. El servidor guarda el sobre y los metadatos, y no puede abrirlo ni firmarlo.
|
|
213
333
|
|
|
214
|
-
**Las claves del equipo** viven en un ítem de 1Password (`vault.item`, `IAS_VAULT_ITEM` o `--item`; por defecto `op://
|
|
334
|
+
**Las claves del equipo** viven en un ítem de 1Password (`vault.item`, `IAS_VAULT_ITEM` o `--item`; por defecto `op://IAS/IAS vault <host>`), con los campos de docs/technical/vault.md §2.2.
|
|
215
335
|
- `ias vault init` (un `owner`) las crea con `node:crypto`, crea el ítem con `op item create` pasándole la plantilla por stdin, y registra las públicas (`POST /vault/keys`). El servidor responde `202 approval_required`: el CLI abre la aprobación, enseña los dos `kid` para compararlos con los del panel y pregunta cada 3 s hasta que se aprueba. Si el ítem ya existe (un intento anterior sin aprobar), registra sus claves.
|
|
216
336
|
- `ias vault status` lee solo las públicas de 1Password, las compara con las del servidor y verifica la lista firmada de runners. Sale con 1 si algo no cuadra.
|
|
217
337
|
|
|
@@ -235,7 +355,7 @@ El material de firma se sella **en este ordenador** con `ias-vault-2` ([docs/tec
|
|
|
235
355
|
- El CLI saca los metadatos con `@imagina-build/ias-shared/credentials` (`metadataOf`), genera el `id` (un UUID), sella el texto en claro del tipo (docs/technical/vault.md §3.5) y sube `{ id, type, name, metadata, envelope }` a `/apps/<app>/credentials` o, si es del equipo, a `/team/credentials`.
|
|
236
356
|
- Una credencial que ya está (el mismo certificado, perfil o clave) se salta; `ias credentials rotate <id>` la sustituye por la versión siguiente. `--dry-run` lo hace todo menos subirla.
|
|
237
357
|
|
|
238
|
-
`ias credentials list` (con `--all`, también las retiradas), `show <id>` (con `--verify`, comprueba la firma con la clave de 1Password), `rotate <id>`
|
|
358
|
+
`ias credentials list` (con `--all`, también las retiradas), `show <id>` (con `--verify`, comprueba la firma con la clave de 1Password), `rotate <id>` (si cambia el SHA-1 de un certificado, pide la aprobación de un `owner`), `retire <id>` y `revoke <id>` (Apple y retirada, con passkey).
|
|
239
359
|
|
|
240
360
|
**`ias env`**:
|
|
241
361
|
- `ias env set NOMBRE [valor] --environment <entorno>`: `EXPO_PUBLIC_*` siempre en claro (y `--secret` da error). Cualquier otra se sella como `environment_variable` salvo `--plain`, y su valor llega con `--from op://…|env:NOMBRE|-`, nunca en la línea de órdenes. Una secreta que ya existe conserva su `id` y sube de versión.
|
|
@@ -268,6 +388,25 @@ Un runner solo se fía de la clave de firma del equipo y del panel que fijó al
|
|
|
268
388
|
|
|
269
389
|
Ninguno imprime ni escribe una clave privada: la de firma y la de recuperación se leen con `op read` a memoria.
|
|
270
390
|
|
|
391
|
+
### 5.3 La política de aprobación (`ias policy`)
|
|
392
|
+
|
|
393
|
+
Qué passkeys pueden aprobar cada build de tienda y cada envío, y cuántas, por aplicación, plataforma, perfil, tipo y, en un envío, tienda y pista ([docs/technical/vault.md](../../docs/technical/vault.md) §2.5). La fuente es **`ias.policy.json`**, versionable en el repositorio; la firma se hace aquí, con la clave del equipo de 1Password, y el servidor solo la guarda y la verifica.
|
|
394
|
+
|
|
395
|
+
| Comando | Qué hace |
|
|
396
|
+
| --- | --- |
|
|
397
|
+
| `ias policy init [--out <fichero>] [--version <n>] [--not-after <fecha>] [--force]` | Lee `GET /team/approvers` y escribe una política **equivalente a hoy**: una regla comodín (`app`, `platform`, `profile`, `type`, `target` y `track` en `*`), `count: 1` y todos los owners con passkey. La versión es la siguiente a la del servidor y caduca en 180 días, salvo que se diga otra cosa. No firma. |
|
|
398
|
+
| `ias policy check [fichero]` | Valida forma y semántica, verifica la firma si la tiene, y compara versión, miembros (cada uno necesita passkey en el `ias-approvers-1` vigente), fecha y cobertura con el servidor. Sale con 1 si hay algo que arreglar y dice el comando o la edición exactos. |
|
|
399
|
+
| `ias policy sign [fichero] [--item <op://…>] [--yes]` | Enseña versión, caducidad, hash, diff y reglas; pide confirmación; lee la clave privada del ítem de la bóveda (solo en memoria), firma el texto canónico y hace `PUT /team/policy` con el documento firmado. El servidor responde 202 con una aprobación `vault.policy.upload`: el CLI abre el panel y espera a que un `owner` compare el hash y apruebe con su passkey. Nunca imprime la clave. |
|
|
400
|
+
| `ias policy show [--item <op://…>]` | La política del servidor, verificada con la clave del equipo (comparada con 1Password cuando está), con versión, caducidad, hash y cada regla con sus miembros. |
|
|
401
|
+
| `ias policy status [--item <op://…>]` | Si hay política, si la firma verifica, cuándo caduca, qué reglas no tienen bastantes passkeys, qué combinaciones de app y perfil no cubre y si `ias.policy.json` es la del servidor. Sale con 1 solo si la del servidor no verifica. |
|
|
402
|
+
| `ias policy diff [fichero]` | Las reglas que entran, cambian o salen y los miembros que entran o salen, entre la política del servidor y el fichero. |
|
|
403
|
+
| `ias policy simulate [fichero] --platform <ios\|android> --profile <perfil> [--app <código>] [--type build\|submit\|testflight\|release] [--target …] [--track …]` | Lo que decidiría el fichero local: la regla que gana (la más específica) y quiénes pueden aprobar, o la denegación. El fichero va como argumento o con `--file` (la forma de antes). Sale con 1 si ninguna regla casa. |
|
|
404
|
+
|
|
405
|
+
- **Matching**: casa una regla cuyos seis campos sean `*` o el valor del trabajo; gana la de más campos concretos. Dos reglas que empaten hacen inválido el documento. Sin regla, se deniega: no hay regla implícita.
|
|
406
|
+
- **Se aplica desde ahora.** El servidor guarda, verifica y sirve la política, y la aplica al crear un trabajo de tienda (`409 policy_unmatched` antes de la URL de subida) y al aprobarlo (solo los miembros de la regla, y el trabajo encola al llegar a `count` personas distintas). Si la política rota, los trabajos que esperaban caducan con `policy_changed`; `JobDispatcher` solo da un trabajo con política a un runner con `capabilities.policy >= 1`, y el runner verifica la firma, la versión, la caducidad, la regla y las N aserciones antes de descargar código. `ias build`, `ias submit`, `ias testflight` y `ias rollout` enseñan la regla y el progreso («policy ios-production · 1 of 2»). Sigue sin haber delegación (`ias-approvers-2`), aprobación por lotes ni política para OTA, y un equipo sin política se comporta exactamente como antes: `ias setup` no la pide y `ias setup next --all` no la firma.
|
|
407
|
+
- **Solo sube la versión.** Volver a una política anterior exige una versión mayor con el contenido anterior. `sign` avisa y no firma una versión que no suba; el servidor la rechaza con `409 policy_version_backwards`.
|
|
408
|
+
- **Con `--json`**, todos devuelven un documento estable (`file`, `version`, `hash`, `problems`, `added`, `allowed`…), sin claves.
|
|
409
|
+
|
|
271
410
|
## 6. Las builds y los envíos a las tiendas
|
|
272
411
|
|
|
273
412
|
Con el contrato de [docs/technical/runner-api.md](../../docs/technical/runner-api.md) §4 y §6. Una build de tienda necesita un token personal (`ias login`) y la aprobación de un `owner` con su passkey; una interna vale también con un token de robot con `builds:create`.
|
|
@@ -284,10 +423,20 @@ Con el contrato de [docs/technical/runner-api.md](../../docs/technical/runner-ap
|
|
|
284
423
|
|
|
285
424
|
**`ias builds`**: `list [--status <estado>] [--platform]`, `view <id>`, `logs <id> [--follow]`, `cancel <id>` (quien la lanzó o un `owner`) y `download <id> [--kind ipa,dsyms] [--out <carpeta>]`. El registro es el que mandó el runner, ya limpio de secretos.
|
|
286
425
|
|
|
287
|
-
**`ias submit <build>`**: una build de tienda que acabó bien va a App Store Connect (con el Apple ID de `--asc-app-id
|
|
426
|
+
**`ias submit <build>`**: una build de tienda que acabó bien va a App Store Connect (con el Apple ID de `--asc-app-id`, de `submit.<perfil>.ios.ascAppId` o del que guardó `ias apps store --check` en la aplicación) o a Google Play (la pista de `--track`, de `submit.<perfil>.android.track` o `internal`), **como borrador por defecto** (D7, acotada por el plan 03): la revisión se pide en la tienda. En Android, `--release-status inProgress --rollout <1-99>` sirve un porcentaje y `--release-status completed` la sirve a todos, solo si la política de la pista lo permite; sin política, el servidor responde `release_policy_required` con el comando. `ias.json` › `submit.<perfil>.android.releaseStatus|rollout` propone lo mismo y cae a borrador con un aviso si la política no lo permite (un flag explícito, no). La aprueba un `owner` con su passkey y la manda el runner con las claves de la bóveda. Sin ningún Apple ID, `ias submit` falla antes de crear el envío con `asc_app_missing` y el comando que lo arregla. `--wait` espera a que acabe y dice qué respondió la tienda.
|
|
288
427
|
|
|
289
428
|
**`ias version get|set`**: los últimos números de build por plataforma. `set --ios <n> --android <n>` o `set --from-app-json` (un `owner`) los fija, por ejemplo al traerlos de EAS; solo suben.
|
|
290
429
|
|
|
430
|
+
**`ias releases` y `ias rollout`** (contrato en [docs/technical/manage-api.md](../../docs/technical/manage-api.md) §19 y [docs/technical/runner-api.md](../../docs/technical/runner-api.md) §6.2): el estado de cada pista de Google Play, su política y el movimiento de una release que ya está allí. `ias releases list` y `ias releases view <pista>` son **solo lectura** y no piden flags; `--refresh` encola un trabajo `release_status` que solo lee Play (sin passkey) y espera. `ias releases policy <pista>` enseña la política y, con `--mode`/`--max-percent`/`--step`/`--min-minutes`, la cambia un `owner` (el panel edita la misma). `ias rollout <pista>` sin flags solo lee y dice el siguiente paso; `--percent <1-99>`, `--halt`, `--resume [--percent]` y `--complete` crean un trabajo `release` con su passkey y **sin artefacto**, con el `versionCode` en el texto canónico. La política (techo, paso y espera) vive en el servidor; repetir el estado actual responde `{changed:false}` sin trabajo.
|
|
431
|
+
|
|
432
|
+
**`ias testflight`** (contrato en [docs/technical/manage-api.md](../../docs/technical/manage-api.md) §18 y [docs/technical/runner-api.md](../../docs/technical/runner-api.md) §6.1): después de `ias submit --wait` en iOS, el build está en App Store Connect y Apple lo procesa. `ias testflight <build>` lo reparte a grupos de TestFlight que ya existen:
|
|
433
|
+
- los grupos salen de `--groups` o de `submit.<perfil>.ios.testflight.groups` de `ias.json`; si no hay ninguno, el comando lee el estado, lista los grupos que la app tiene y da el comando exacto, sin crear nada;
|
|
434
|
+
- el «What to Test» sale de `--what-to-test` con `--locale` (`es-ES` por defecto), de `--notes-file` (varios idiomas) o del mensaje de la build;
|
|
435
|
+
- `--notify` avisa a los testers (apagado por defecto), `--dry-run` enseña el plan y no crea nada, y `--wait` sigue el trabajo;
|
|
436
|
+
- el plan se aprueba con passkey y el runner lo aplica con la clave de App Store Connect de la bóveda. El servidor no ve el `.p8`.
|
|
437
|
+
|
|
438
|
+
`ias testflight review <build>` es **otro comando y otra aprobación**: envía el build a Beta App Review, que solo necesitan los testers externos y que Apple puede rechazar. `ias testflight status <build>` solo lee: procesamiento (`PROCESSING`, `VALID`, `FAILED`), grupos, idiomas con notas, aviso y estado de la revisión. Los grupos, las notas y el estado viven en `testflight_builds`; los límites son de Apple (100 testers internos, 10.000 externos, 90 días por build, 6 envíos a revisión cada 24 h). Distribuir no envía a revisión: son dos decisiones.
|
|
439
|
+
|
|
291
440
|
### 6.1 Los dispositivos de quien prueba y las nuevas firmas
|
|
292
441
|
|
|
293
442
|
Con el contrato de [docs/technical/manage-api.md](../../docs/technical/manage-api.md) §15 y [docs/technical/runner-api.md](../../docs/technical/runner-api.md) §11. Es lo que hacen `eas device:create` y `eas build:resign`.
|
|
@@ -367,6 +516,11 @@ Después, `ias migrate eas prepare`; en la carpeta que imprime, `EAS_NO_VCS=1 ea
|
|
|
367
516
|
- `src/cli.mjs`: el enrutador.
|
|
368
517
|
- Cada comando es una carpeta de `src/commands/` con un `index.mjs`, así que uno nuevo no toca el enrutador.
|
|
369
518
|
- `vault`, `credentials` y `env` son de la pista 1D; `push`, de la 3B; `migrate`, de la 4D (`project.mjs`: la app y la carpeta de `prepare`; `eas-cli.mjs`: lo que se ejecuta y se lee de eas-cli; un fichero por paso); `build`, `builds`, `submit`, `version` y `vault approvers|grant|revoke`, de la 2C; `devices`, de la 4B (`asc.mjs`: la API de App Store Connect; `sync.mjs`: `ias devices sync`). `build/follow.mjs` (seguir y bajar), `build/source.mjs` (el commit y su tarball) y `build/profile.mjs` (el perfil resuelto) los comparten `build`, `builds` y `submit`; `vault/team.mjs`, los tres de confianza.
|
|
519
|
+
- `src/setup.mjs` y `src/setup-state.mjs`: la checklist, sus pasos y el estado local (fuera del repositorio, sin
|
|
520
|
+
secretos). `src/provisioning.mjs`: el alta de los proyectos OTA y de distribución, la clave y los tokens;
|
|
521
|
+
`src/ota-key.mjs` hace la clave RSA y el certificado, y `src/op.mjs` los ítems de 1Password.
|
|
522
|
+
- `src/commands/init/`, `src/commands/doctor/`, `src/commands/sdk/` y `src/commands/setup/`: la configuración, las
|
|
523
|
+
comprobaciones y la checklist de alta.
|
|
370
524
|
- `src/config.mjs`: carga `ias.json` o los ficheros viejos.
|
|
371
525
|
- `src/schema.mjs` y `schema/ias.schema.json`: el esquema y su validador.
|
|
372
526
|
- `src/ota.mjs` y `src/dist.mjs`: lo que no necesita red. `src/http.mjs`: las peticiones.
|
|
@@ -389,6 +543,15 @@ No usan red real, claves reales ni 1Password: el certificado lo crea `openssl` e
|
|
|
389
543
|
- `ias update` y `ias dist`, con las pruebas de `tools/` portadas;
|
|
390
544
|
- `ias.json`: el esquema, el cargador y sus negativas, y lo que `ias update` toma de él cuando `app.json` lo deja al plugin de `@imagina-build/ias` (`test/config.test.mjs`);
|
|
391
545
|
- `ias init` con la configuración de jscheduler-app y jero-invoices-app;
|
|
546
|
+
- `ias setup` (`test/setup.test.mjs`): el estado fuera del repositorio, los servicios, la checklist completa, el bloqueo
|
|
547
|
+
de un paso mientras el anterior no está hecho, `ias init` dentro de `next`, los módulos de Expo del SDK, el paso
|
|
548
|
+
`services` creando el proyecto OTA con la clave y el token en 1Password, y las salidas para personas y `--json`;
|
|
549
|
+
- `ias apps --create` y `--services` (`test/applications.test.mjs`), y `ensurePush` escribiendo `push.installKey`;
|
|
550
|
+
- `ias update init`, `ias dist init`, `ias credentials generate android_upload_keystore`, `ias tokens create` y `ias runners code` (`test/provisioning.test.mjs`): la clave,
|
|
551
|
+
el certificado, el proyecto, el ítem de 1Password, `ias.json`, la idempotencia y `--dry-run`, sin que la clave ni el
|
|
552
|
+
token salgan en una salida ni en un argumento de `op`;
|
|
553
|
+
- la skill de `.claude/skills/ias` (`test/skill.test.mjs`): su referencia de comandos es la que genera
|
|
554
|
+
`scripts/sync-cli-reference.mjs` a partir de `loadCommands`, y todos sus enlaces existen;
|
|
392
555
|
- `ias doctor` con programas simulados;
|
|
393
556
|
- `ias sdk install` y el SDK en `ias doctor` (`test/sdk.test.mjs`): el gestor de cada lockfile, de dónde sale el paquete, el plugin en `app.json` sin perder lo que había, lo que falta y cada estado del SDK, con un gestor de paquetes simulado;
|
|
394
557
|
- los `.tgz` del CLI y del runner y la copia que se publica (`test/pack.test.mjs`): llevan `shared/` (el runner, también el validador de `ias.json`), funcionan sin el repositorio al lado, `ias build --local` encuentra el runner instalado junto al CLI, y la copia sale pública y sin `private`;
|
package/package.json
CHANGED
package/schema/ias.schema.json
CHANGED
|
@@ -491,13 +491,57 @@
|
|
|
491
491
|
"additionalProperties": false,
|
|
492
492
|
"properties": {
|
|
493
493
|
"ascAppId": {
|
|
494
|
-
"description": "The Apple ID of the app in App Store Connect.",
|
|
494
|
+
"description": "The Apple ID of the app in App Store Connect. It wins over the one saved in the application (ias apps store --check); leave it out to use that one.",
|
|
495
495
|
"type": "string",
|
|
496
496
|
"pattern": "^[0-9]+$"
|
|
497
497
|
},
|
|
498
498
|
"appleTeamId": {
|
|
499
499
|
"type": "string",
|
|
500
500
|
"pattern": "^[A-Z0-9]{10}$"
|
|
501
|
+
},
|
|
502
|
+
"testflight": {
|
|
503
|
+
"description": "TestFlight after the submission (docs/plans/gaps/04-testflight.md §3.6): the groups ias testflight distributes to when no --groups is given, the «What to Test» by locale, and whether the testers are notified. Beta App Review is never sent by this: it is ias testflight review, with its own approval.",
|
|
504
|
+
"type": "object",
|
|
505
|
+
"additionalProperties": false,
|
|
506
|
+
"properties": {
|
|
507
|
+
"groups": {
|
|
508
|
+
"description": "The TestFlight groups by name, at most 20. They must exist in App Store Connect: IAS never creates one.",
|
|
509
|
+
"type": "array",
|
|
510
|
+
"maxItems": 20,
|
|
511
|
+
"uniqueItems": true,
|
|
512
|
+
"items": {
|
|
513
|
+
"type": "string",
|
|
514
|
+
"minLength": 1,
|
|
515
|
+
"maxLength": 100
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
"notes": {
|
|
519
|
+
"description": "The «What to Test» by locale: at most 8 locales of App Store Connect and 4000 characters each.",
|
|
520
|
+
"type": "object",
|
|
521
|
+
"maxProperties": 8,
|
|
522
|
+
"propertyNames": {
|
|
523
|
+
"enum": [
|
|
524
|
+
"ar-SA", "ca", "cs", "da", "de-DE", "el", "en-AU", "en-CA", "en-GB", "en-US",
|
|
525
|
+
"es-ES", "es-MX", "fi", "fr-CA", "fr-FR", "he", "hi", "hr", "hu", "id",
|
|
526
|
+
"it", "ja", "ko", "ms", "nl-NL", "no", "pl", "pt-BR", "pt-PT", "ro",
|
|
527
|
+
"ru", "sk", "sv", "th", "tr", "uk", "vi", "zh-Hans", "zh-Hant"
|
|
528
|
+
]
|
|
529
|
+
},
|
|
530
|
+
"additionalProperties": {
|
|
531
|
+
"type": "string",
|
|
532
|
+
"minLength": 1,
|
|
533
|
+
"maxLength": 4000
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
"notify": {
|
|
537
|
+
"description": "Notify the testers when the build is added (off by default).",
|
|
538
|
+
"type": "boolean"
|
|
539
|
+
},
|
|
540
|
+
"betaReview": {
|
|
541
|
+
"description": "Advisory only: whether external testers are expected. It never sends the build to Beta App Review; that is ias testflight review.",
|
|
542
|
+
"type": "boolean"
|
|
543
|
+
}
|
|
544
|
+
}
|
|
501
545
|
}
|
|
502
546
|
}
|
|
503
547
|
},
|
|
@@ -525,6 +569,7 @@
|
|
|
525
569
|
"maximum": 1
|
|
526
570
|
},
|
|
527
571
|
"changesNotSentForReview": {
|
|
572
|
+
"description": "Google Play: apply the commit without sending the release for review; a person sends it from Play Console (off by default).",
|
|
528
573
|
"type": "boolean"
|
|
529
574
|
}
|
|
530
575
|
}
|
package/shared/vault/index.mjs
CHANGED
|
@@ -581,3 +581,28 @@ export { PLAINTEXT_TYPES, PlaintextError, decodePlaintext, encodePlaintext, vali
|
|
|
581
581
|
// The passkeys that may approve a store job, signed by the team with the same key (docs/technical/vault.md §2.4). approvers.mjs
|
|
582
582
|
// imports this module; it only defines functions at its top level, so the cycle is safe either way it is entered.
|
|
583
583
|
export { APPROVERS_FORMAT, APPROVER_ALGORITHMS, APPROVER_LIMITS, approverAlgorithm, approverFingerprint, approversText, signApprovers, validateApprovers, verifyApprovers } from './approvers.mjs';
|
|
584
|
+
// The signed approval policy (ias-policy-1, docs/technical/vault.md §2.5): which passkeys approve which job and how
|
|
585
|
+
// many. policy.mjs imports this module; it only defines functions at its top level, so the cycle is safe either way.
|
|
586
|
+
export {
|
|
587
|
+
POLICY_FORMAT,
|
|
588
|
+
POLICY_LIMITS,
|
|
589
|
+
POLICY_FIELDS,
|
|
590
|
+
POLICY_PLATFORMS,
|
|
591
|
+
POLICY_TARGETS,
|
|
592
|
+
POLICY_TYPES,
|
|
593
|
+
DRAFT_SIGNATURE,
|
|
594
|
+
PLAY_TRACK,
|
|
595
|
+
isNotAfter,
|
|
596
|
+
jobOf,
|
|
597
|
+
policyHash,
|
|
598
|
+
policyText,
|
|
599
|
+
ruleAndSpecificityFor,
|
|
600
|
+
ruleFor,
|
|
601
|
+
signPolicy,
|
|
602
|
+
simulate,
|
|
603
|
+
specificityOf,
|
|
604
|
+
summary,
|
|
605
|
+
validatePolicy,
|
|
606
|
+
validatePolicyDraft,
|
|
607
|
+
verifyPolicy,
|
|
608
|
+
} from './policy.mjs';
|