@imagina-build/ias-cli 0.1.0 → 0.1.2
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 +13 -13
- package/package.json +1 -1
- package/schema/ias.schema.json +5 -5
- package/src/commands/build/index.mjs +1 -1
- package/src/commands/build/local.mjs +2 -2
- package/src/commands/credentials/index.mjs +1 -1
- package/src/commands/doctor/index.mjs +2 -2
- package/src/commands/init/index.mjs +13 -10
- package/src/commands/sdk/index.mjs +3 -3
- package/src/config.mjs +6 -6
- package/src/sdk.mjs +7 -13
- package/src/shared.mjs +1 -1
- package/src/team-vault.mjs +9 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# `ias`: el CLI de Imagina Application Services
|
|
2
2
|
|
|
3
|
-
`ias` da a cada app acceso a IAS desde la terminal. Es el paquete `@imagina/ias-cli` (pista 1B de [PLAN-APPLICATION-SERVICES.md](../../docs/plans/PLAN-APPLICATION-SERVICES.md)):
|
|
3
|
+
`ias` da a cada app acceso a IAS desde la terminal. Es el paquete `@imagina-build/ias-cli` (pista 1B de [PLAN-APPLICATION-SERVICES.md](../../docs/plans/PLAN-APPLICATION-SERVICES.md)):
|
|
4
4
|
|
|
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`;
|
|
@@ -23,7 +23,7 @@ Para tenerlo como `ias` en el Mac:
|
|
|
23
23
|
npm install --global ./packages/cli
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Sin el repositorio, desde npm, donde 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)).
|
|
26
|
+
Sin el repositorio, desde npm, donde 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
27
|
|
|
28
28
|
```bash
|
|
29
29
|
npm install --global @imagina-build/ias-cli
|
|
@@ -44,8 +44,8 @@ Fuera de un clon, `ias build --local` necesita `IAS_RUNNER_MODULE`.
|
|
|
44
44
|
| Comando | Qué hace |
|
|
45
45
|
| --- | --- |
|
|
46
46
|
| `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
|
-
| `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/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
|
-
| `ias sdk install [--from <origen>] [--dry-run]` | Instala `@imagina/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
|
|
47
|
+
| `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
|
+
| `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
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). |
|
|
50
50
|
| `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
51
|
| `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). |
|
|
@@ -62,7 +62,7 @@ Fuera de un clon, `ias build --local` necesita `IAS_RUNNER_MODULE`.
|
|
|
62
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
63
|
| `ias credentials import\|list\|show\|rotate\|retire` | 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 (§5). |
|
|
64
64
|
| `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/ias-runner` (§6). |
|
|
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). |
|
|
66
66
|
| `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
67
|
| `ias submit <build> [--track <pista>] [--wait]` | Manda una build de tienda a App Store Connect o a Google Play, siempre como borrador, después de que un `owner` lo apruebe con su passkey (§6). |
|
|
68
68
|
| `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). |
|
|
@@ -114,7 +114,7 @@ Va en la raíz de la app, junto a `app.json`, y en git: no lleva secretos. Su es
|
|
|
114
114
|
| `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
115
|
| `server` | El origen de IAS. Por defecto, `https://ias.imagina.build`, que todavía no responde. 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
116
|
| `build`, `submit` | Los perfiles de `eas.json`, sin credenciales. La Fase 2 (`ias build`) los completa. |
|
|
117
|
-
| `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/ias` (§3.2). `channels`, la firma de los demás canales (§3.1). |
|
|
117
|
+
| `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
118
|
| `distribution` | El `token` del proyecto de distribución y, si no es el de `app`, su `project`. |
|
|
119
119
|
| `workflows` | Lo que lanza GitHub (una etiqueta, una rama o un pull request) y en qué pasos: `build` y `submit` (§3.0). |
|
|
120
120
|
| `vault` | `item`: el ítem de 1Password de la bóveda (`op://<bóveda>/<ítem>`), si no es `op://Private/IAS vault <host del servidor>`. |
|
|
@@ -176,7 +176,7 @@ La clave privada tampoco puede ser la ruta de un fichero. Para usar un fichero,
|
|
|
176
176
|
|
|
177
177
|
### 3.2 Lo que `app.json` deja a `ias.json`
|
|
178
178
|
|
|
179
|
-
Con el plugin de `@imagina/ias` en los `plugins` de `app.json` (`packages/sdk/README.md` §10), la configuración de las OTA puede ir solo en `ias.json`. `ias update` la lee con las mismas reglas con que el plugin la escribe en las builds:
|
|
179
|
+
Con el plugin de `@imagina-build/ias` en los `plugins` de `app.json` (`packages/sdk/README.md` §10), la configuración de las OTA puede ir solo en `ias.json`. `ias update` la lee con las mismas reglas con que el plugin la escribe en las builds:
|
|
180
180
|
|
|
181
181
|
| En `app.json` | Si falta, de `ias.json` |
|
|
182
182
|
| --- | --- |
|
|
@@ -185,7 +185,7 @@ Con el plugin de `@imagina/ias` en los `plugins` de `app.json` (`packages/sdk/RE
|
|
|
185
185
|
| `updates.codeSigningMetadata.keyid` | `updates.signing.keyId`. |
|
|
186
186
|
|
|
187
187
|
- Lo que está en los dos tiene que coincidir: el mismo fichero (aunque se escriba distinto) y el mismo keyid. Si no, es un error, como en el plugin.
|
|
188
|
-
- Si a `app.json` le falta alguno y sus `plugins` no tienen `@imagina/ias` (ni `@imagina/ias/plugin`), `ias update` para: ninguna build llevaría ese valor. Con un `app.config.*`, que el CLI no lee, da por hecho que lo añade.
|
|
188
|
+
- Si a `app.json` le falta alguno y sus `plugins` no tienen `@imagina-build/ias` (ni `@imagina-build/ias/plugin`), `ias update` para: ninguna build llevaría ese valor. Con un `app.config.*`, que el CLI no lee, da por hecho que lo añade.
|
|
189
189
|
- Una URL de IAS que `app.json` ya tiene se queda: es la que piden las builds publicadas (jScheduler, `sync.domibi.com`).
|
|
190
190
|
- Las builds de un canal con clave propia (§3.1) llevan su certificado porque el runner exporta `IAS_BUILD_PROFILE` (el perfil del trabajo) a todos sus pasos, y el plugin elige el canal con él. El runner comprueba después que la build lleva el certificado de `expo config --json`.
|
|
191
191
|
|
|
@@ -232,7 +232,7 @@ El material de firma se sella **en este ordenador** con `ias-vault-2` ([docs/tec
|
|
|
232
232
|
- **Ficheros**: una ruta o una referencia `op://`.
|
|
233
233
|
- **Contraseñas**: `op://…`, `env:NOMBRE` (una variable del entorno) o `-` (stdin). **Nunca la contraseña misma**: `ias` la rechaza, porque `ps` y el historial de la shell la verían.
|
|
234
234
|
- El tipo se puede omitir cuando las opciones lo dicen. `--name` da el nombre; si no, sale del material.
|
|
235
|
-
- El CLI saca los metadatos con `@imagina/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`.
|
|
235
|
+
- 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
236
|
- 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
237
|
|
|
238
238
|
`ias credentials list` (con `--all`, también las retiradas), `show <id>` (con `--verify`, comprueba la firma con la clave de 1Password), `rotate <id>` y `retire <id>`.
|
|
@@ -280,7 +280,7 @@ Con el contrato de [docs/technical/runner-api.md](../../docs/technical/runner-ap
|
|
|
280
280
|
5. Sigue el estado cada 5 s y el registro según llega, hasta que acaba (Ctrl-C deja de seguirla, no la cancela; `--detach` no la sigue). Sale con 1 si no acaba bien.
|
|
281
281
|
6. Con `--wait`, baja sus artefactos a `~/ias-builds/<app>/<plataforma>-<versión>-<número>` (o `--out`) y comprueba el tamaño y el SHA-256 de cada uno.
|
|
282
282
|
|
|
283
|
-
**`ias build --local`** hace lo mismo en este Mac, importando `@imagina/ias-runner` (`packages/runner`, o `IAS_RUNNER_MODULE`): una plataforma detrás de otra, a baja prioridad, con el cerrojo de la máquina (`--wait` espera a otra build que lo tenga). El material de firma es `--credentials op://<bóveda>/<elemento>` o una carpeta 700, o `IAS_IOS_CREDENTIALS` e `IAS_ANDROID_CREDENTIALS`. No habla con el servidor.
|
|
283
|
+
**`ias build --local`** hace lo mismo en este Mac, importando `@imagina-build/ias-runner` (`packages/runner`, o `IAS_RUNNER_MODULE`): una plataforma detrás de otra, a baja prioridad, con el cerrojo de la máquina (`--wait` espera a otra build que lo tenga). El material de firma es `--credentials op://<bóveda>/<elemento>` o una carpeta 700, o `IAS_IOS_CREDENTIALS` e `IAS_ANDROID_CREDENTIALS`. No habla con el servidor.
|
|
284
284
|
|
|
285
285
|
**`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
286
|
|
|
@@ -341,7 +341,7 @@ Lo que jscheduler-app hizo a mano el 19 de octubre de 2026 (su `docs/local-build
|
|
|
341
341
|
| --- | --- |
|
|
342
342
|
| `profiles [--app <código>] [--server <origen>] [--force] [--dry-run]` | Pasa los perfiles de build y de envío de `eas.json` a `ias.json`, con la conversión de `ias init`. Un `ias.json` nuevo se escribe entero; uno que ya existe solo gana los perfiles que le faltan (`--force` toma los de `eas.json` cuando difieren). Dice lo que no tiene equivalente y qué hacer en su lugar: las variables de cada perfil, `resourceClass`, las claves de envío, los ganchos `eas-build-*` de `package.json`, un `releaseStatus` que no es `draft`… |
|
|
343
343
|
| `prepare [--eas-project <id>] [--owner <cuenta>]` | Una carpeta en `$TMPDIR`, fuera del proyecto y con permisos 700, con `app.json` (nombre, identificadores y el `projectId` de EAS), `eas.json` (sus perfiles de build, sin `env`) y `node_modules` enlazado al de la app. Enseña las órdenes de `eas credentials`, que **ejecutas tú**: manejan secretos y preguntan. Si `app.json` ya no nombra el proyecto, `--eas-project` y `--owner`. Repetido, reutiliza la misma carpeta. |
|
|
344
|
-
| `import <carpeta> [--dry-run] [--force]` | Lee `credentials.json` y `credentials/` con `inspectEasCredentials` (`@imagina/ias-shared/credentials`), enseña sus metadatos sin secretos y lo que no cuadra con `app.json`, e importa cada credencial por el mismo camino que `ias credentials import --eas`: sellada y firmada por el equipo en este Mac, con el `id` hecho antes de sellar. Lo que ya está en la bóveda se salta. Después **borra lo bajado**, también si falla a medias o se interrumpe con Ctrl-C, y dice qué se importó y qué falta. Con `--dry-run` no envía nada y deja la descarga. |
|
|
344
|
+
| `import <carpeta> [--dry-run] [--force]` | Lee `credentials.json` y `credentials/` con `inspectEasCredentials` (`@imagina-build/ias-shared/credentials`), enseña sus metadatos sin secretos y lo que no cuadra con `app.json`, e importa cada credencial por el mismo camino que `ias credentials import --eas`: sellada y firmada por el equipo en este Mac, con el `id` hecho antes de sellar. Lo que ya está en la bóveda se salta. Después **borra lo bajado**, también si falla a medias o se interrumpe con Ctrl-C, y dice qué se importó y qué falta. Con `--dry-run` no envía nada y deja la descarga. |
|
|
345
345
|
| `version [--ios <n>] [--android <n>] [--profile <perfil>] [--dry-run]` | Los últimos números de build de EAS (`eas build:version:get --json`), los de `app.json` si `appVersionSource` no es `remote`, o los de las opciones, a `PUT /apps/<app>/build-numbers` ([docs/technical/runner-api.md](../../docs/technical/runner-api.md) §4.1). Gana el mayor, y el del servidor **nunca baja**: solo se envía la plataforma que sube. |
|
|
346
346
|
| `env [--environment <entorno>] [--env-file <fichero>] [--force] [--dry-run]` | Las variables de cada entorno de EAS (`eas env:list`, o el `.env` de `eas env:pull` con `--env-file`): `EXPO_PUBLIC_*` en claro y las demás selladas, como `ias env set`. Las secretas de EAS solo las leen sus builders: las lista para `ias env set … --from op://…`. Una variable que ya está en IAS se queda salvo `--force`, que sella la versión siguiente con su mismo `id`. Si el `.env` está en la carpeta de `prepare`, lo borra al acabar. |
|
|
347
347
|
| `checklist` | Lo que queda en IAS, en la app, en expo.dev y en las tiendas, adaptado a lo que encuentra, como el §7 de `docs/local-builds.md`: comparar la huella de la clave de subida con Play Console, cambiar `updates.url` en una build nueva, la cuenta de servicio en la carpeta del proyecto, quitar las claves de envío de EAS, las de push, borrar el proyecto de EAS, revisar las credenciales de la cuenta (pueden servir a otras apps del equipo) y borrar la carpeta de `prepare`. **No borra nada.** |
|
|
@@ -374,7 +374,7 @@ Después, `ias migrate eas prepare`; en la carpeta que imprime, `EAS_NO_VCS=1 ea
|
|
|
374
374
|
- `src/manage.mjs`: la API de gestión (el servidor, el token, las peticiones y las aprobaciones). `src/token-store.mjs`: dónde guarda el token `ias login`.
|
|
375
375
|
- `src/team-vault.mjs`: el ítem de 1Password de la bóveda y lo que hace falta para sellar, comprobado contra él. `src/secret-input.mjs`: de dónde se lee un secreto (`op://`, `env:`, `-`).
|
|
376
376
|
- `src/shared.mjs`: lo que se toma de `packages/shared` (`../../shared/…`). `npm pack` no lo lleva consigo: `scripts/pack.mjs` empaqueta una copia con `shared/` dentro (§1).
|
|
377
|
-
- `src/sdk.mjs`: el SDK `@imagina/ias` en una app (el gestor de paquetes, de dónde sale, su versión y su plugin en `app.json`), que comparten `ias sdk install` y `ias doctor`.
|
|
377
|
+
- `src/sdk.mjs`: el SDK `@imagina-build/ias` en una app (el gestor de paquetes, de dónde sale, su versión y su plugin en `app.json`), que comparten `ias sdk install` y `ias doctor`.
|
|
378
378
|
|
|
379
379
|
## 10. Pruebas
|
|
380
380
|
|
|
@@ -387,7 +387,7 @@ En Node 22, `node --test` no acepta una carpeta: desde la raíz, `node --test 'p
|
|
|
387
387
|
No usan red real, claves reales ni 1Password: el certificado lo crea `openssl` en una carpeta temporal. Prueban:
|
|
388
388
|
|
|
389
389
|
- `ias update` y `ias dist`, con las pruebas de `tools/` portadas;
|
|
390
|
-
- `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/ias` (`test/config.test.mjs`);
|
|
390
|
+
- `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
391
|
- `ias init` con la configuración de jscheduler-app y jero-invoices-app;
|
|
392
392
|
- `ias doctor` con programas simulados;
|
|
393
393
|
- `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;
|
package/package.json
CHANGED
package/schema/ias.schema.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"pattern": "^[a-z0-9][a-z0-9-]{0,31}$"
|
|
20
20
|
},
|
|
21
21
|
"server": {
|
|
22
|
-
"description": "The origin of IAS. OTA updates go to the server of updates.url in app.json, which is the one the builds ask (without it, the config plugin of @imagina/ias gives the builds this one's); the rest goes here. IAS_SERVER wins over it, except for that URL.",
|
|
22
|
+
"description": "The origin of IAS. OTA updates go to the server of updates.url in app.json, which is the one the builds ask (without it, the config plugin of @imagina-build/ias gives the builds this one's); the rest goes here. IAS_SERVER wins over it, except for that URL.",
|
|
23
23
|
"type": "string",
|
|
24
24
|
"pattern": "^https?://[^/?#\\s]+/?$",
|
|
25
25
|
"default": "https://ias.imagina.build"
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
}
|
|
221
221
|
},
|
|
222
222
|
"updates": {
|
|
223
|
-
"description": "OTA updates (ias update). The server and project come from updates.url in app.json, which is what the builds ask, or without it from server and app, as the config plugin of @imagina/ias writes it; the certificate and keyid, from its updates.codeSigning* or from signing.",
|
|
223
|
+
"description": "OTA updates (ias update). The server and project come from updates.url in app.json, which is what the builds ask, or without it from server and app, as the config plugin of @imagina-build/ias writes it; the certificate and keyid, from its updates.codeSigning* or from signing.",
|
|
224
224
|
"type": "object",
|
|
225
225
|
"additionalProperties": false,
|
|
226
226
|
"properties": {
|
|
@@ -229,12 +229,12 @@
|
|
|
229
229
|
"additionalProperties": false,
|
|
230
230
|
"properties": {
|
|
231
231
|
"keyId": {
|
|
232
|
-
"description": "The keyid of the certificate: the one of updates.codeSigningMetadata.keyid in app.json. Without it there, the config plugin of @imagina/ias writes this one into the builds.",
|
|
232
|
+
"description": "The keyid of the certificate: the one of updates.codeSigningMetadata.keyid in app.json. Without it there, the config plugin of @imagina-build/ias writes this one into the builds.",
|
|
233
233
|
"type": "string",
|
|
234
234
|
"pattern": "^[A-Za-z0-9._-]{1,64}$"
|
|
235
235
|
},
|
|
236
236
|
"certificate": {
|
|
237
|
-
"description": "The certificate (public) of the default channel, a file of the repository: the one of updates.codeSigningCertificate in app.json. Without it there, the config plugin of @imagina/ias writes this one into the builds; if both are written, they must be the same file.",
|
|
237
|
+
"description": "The certificate (public) of the default channel, a file of the repository: the one of updates.codeSigningCertificate in app.json. Without it there, the config plugin of @imagina-build/ias writes this one into the builds; if both are written, they must be the same file.",
|
|
238
238
|
"type": "string",
|
|
239
239
|
"minLength": 1
|
|
240
240
|
},
|
|
@@ -312,7 +312,7 @@
|
|
|
312
312
|
}
|
|
313
313
|
},
|
|
314
314
|
"push": {
|
|
315
|
-
"description": "Push notifications in the app (API.md §38): what ias.push of @imagina/ias and its config plugin (@imagina/ias/plugin) need. Nothing here is a secret.",
|
|
315
|
+
"description": "Push notifications in the app (API.md §38): what ias.push of @imagina-build/ias and its config plugin (@imagina-build/ias/plugin) need. Nothing here is a secret.",
|
|
316
316
|
"type": "object",
|
|
317
317
|
"additionalProperties": false,
|
|
318
318
|
"properties": {
|
|
@@ -103,7 +103,7 @@ export default {
|
|
|
103
103
|
wait: { type: 'boolean', description: 'When it succeeds, download its artifacts (to ~/ias-builds/<app>/… or --out). With --local, wait for a build of this Mac that holds the lock.' },
|
|
104
104
|
out: { type: 'string', value: '<folder>', description: 'Where the artifacts go (one folder per platform), or the output of --local.' },
|
|
105
105
|
detach: { type: 'boolean', description: 'Do not follow the build: launch it, open the approval and leave (ias builds logs <id> --follow follows it later).' },
|
|
106
|
-
local: { type: 'boolean', description: 'Build on this Mac with @imagina/ias-runner, one platform after the other: nothing goes to the server.' },
|
|
106
|
+
local: { type: 'boolean', description: 'Build on this Mac with @imagina-build/ias-runner, one platform after the other: nothing goes to the server.' },
|
|
107
107
|
credentials: { type: 'string', value: '<op://vault/item|folder>', description: 'With --local: the signing material (IAS_IOS_CREDENTIALS or IAS_ANDROID_CREDENTIALS by default).' },
|
|
108
108
|
},
|
|
109
109
|
run: build,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ias build --local: the build on this Mac, by @imagina/ias-runner imported here (it is the same pipeline the runners
|
|
1
|
+
// ias build --local: the build on this Mac, by @imagina-build/ias-runner imported here (it is the same pipeline the runners
|
|
2
2
|
// use), one platform after the other, never two at a time. The signing material is the runner's CredentialSource
|
|
3
3
|
// (op://vault/item or a 700 folder); nothing goes to the server. IAS_RUNNER_MODULE points at another copy of the runner.
|
|
4
4
|
import { resolve } from 'node:path';
|
|
@@ -14,7 +14,7 @@ async function runnerModule(context) {
|
|
|
14
14
|
try {
|
|
15
15
|
return await import(where.href);
|
|
16
16
|
} catch (error) {
|
|
17
|
-
throw new IasError(`@imagina/ias-runner is not at ${where.pathname} (${error.code ?? error.message})`, { fix: 'ias build --local needs the runner of packages/runner beside the CLI, or IAS_RUNNER_MODULE pointing at its src/index.mjs', code: 'runner_missing' });
|
|
17
|
+
throw new IasError(`@imagina-build/ias-runner is not at ${where.pathname} (${error.code ?? error.message})`, { fix: 'ias build --local needs the runner of packages/runner beside the CLI, or IAS_RUNNER_MODULE pointing at its src/index.mjs', code: 'runner_missing' });
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ias credentials import, list, show, rotate and retire (docs/technical/manage-api.md §8). import reads the material (files,
|
|
2
|
-
// op:// references or EAS's credentials.json), takes its metadata with @imagina/ias-shared/credentials, makes the id
|
|
2
|
+
// op:// references or EAS's credentials.json), takes its metadata with @imagina-build/ias-shared/credentials, makes the id
|
|
3
3
|
// (a UUID) and seals it in ias-vault-2 on this computer: for the recovery key and the runners of the list the team
|
|
4
4
|
// signed, both checked against 1Password, and signed with the team's key. The server gets the envelope and the
|
|
5
5
|
// metadata, never a secret; nothing here prints one.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ias doctor: whether this Mac and this app have what the commands need: Node, git, the 1Password CLI, Xcode and its
|
|
2
2
|
// tools (plutil, security, sips), aapt2 of the Android SDK, a configuration that loads and, in an app with a
|
|
3
|
-
// package.json, the SDK @imagina/ias and its plugin. It reads no secret: an op:// reference is checked for its form,
|
|
3
|
+
// package.json, the SDK @imagina-build/ias and its plugin. It reads no secret: an op:// reference is checked for its form,
|
|
4
4
|
// never opened (that would ask for Touch ID).
|
|
5
5
|
import { existsSync } from 'node:fs';
|
|
6
6
|
import { join, resolve } from 'node:path';
|
|
@@ -152,7 +152,7 @@ export function doctor(dir, { env, output }) {
|
|
|
152
152
|
|
|
153
153
|
export default {
|
|
154
154
|
name: 'doctor',
|
|
155
|
-
summary: 'Check Node, git, op, Xcode and its tools, the Android SDK, the app\'s configuration and the SDK @imagina/ias',
|
|
155
|
+
summary: 'Check Node, git, op, Xcode and its tools, the Android SDK, the app\'s configuration and the SDK @imagina-build/ias',
|
|
156
156
|
description: 'It reads no secret. A fail means a command of ias will not work here; a warning, that something may be needed later.',
|
|
157
157
|
run: ({ values, cwd, env, output }) => doctor(resolve(cwd, values.project ?? '.'), { env, output }),
|
|
158
158
|
};
|
|
@@ -13,15 +13,18 @@ const PROFILE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,49}$/;
|
|
|
13
13
|
|
|
14
14
|
/** Why a field of eas.json does not go to ias.json. */
|
|
15
15
|
const LEFT_OUT = {
|
|
16
|
-
env: 'variables go to ias env
|
|
17
|
-
credentialsSource: 'credentials go to the vault
|
|
18
|
-
ascApiKeyPath: 'credentials go to the vault
|
|
19
|
-
ascApiKeyId: 'credentials go to the vault
|
|
20
|
-
ascApiKeyIssuerId: 'credentials go to the vault
|
|
21
|
-
serviceAccountKeyPath: 'credentials go to the vault
|
|
22
|
-
appleId: 'credentials go to the vault
|
|
16
|
+
env: 'variables go to ias env, sealed when they are secret: ias migrate eas env brings those of EAS',
|
|
17
|
+
credentialsSource: 'credentials go to the vault: ias credentials import, or ias migrate eas',
|
|
18
|
+
ascApiKeyPath: 'credentials go to the vault: ias credentials import, or ias migrate eas',
|
|
19
|
+
ascApiKeyId: 'credentials go to the vault: ias credentials import, or ias migrate eas',
|
|
20
|
+
ascApiKeyIssuerId: 'credentials go to the vault: ias credentials import, or ias migrate eas',
|
|
21
|
+
serviceAccountKeyPath: 'credentials go to the vault: ias credentials import, or ias migrate eas',
|
|
22
|
+
appleId: 'credentials go to the vault: ias credentials import, or ias migrate eas',
|
|
23
23
|
};
|
|
24
|
-
const leftOutReason = (key) => LEFT_OUT[key] ?? 'ias.json has no field for it
|
|
24
|
+
const leftOutReason = (key) => LEFT_OUT[key] ?? 'ias.json has no field for it (schema/ias.schema.json lists what its profiles take)';
|
|
25
|
+
|
|
26
|
+
/** Why eas.json › cli stays out, and where the build numbers go: IAS keeps them on its server (ias version). */
|
|
27
|
+
const cliReason = (cli) => `it configures eas-cli; IAS keeps the build numbers on its server, and autoIncrement takes the next: ${cli?.appVersionSource === 'remote' ? 'EAS keeps them now (appVersionSource remote), and ias migrate eas version brings them' : 'ias version set --from-app-json brings those of app.json'}`;
|
|
25
28
|
|
|
26
29
|
/** The code of an application made from a slug: lowercase letters, digits and hyphens, up to 32. */
|
|
27
30
|
const codeFromSlug = (slug) => (typeof slug === 'string' ? slug.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 32) : undefined);
|
|
@@ -114,7 +117,7 @@ export function initialConfig(dir, { app: appOption } = {}) {
|
|
|
114
117
|
config.submit = submit;
|
|
115
118
|
from.submit = 'eas.json';
|
|
116
119
|
}
|
|
117
|
-
for (const key of Object.keys(eas ?? {}).filter((candidate) => !['build', 'submit'].includes(candidate))) leave(`eas.json › ${key}`, key === 'cli' ?
|
|
120
|
+
for (const key of Object.keys(eas ?? {}).filter((candidate) => !['build', 'submit'].includes(candidate))) leave(`eas.json › ${key}`, key === 'cli' ? cliReason(eas.cli) : leftOutReason(key));
|
|
118
121
|
|
|
119
122
|
const secrets = writtenSecrets(config);
|
|
120
123
|
const errors = validate(config);
|
|
@@ -200,7 +203,7 @@ export function init(dir, { app, force = false, dryRun = false }, output) {
|
|
|
200
203
|
}
|
|
201
204
|
const legacy = Object.values(LEGACY_FILES).filter((name) => existsSync(join(dir, name)));
|
|
202
205
|
if (legacy.length > 0) output.log(`${legacy.join(' and ')} can go once nothing runs tools/ any more: ias reads ias.json first.`);
|
|
203
|
-
output.log('ias doctor checks it; ias sdk install puts the SDK @imagina/ias in the app, with its plugin, which writes ias.json into the builds.');
|
|
206
|
+
output.log('ias doctor checks it; ias sdk install puts the SDK @imagina-build/ias in the app, with its plugin, which writes ias.json into the builds.');
|
|
204
207
|
return { file, written: !dryRun, config, from, left_out: leftOut };
|
|
205
208
|
}
|
|
206
209
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ias sdk install: puts the SDK @imagina/ias in an Expo app: the package with the app's package manager, its config
|
|
1
|
+
// ias sdk install: puts the SDK @imagina-build/ias in an Expo app: the package with the app's package manager, its config
|
|
2
2
|
// plugin in the plugins of app.json, and what is left to do by hand (the Expo modules it uses and a new build).
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
@@ -6,7 +6,7 @@ import process from 'node:process';
|
|
|
6
6
|
import { CONFIG_FILE, DYNAMIC_CONFIGS, loadProject } from '../../config.mjs';
|
|
7
7
|
import { IasError } from '../../errors.mjs';
|
|
8
8
|
import { gently, run } from '../../process.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import { SDK_PACKAGE, addCommand, addPlugin, packageManager, sdkSource, sdkState } from '../../sdk.mjs';
|
|
10
10
|
|
|
11
11
|
/** Runs the install with its output on stderr, so --json keeps stdout whole; at a low priority on macOS. */
|
|
12
12
|
const systemExec = (command, commandArgs, options) => (process.platform === 'darwin' ? gently : run)(command, commandArgs, { stdio: ['inherit', 2, 'inherit'], ...options });
|
|
@@ -64,7 +64,7 @@ export default {
|
|
|
64
64
|
summary: `Install ${SDK_PACKAGE} with the app's package manager and add its config plugin to app.json`,
|
|
65
65
|
description: [
|
|
66
66
|
'The package manager is the one of packageManager in package.json or of the lockfile (npm, yarn, pnpm or bun); npm without either.',
|
|
67
|
-
`${SDK_PACKAGE} comes from --from, from IAS_SDK_SOURCE, from the packages/sdk of the repository when ias runs from a clone, or from npm as
|
|
67
|
+
`${SDK_PACKAGE} comes from --from, from IAS_SDK_SOURCE, from the packages/sdk of the repository when ias runs from a clone, or from npm as ${SDK_PACKAGE}@^<version of ias>.`,
|
|
68
68
|
'With app.config.js (or .ts), ias does not touch it and says what to add. Then it lists what is left: expo-updates, expo-notifications if the app uses push, and a prebuild or a new build.',
|
|
69
69
|
].join('\n'),
|
|
70
70
|
options: {
|
package/src/config.mjs
CHANGED
|
@@ -16,13 +16,13 @@ export const DEFAULT_SERVER = 'https://ias.imagina.build';
|
|
|
16
16
|
/** The path of an OTA manifest, the URL the builds carry in updates.url: /api/v1/ota/<project>/manifest. */
|
|
17
17
|
export const MANIFEST_PATH = /^\/api\/v1\/ota\/([^/]+)\/manifest$/;
|
|
18
18
|
|
|
19
|
-
/** The names the config plugin of @imagina/ias (packages/sdk/app.plugin.cjs) goes by in the plugins of app.json. */
|
|
20
|
-
export const IAS_PLUGINS = ['@imagina/ias', '@imagina/ias/plugin', '@imagina/ias/app.plugin.cjs'];
|
|
19
|
+
/** The names the config plugin of @imagina-build/ias (packages/sdk/app.plugin.cjs) goes by in the plugins of app.json. */
|
|
20
|
+
export const IAS_PLUGINS = ['@imagina-build/ias', '@imagina-build/ias/plugin', '@imagina-build/ias/app.plugin.cjs'];
|
|
21
21
|
/** The app configs Expo runs as code: ias cannot read nor rewrite them. */
|
|
22
22
|
export const DYNAMIC_CONFIGS = ['app.config.js', 'app.config.ts', 'app.config.mjs', 'app.config.cjs'];
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* The updates.url the config plugin of @imagina/ias gives the builds of an app.json that has none: the server of
|
|
25
|
+
* The updates.url the config plugin of @imagina-build/ias gives the builds of an app.json that has none: the server of
|
|
26
26
|
* ias.json and the application. Not IAS_SERVER: the plugin never reads it, so the builds do not ask there.
|
|
27
27
|
*/
|
|
28
28
|
export const pluginManifestUrl = (config) => `${(config.server ?? DEFAULT_SERVER).replace(/\/+$/, '')}/api/v1/ota/${config.app}/manifest`;
|
|
@@ -116,7 +116,7 @@ export function updateSettings(project, env = process.env) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
* The URL, certificate and keyid of app.json or, where it has none, what the config plugin of @imagina/ias writes into
|
|
119
|
+
* The URL, certificate and keyid of app.json or, where it has none, what the config plugin of @imagina-build/ias writes into
|
|
120
120
|
* the builds from ias.json (packages/sdk/app.plugin.cjs), by the same rules: updates.url from the server and the
|
|
121
121
|
* application, the certificate and keyid of the default channel from updates.signing. A value of ias.json that
|
|
122
122
|
* contradicts app.json is an error, as in the plugin.
|
|
@@ -132,7 +132,7 @@ function iasUpdateSettings({ dir, config }, env) {
|
|
|
132
132
|
const url = updates.url ?? pluginManifestUrl(config);
|
|
133
133
|
const manifestUrl = URL.canParse(url) ? new URL(url) : undefined;
|
|
134
134
|
if (!manifestUrl || !MANIFEST_PATH.test(manifestUrl.pathname)) {
|
|
135
|
-
throw new IasError(`updates.url in app.json (${updates.url}) is not the manifest of an IAS OTA project`, { fix: `it must be ${serverOf({ config }, env).replace(/\/$/, '')}/api/v1/ota/<project>/manifest, and changing it needs a new build; without it, the config plugin of @imagina/ias writes ${pluginManifestUrl(config)}` });
|
|
135
|
+
throw new IasError(`updates.url in app.json (${updates.url}) is not the manifest of an IAS OTA project`, { fix: `it must be ${serverOf({ config }, env).replace(/\/$/, '')}/api/v1/ota/<project>/manifest, and changing it needs a new build; without it, the config plugin of @imagina-build/ias writes ${pluginManifestUrl(config)}` });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
const appKeyid = updates.codeSigningMetadata?.keyid;
|
|
@@ -152,7 +152,7 @@ function iasUpdateSettings({ dir, config }, env) {
|
|
|
152
152
|
if (!appCertificate) fromPlugin.push('updates.codeSigningCertificate');
|
|
153
153
|
|
|
154
154
|
if (fromPlugin.length > 0 && !runsIasPlugin(dir, expo)) {
|
|
155
|
-
throw new IasError(`app.json has no ${fromPlugin.join(', ')}, and its plugins do not have @imagina/ias, which writes ${fromPlugin.length === 1 ? 'it' : 'them'} into the builds from ias.json`, { fix: 'ias sdk install adds "@imagina/ias" to the plugins of app.json (packages/sdk/README.md §10), for a new build; or write them in app.json' });
|
|
155
|
+
throw new IasError(`app.json has no ${fromPlugin.join(', ')}, and its plugins do not have @imagina-build/ias, which writes ${fromPlugin.length === 1 ? 'it' : 'them'} into the builds from ias.json`, { fix: 'ias sdk install adds "@imagina-build/ias" to the plugins of app.json (packages/sdk/README.md §10), for a new build; or write them in app.json' });
|
|
156
156
|
}
|
|
157
157
|
const certificateFile = join(dir, certificatePath);
|
|
158
158
|
if (!existsSync(certificateFile)) throw new IasError(`no certificate at ${certificatePath} (${appCertificate ? 'updates.codeSigningCertificate of app.json' : 'updates.signing.certificate of ias.json'})`, { fix: 'it is the public certificate the builds carry: restore it from git' });
|
package/src/sdk.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The SDK @imagina/ias (packages/sdk) in an app: which package manager installs it, where it comes from, whether it is
|
|
1
|
+
// The SDK @imagina-build/ias (packages/sdk) in an app: which package manager installs it, where it comes from, whether it is
|
|
2
2
|
// installed and compatible with this ias, and its config plugin in the plugins of app.json. ias sdk install and ias
|
|
3
3
|
// doctor share it.
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
@@ -9,16 +9,10 @@ import { DYNAMIC_CONFIGS, IAS_PLUGINS, readExpo, readJson } from './config.mjs';
|
|
|
9
9
|
import { IasError } from './errors.mjs';
|
|
10
10
|
import { INSTALLS } from './ota.mjs';
|
|
11
11
|
|
|
12
|
-
export const SDK_PACKAGE = '@imagina/ias';
|
|
12
|
+
export const SDK_PACKAGE = '@imagina-build/ias';
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* The registry the packages are published to (scripts/publish.mjs) and their scope there: the public npm registry,
|
|
16
|
-
* where the organization is imagina-build, so @imagina/ias goes out as @imagina-build/ias and the apps install it under
|
|
17
|
-
* its own name with an alias (@imagina/ias@npm:@imagina-build/ias@…). Installing needs no token.
|
|
18
|
-
*/
|
|
14
|
+
/** The registry the packages are published to (scripts/publish.mjs): npm, in the organization imagina-build. Installing needs no token. */
|
|
19
15
|
export const REGISTRY = 'https://registry.npmjs.org';
|
|
20
|
-
export const REGISTRY_SCOPE = 'imagina-build';
|
|
21
|
-
export const REGISTRY_SDK = `@${REGISTRY_SCOPE}/ias`;
|
|
22
16
|
|
|
23
17
|
/** How each package manager adds a dependency. */
|
|
24
18
|
const ADD = { npm: ['install'], yarn: ['add'], pnpm: ['add'], bun: ['add'] };
|
|
@@ -45,7 +39,7 @@ export function isCompatible(version, cliVersion = VERSION) {
|
|
|
45
39
|
|
|
46
40
|
/**
|
|
47
41
|
* Where the SDK is installed from: --from, IAS_SDK_SOURCE, the packages/sdk of the repository when ias runs from a
|
|
48
|
-
* clone, or npm (
|
|
42
|
+
* clone, or npm (@imagina-build/ias@^VERSION). { spec, origin }; a folder of the
|
|
49
43
|
* repository becomes a path relative to the app, as package.json keeps it.
|
|
50
44
|
*/
|
|
51
45
|
export function sdkSource(dir, { from, env = {}, repository = repositorySdk() } = {}) {
|
|
@@ -53,7 +47,7 @@ export function sdkSource(dir, { from, env = {}, repository = repositorySdk() }
|
|
|
53
47
|
if (from) return { spec: asSpec(from), origin: '--from' };
|
|
54
48
|
if (env.IAS_SDK_SOURCE) return { spec: asSpec(env.IAS_SDK_SOURCE), origin: 'IAS_SDK_SOURCE' };
|
|
55
49
|
if (repository) return { spec: `file:${relative(dir, repository)}`, origin: 'the packages/sdk of the repository ias runs from', folder: repository };
|
|
56
|
-
return { spec: `${SDK_PACKAGE}
|
|
50
|
+
return { spec: `${SDK_PACKAGE}@${compatibleRange()}`, origin: 'npm' };
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
/** The package manager of the app: packageManager of package.json, or the first lockfile from `dir` up; npm without one. */
|
|
@@ -100,7 +94,7 @@ export function sdkState(dir) {
|
|
|
100
94
|
const hasIasPlugin = (plugins) => (Array.isArray(plugins) ? plugins : []).some((entry) => IAS_PLUGINS.includes(Array.isArray(entry) ? entry[0] : entry));
|
|
101
95
|
|
|
102
96
|
/**
|
|
103
|
-
* Adds "@imagina/ias" to the plugins of app.json, keeping the rest of the file and its indentation: { changed, reason }.
|
|
97
|
+
* Adds "@imagina-build/ias" to the plugins of app.json, keeping the rest of the file and its indentation: { changed, reason }.
|
|
104
98
|
* With an app.config.*, which ias cannot rewrite, it changes nothing and says so (reason dynamic).
|
|
105
99
|
*/
|
|
106
100
|
export function addPlugin(dir, { dryRun = false } = {}) {
|
|
@@ -111,7 +105,7 @@ export function addPlugin(dir, { dryRun = false } = {}) {
|
|
|
111
105
|
const text = readFileSync(file, 'utf8');
|
|
112
106
|
const appJson = readJson(file, 'app.json', 'fix app.json, then run ias sdk install again');
|
|
113
107
|
if (!appJson?.expo || typeof appJson.expo !== 'object') throw new IasError('app.json has no expo object', { fix: 'the config plugin goes in app.json › expo › plugins' });
|
|
114
|
-
if (appJson.expo.plugins !== undefined && !Array.isArray(appJson.expo.plugins)) throw new IasError('plugins of app.json is not a list', { fix: 'make it a list, such as "plugins": ["@imagina/ias"]' });
|
|
108
|
+
if (appJson.expo.plugins !== undefined && !Array.isArray(appJson.expo.plugins)) throw new IasError('plugins of app.json is not a list', { fix: 'make it a list, such as "plugins": ["@imagina-build/ias"]' });
|
|
115
109
|
if (hasIasPlugin(appJson.expo.plugins)) return { changed: false, reason: 'present', file: 'app.json' };
|
|
116
110
|
appJson.expo.plugins = [...(appJson.expo.plugins ?? []), SDK_PACKAGE];
|
|
117
111
|
const indent = text.match(/^[ \t]+(?=")/m)?.[0] ?? ' ';
|
package/src/shared.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// What the CLI takes from @imagina/ias-shared (packages/shared): the vault (docs/technical/vault.md) and the reading of the
|
|
1
|
+
// What the CLI takes from @imagina-build/ias-shared (packages/shared): the vault (docs/technical/vault.md) and the reading of the
|
|
2
2
|
// signing credentials. The packages have no dependencies (decision D5), so it is a path inside the repository; npm pack
|
|
3
3
|
// does not take ../shared with it, so scripts/pack.mjs packs a copy with shared/ inside and points these imports there.
|
|
4
4
|
export * from '../shared/vault/index.mjs';
|
package/src/team-vault.mjs
CHANGED
|
@@ -32,7 +32,15 @@ export function vaultItem(context, project, server) {
|
|
|
32
32
|
return { reference, vault: match[1], title: match[2] };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Runs op. With `input`, through cat: Node gives a child a socket for stdin, not a pipe, and op only reads a template
|
|
37
|
+
* from a pipe ("provide the item category with '--category' flag" otherwise). The template never touches the disk nor
|
|
38
|
+
* the arguments.
|
|
39
|
+
*/
|
|
40
|
+
const op = (args, { env, input } = {}) => {
|
|
41
|
+
const [command, commandArgs] = input === undefined ? ['op', args] : ['/bin/sh', ['-c', 'cat | op "$@"', 'sh', ...args]];
|
|
42
|
+
return spawnSync(command, commandArgs, { encoding: 'utf8', env, input, maxBuffer: 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
43
|
+
};
|
|
36
44
|
|
|
37
45
|
/** The first line of what op said, which names the reference and never a value. */
|
|
38
46
|
const opReason = (result) => (result.error ? result.error.message : (result.stderr || '').trim().split('\n').at(-1) || `exit ${result.status}`);
|