Skip to main content

openci_cli_plus 0.8.0 copy "openci_cli_plus: ^0.8.0" to clipboard
openci_cli_plus: ^0.8.0 copied to clipboard

Command-line tools for OpenCI authentication, teams, secrets, App Store Connect setup, and workflow paths.

OpenCI CLI #

Command-line tools for OpenCI authentication, team switching, secrets, workflow paths, and local development.

Install #

Install with Dart 3.11.5 or later:

dart install openci_cli_plus
openci --help

If openci is not found, follow the dart install PATH setup. Run openci update to update to the latest stable version on pub.dev.

If you previously installed genuineci_cli, run dart install openci_cli_plus once to switch packages. The new openci command uses your existing login profiles and language settings.

In an interactive terminal, commands check for a newer stable version and ask Update now? [y/N]. Enter y or yes to install it, then rerun your original command with the updated CLI. Enter n, no, or press Enter to continue without updating. Updates use dart install, so the Dart SDK must be on PATH.

Automatic checks time out after two seconds and silently skip network errors. They are skipped for help, version, and update commands, when CI is set, or when stdin, stdout, or stderr is redirected. Use openci --no-check-updates <command> to skip the check explicitly. openci update also works in scripts without confirmation and returns a nonzero exit code if checking or installing fails.

The remote commands work from any directory. Local development commands require an OpenCI checkout and the services described below.

Shell completion #

Bash and Zsh completion is provided by cli_completion. Completion scripts are installed automatically when running openci --help or another command in an interactive terminal outside CI. Restart your shell afterwards, or install them explicitly and follow the printed instructions:

openci install-completion-files

For Zsh, ensure autoload -Uz compinit and compinit run in ~/.zshrc before the completion script is sourced. Reload with source ~/.zshrc.

Press Tab to complete every command and subcommand, their options (including global flags), and known argument values:

openci reg<Tab>                      # register
openci register <Tab>                # secret, secretFile, options
openci sync <Tab>                    # paths, secrets, options
openci use <Tab>                     # japanese, english, options
openci help register <Tab>           # secret, secretFile, options
openci login --ser<Tab>              # --server
openci login --server <Tab>          # default and saved HTTPS servers
openci login --team-id <Tab>         # saved team IDs for the selected server

Login value completion reads local credential profiles only. --firebase-api-key also suggests the default and saved Firebase Web API keys for the selected server. New server URLs, team IDs, and Web API keys can still be entered directly. Completion does not fetch team lists or refresh tokens. Completion requests and completion setup commands skip automatic update checks.

Use openci uninstall-completion-files to remove the scripts.

Usage #

Log in to a remote server with the same email and password as the dashboard:

openci login

The default server is https://openci-worker-01.tail4beb18.ts.net. To connect to another server, pass the optional --server <url> option.

Enter your email and password at the prompts. The password is hidden and is only sent to Firebase Authentication; it is never saved. After the server confirms your team membership, the CLI saves and activates the remote credential profile, keeping the local profile. Failed login attempts preserve existing credentials.

If you belong to multiple teams, specify --team-id <id> using the displayed list. For a self-hosted Firebase project, also pass its Web API key with --firebase-api-key <key>; the default is the dashboard's openci-b1b91 project. The remote server URL must use HTTPS.

Secret commands use this profile and automatically refresh expiring Firebase ID tokens. Credentials, including the refresh token, are stored in the openci application config directory with owner-only permissions on macOS/Linux. If an older development build stored your credentials in a openci configuration directory, log in again; those credentials are not migrated.

Check the active profile, server, and current team:

openci status
Profile: remote
Server: https://ci.example.com
Selected team ID: team-id
Team name: My team

The command fetches the current team name from the saved server using the active profile's team ID. It works from any directory and supports redirected output. It preserves the selected team and other profiles; expiring Firebase tokens are refreshed automatically. The saved profile, server, and team ID are displayed even if the team name cannot be fetched. If the selected team is no longer available, run openci switch team. Errors go to stderr and return a nonzero exit code. A fresh installation shows a login hint and exits successfully.

List all teams available to the active profile:

openci list teams
  Alpha (team-a)
* OpenCI (team-b)

Each line shows a team name and ID, sorted by name and then ID. * marks the selected team. Listing teams does not change the selected team or active profile; expiring Firebase tokens are refreshed automatically. It works with redirected output and does not require a selected team. An empty list shows a message and exits successfully; authentication, connection, and invalid-response errors go to stderr and return a nonzero exit code.

Switch the active profile's team without logging in again:

openci switch team

Login, status, team listing and switching fetch memberships from the teamManagement Firebase callable Function, backed by FSC. The active profile's Firebase project (or the ID token's audience) and functions_region select the endpoint. --server remains the address for other legacy APIs. A local profile uses its saved Auth/Functions Emulator settings (Functions defaults to port 5001). To activate the remote or local profile, run the corresponding login command first.

The picker shows each team's name and ID, with * marking the saved current team and > marking the highlighted choice. It starts on the current team if that team is still available, otherwise on the first candidate.

  • Use the up/down arrow keys to move; navigation wraps at either end.
  • Press Enter to confirm, even if only one team is available.
  • Press Esc or Ctrl+C, or end the input, to cancel.

Confirmation updates only the active profile's teamId. The remote and local profiles each keep their own current team; selecting a team updates the existing profile. Server and Auth Emulator settings, refreshed tokens, and other profiles are preserved. Choosing the current team succeeds without a write. Cancellation or a failed save preserves the previous selected team. Expiring authentication tokens may still be refreshed before the picker opens. If the active profile or its credentials change during selection, the CLI refuses to overwrite them and asks you to run openci switch team again.

Subsequent openci list secrets, openci register secret, openci register secretFile, openci delete secret SECRET_NAME, and openci sync --secrets commands use the newly selected team. Existing openci/generated/secrets.g.dart files are updated by running openci sync --secrets in the workflow project.

Team switching requires interactive stdin and stdout with ANSI support. Run it directly in a terminal, without piped input or redirected output. Success and unchanged-selection messages go to stdout; errors and cancellations go to stderr and return a nonzero exit code.

Troubleshooting team selection:

Condition What to check
Login required or HTTP 401/403 Log in again for the intended server with openci login or openci login --local.
No team memberships Create or join a team in the dashboard for that server.
Interactive terminal required Use an ANSI-capable terminal with interactive stdin and stdout; remove pipes and output redirection.
HTTP error or connection failure Check the active profile's server URL and network connection. For local development, check that the Auth and Functions emulators are running.
Invalid team list Check that the saved server URL points to a compatible OpenCI API.
Changed profile or credentials Run openci switch team again using the current authentication.
Save failure Check the credentials file, its permissions, and available disk space.

Choose English or Japanese messages with openci use english or openci use japanese. The language setting is saved for future invocations.

List secret names for the active profile's team:

openci list secrets

Names are printed one per line, sorted by name, without secret values. If the team has no secrets, the command prints a message and exits successfully. It works from any directory, supports redirected output, and does not generate workflow files. Control characters in names are displayed as escaped text.

Delete a secret by name from the active profile's team:

openci delete secret SECRET_NAME

The command deletes the named secret immediately, without a confirmation prompt, and works in scripts. It returns exit code 0 on success, 1 if the secret is not found or the request fails, and 64 for invalid arguments. Secret values are never fetched or displayed. After deletion, run openci sync --secrets from your workflow project to update its generated secret definitions.

Register a secret for the active profile's team:

openci register secret

Enter the secret name (for example, API_TOKEN), then its value in an interactive terminal. The value is hidden while typing. After you press Enter, the prompt shows ******, regardless of the value's length.

Registering an existing name updates its value. The CLI uses the dashboard's existing API, which trims leading and trailing whitespace from values. Values are not printed or saved locally. Firebase tokens are refreshed when needed. After saving, the command automatically runs openci sync to update both openci/generated/paths.g.dart and openci/generated/secrets.g.dart. Run it from your workflow project or a subdirectory. If sync fails, the secret remains saved and the command returns exit code 1; fix the reported problem and retry openci sync. Cancelling input or a failed save does not run sync.

Register a file as a Base64 secret:

openci register secretFile

Type a file path to filter the displayed candidates. Press Tab or the up/down arrow keys to complete a candidate, then Enter to enter a folder or select a file. Relative paths, absolute paths, ~/, spaces, and Unicode filenames are supported. Type . to include hidden files such as .env. Press Esc or Ctrl+C to cancel. The picker requires an interactive terminal with ANSI support.

The selected file is read as bytes and Base64-encoded. Its basename, including the extension, is uppercased, runs of characters other than letters, digits and underscores become _, and _BASE64 is appended. Names beginning with a digit are prefixed with _. For example, google-services.json becomes GOOGLE_SERVICES_JSON_BASE64. Selecting an existing name updates that secret. Empty files are rejected. Neither the file contents nor the encoded value is printed or saved locally. After saving, the command automatically runs the same sync as register secret, updating both generated files. A sync failure leaves the uploaded secret saved and returns exit code 1; retry openci sync after fixing the reported problem.

Local development #

The legacy openci dev start command and its PostgreSQL/API/Orchard Compose stack have been retired. The CLI's team and Secrets commands use Firebase Functions. See Firebase development for the Auth Emulator and FSC deployment for the Functions, Data Connect and Cloud Run setup.

To log in locally, start the Auth, Functions and FSC emulators, create an Auth user and its FSC team membership, then run:

openci login --local

Enter the credentials of a user you created in the Auth Emulator UI. The CLI signs in at 127.0.0.1:9099, then calls teamManagement on the Functions Emulator at port 5001. It selects test-team only if FSC returns it among the user's memberships. See the team migration guide.

Successful login saves and activates the local Firebase profile, including the refresh credentials and firebase_auth_emulator_host. Later token refreshes use the same emulator. Failed or cancelled login preserves saved profiles and never falls back to real Firebase. To replace an older local profile containing an internal API key, run openci login --local again after preparing the user and team; successful login replaces only that profile. The password is never saved. The remote --server, --team-id, and --firebase-api-key options cannot be combined with --local.

Run openci sync from your workflow project to generate both typed secret definitions and workspace paths:

openci sync

The files are written to openci/generated/secrets.g.dart and openci/generated/paths.g.dart. The generated directory is created if needed. Use --secrets or --paths to generate only that file; passing both flags generates both. Each selected generator runs even if the other fails, and the command exits unsuccessfully if either selected generator fails.

With an authenticated credential profile, generate only secret definitions:

openci sync --secrets

The command uses the active credential profile and finds the nearest ancestor containing an openci directory, starting from the current directory. It replaces openci/generated/secrets.g.dart with constant Secret references from openci_workflow. Each reference preserves the original environment variable name in .key and reads its runtime value through .value:

import 'generated/secrets.g.dart';

final secret = Secrets.iosBuildNumber;
final key = secret.key; // IOS_BUILD_NUMBER
final value = secret.value; // The environment variable's value as a String.

Creating or passing a reference and reading .key do not require an environment variable. Reading .value throws a StateError if the variable is absent; an empty value remains an empty string. Secret values are never downloaded or written to the generated file. Run it again after adding or removing secrets. Fetch or generation failures leave the existing file unchanged.

When regenerating an existing workflow, replace value usages such as Secrets.apiToken with Secrets.apiToken.value, including string interpolation. Use openci_workflow 0.1.5 or later, which exports Secret. Deploy the server's updated secret-definition parser before running workflows with the new format; the parser continues to support previously generated String getters.

Generate typed workspace paths without logging in or starting local services:

openci sync --paths

Run this from your workflow project or one of its subdirectories. The command reads the workspace list in the root pubspec.yaml and each listed package's name, recursively collects their subdirectories, then writes openci/generated/paths.g.dart following the directory hierarchy. For example, apps/dashboard/android/app becomes WorkspacePaths.root.apps.dashboard.android.app. If workspace is absent, the command treats the root package as a single-package project and collects its subdirectories. For example, android/app becomes WorkspacePaths.root.android.app, and lib becomes WorkspacePaths.root.lib. An explicitly empty workspace: [] still generates only the root accessor. Hidden directories, symlinks, and generated or dependency directories (build, coverage, node_modules, Pods, ephemeral, and xcuserdata) are excluded at every level. openci/generated is also excluded so repeated syncs produce stable paths. Directory names determine the getters; package names are used to validate the workspace. Dart keywords and Object member names get a trailing underscore: switch becomes switch_, class becomes class_, and hash_code becomes hashCode_. The directory paths themselves are preserved. Sibling names that map to the same getter, such as switch and switch_, are rejected. Run it again after adding, moving or renaming workspace directories. Read or generation failures preserve the existing file.

The previous sync secrets and sync paths subcommands are replaced by these flags. When upgrading existing workflows, change imports to generated/secrets.g.dart and generated/paths.g.dart, then remove the old files from openci/ after updating their imports.

Import the generated file in your workflow and run it from the repository root, as the worker does, because these paths are relative to that root:

import 'package:openci_workflow/openci_workflow.dart';

import 'generated/paths.g.dart';

final openCI = await OpenCI.init(
  workflowName: 'Dashboard CI',
  ciTriggers: [CITrigger.push(branch: 'develop')],
  currentWorkingDirectory: WorkspacePaths.root.apps.dashboard,
);

await openCI.flutter.staticAnalysis();
await openCI.flutter.unitTests();

openCI.flutter uses the same working directory as openCI.run(). It resolves currentWorkingDirectory relative to the workspace root, or uses the workspace root when no directory is configured. Both Flutter methods also accept dir, for example await openCI.flutter.unitTests(dir: WorkspacePaths.root.apps.dashboard);. The override is relative to the workspace root and applies only to that call; later calls without dir continue to use the configured working directory.

WorkspacePaths.root represents . and WorkspacePaths.root.apps represents apps. Both can also be passed directly to methods accepting a String path.

openci setup asc-keys creates a key on Apple Silicon Macs (macOS arm64) and saves its credentials as three secrets for the active OpenCI team. It also prepares a certificate private key for iOS signing, keeping any existing key. First run openci login (or openci login --local) and select the intended team. Setup checks access to that team's secrets before contacting Apple, displays the server, team ID, and secret names, and indicates which existing secrets will be replaced.

It reuses a regular asc 5.11.0 file in OpenCI's cache at <cache>/tools/asc/5.11.0/macOS_arm64/asc. If absent, it downloads the pinned official release, verifies its SHA-256, sets owner-only executable permissions, and moves it into the cache alongside its MIT license. Failed downloads are cleaned up. Before running either an existing or newly installed binary, it verifies the cached file's SHA-256 again. It then runs asc version using the absolute cache path and requires exit code 0 and version 5.11.0. A checksum mismatch prevents execution; an execution failure, unexpected version, or 10-second timeout stops setup with an error. A timed-out process is terminated. After verification, it prompts for an Apple ID email address in an interactive terminal. Surrounding whitespace is trimmed; empty input or EOF stops setup with exit code 1. Ctrl+C at the email prompt interrupts it with shell exit code 130. Input and prompt output (stderr) must both be attached to a terminal. The prompt leaves stdin open and does not change terminal modes. It then starts the verified asc binary with web auth login --apple-id <email> --output table, inheriting the terminal's stdin, stdout, and stderr. Password and two-factor prompts are handled directly by asc, which manages its own saved credentials and web sessions. OpenCI does not read those inputs. The command waits for asc to exit and propagates a nonzero exit code (negative signal codes are mapped to 128 + signal). A failure to start asc produces a localized error. Cancellation inside asc follows asc's exit status; asc 5.11.0 returns 1 when its password prompt is interrupted. After login exits with code 0, OpenCI checks the same Apple ID with web auth status --apple-id <email> --output json. The check requires exit code 0 and a JSON object with authenticated: true; asc can also exit with code 0 for an unauthenticated session. A false value stops setup with a sign-in message. Execution failures, malformed responses, and a 30-second timeout produce localized errors. Raw status output is limited to 16 KiB and is not printed; a timed-out process is terminated. The authentication result retains the optional providerName, providerId, and publicProviderId fields. Once authentication is confirmed, OpenCI displays the returned name (when available) and IDs of the currently selected App Store Connect provider. If neither ID is available, setup stops. Other team IDs are not used as substitutes. This response describes the current provider, not a list of teams. The pinned asc 5.11.0 does not emit providerName, so its output continues to display IDs only.

After displaying the provider, OpenCI asks whether to create a new key named OpenCI with APP_MANAGER access to all apps and save it to the displayed OpenCI destination. Only y or yes proceeds; other input or EOF cancels without requesting a key. The Apple account must be an Account Holder or Admin to create a team key. The verified binary runs web api-keys create --apple-id <email> --provider-id <id> (and/or --public-provider-id <id>) with --name OpenCI --role APP_MANAGER, an explicit output directory, and --output json. Users do not enter provider IDs. Both returned IDs are passed when available so asc can verify they match.

For a key created by the previous CLI, pass its original directory to openci setup asc-keys --key-directory <directory> to resume registration.

Each issuance uses a new owner-only directory under ~/Library/Application Support/openci/asc-api-keys/. Its path is printed before creation. asc saves the one-time AuthKey_<KEY_ID>.p8 with mode 0600; OpenCI validates the result and saves key.json containing the Key ID, Issuer ID, provider IDs, role, and private-key path with mode 0600. Private key contents and raw asc output are never printed. asc handles any reauthentication through /dev/tty and applies its own network timeouts.

Setup prepares four secrets for the active team:

Secret name Value
OPENCI_GENERATED_ASC_KEY_ID The Key ID as text
OPENCI_GENERATED_ASC_ISSUER_ID The Issuer ID as text
OPENCI_GENERATED_P8_BASE64 The complete P8 file encoded as Base64, matching register secret-file
OPENCI_GENERATED_IOS_CERTIFICATE_PRIVATE_KEY A persistent RSA private key generated on the server if absent; existing keys are preserved

The server encrypts each value in its existing secrets table. Setup refreshes OpenCI authentication before saving and stops if the active profile or credentials changed while Apple authentication was in progress. Secret values are not logged. After saving the three ASC values, setup checks whether the certificate private key exists. If absent, it calls the server's iOS signing key endpoint and verifies that the secret was saved. The private key is not returned to the CLI. The server preserves existing keys even during concurrent requests. Deploy the server's updated key-generation endpoint before using this CLI update.

Success means the ASC saves succeeded and the certificate key is present; local ASC files are retained. Saves are sequential, so a failure or interruption can leave a partial update. Retry with the same local ASC key. This step prepares the private key; signing certificates and provisioning profiles are obtained during the build. Run openci sync --secrets afterward to generate the references used by workflows.

Creation is never automatically retried. On failure or interruption, downloaded files are retained; check the displayed directory and App Store Connect before issuing another key. If a key was saved locally, retry only the OpenCI save:

openci setup asc-keys --key-directory '/path/to/asc-api-keys/key-xxxxxx'

This reads key.json and its matching .p8, displays the destination and key IDs, and asks for confirmation. It does not install or run asc or contact Apple. Existing ASC secrets are replaced only after confirmation. Save or certificate key preparation failures exit with code 1 and print this retry command. Each retry saves all three ASC values, including any that were already saved, then prepares the certificate key if missing. Existing certificate keys are kept. Provider switching remains a future step. Use openci setup asc-keys --help to view its help.

To prepare only the certificate private key for the active OpenCI team, run:

openci setup ios-certificate-key

This command requires OpenCI login and a selected team. It generates and saves OPENCI_GENERATED_IOS_CERTIFICATE_PRIVATE_KEY on the server if missing, or keeps the existing key. It verifies that the secret is present before reporting success. It does not require Apple login, ASC keys, or local signing tools, and does not issue or replace ASC credentials. The private key is not downloaded or printed. You can rerun the same command after a failure. Run openci sync --secrets afterward to update the workflow secret references.

Code generation #

Run these commands from apps/openci_cli:

dart run slang
dart run build_runner build

Translations use slang.yaml through the Slang CLI. build_runner generates the Freezed and JSON models.

Secrets backend #

Secrets commands use the Firebase teamSecrets callable in asia-northeast1. The Firebase project is inferred from the current login ID token. Firebase Auth emulator profiles use the Functions emulator on loopback port 5001. Static API-key profiles must log in with Firebase. See the cutover guide for deployment and existing-secret migration.

0
likes
130
points
0
downloads

Documentation

API reference

Publisher

verified publisheropenci.org

Weekly Downloads

Command-line tools for OpenCI authentication, teams, secrets, App Store Connect setup, and workflow paths.

Repository (GitHub)
View/report issues

License

Apache-2.0 (license)

Dependencies

args, characters, cli_completion, cli_util, crypto, dart_console_plus, freezed_annotation, http, intl, json_annotation, mason_logger, meta, openci_shared, path, pub_semver, pub_updater, slang, yaml

More

Packages that depend on openci_cli_plus