Skip to main content

genuineci_cli 0.4.0 copy "genuineci_cli: ^0.4.0" to clipboard
genuineci_cli: ^0.4.0 copied to clipboard

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

GenuineCI 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 genuineci_cli
genuineci --help

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

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 genuineci --no-check-updates <command> to skip the check explicitly. genuineci 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 genuineci --help or another command in an interactive terminal outside CI. Restart your shell afterwards, or install them explicitly and follow the printed instructions:

genuineci 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:

genuineci reg<Tab>                      # register
genuineci register <Tab>                # secret, secretFile, options
genuineci sync <Tab>                    # paths, secrets, options
genuineci dev start --s<Tab>             # --seed
genuineci use <Tab>                     # japanese, english, options
genuineci help register <Tab>           # secret, secretFile, options
genuineci login --ser<Tab>              # --server
genuineci login --server <Tab>          # default and saved HTTPS servers
genuineci 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 genuineci uninstall-completion-files to remove the scripts.

Usage #

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

genuineci 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 genuineci configuration directory, log in again; those credentials are not migrated.

Check the active profile, server, and current team:

genuineci 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 genuineci 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:

genuineci 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:

genuineci switch team

The CLI fetches your team memberships from the server saved in the active credential profile. A remote profile uses the HTTPS URL saved by genuineci login, including any --server override. A local profile created by genuineci login --local uses http://localhost:8080 and its saved Auth Emulator settings. 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 genuineci switch team again.

Subsequent genuineci list secrets, genuineci register secret, genuineci register secretFile, and genuineci sync --secrets commands use the newly selected team. Existing openci/generated/secrets.g.dart files are updated by running genuineci 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 genuineci login or genuineci 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 genuineci dev start is running.
Invalid team list Check that the saved server URL points to a compatible OpenCI API.
Changed profile or credentials Run genuineci 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 genuineci use english or genuineci use japanese. The language setting is saved for future invocations.

List secret names for the active profile's team:

genuineci 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.

Register a secret for the active profile's team:

genuineci 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 adding a secret, run genuineci sync --secrets from your workflow project to update its generated secret definitions.

Register a file as a Base64 secret:

genuineci 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. Run genuineci sync --secrets afterwards to update the generated definitions.

Run genuineci dev start from the OpenCI checkout to start local services and the Mac Orchard worker. Prepare the existing non-Firebase Docker Compose credentials and base-macos VM first. Docker Compose 2.24.4 or later is required for the local API override.

The command uses docker-compose.yml, docker-compose.local.yml, and docker-compose.local-api.yml together. It starts the Firebase Auth Emulator for project demo-openci and waits for its health check before starting Orchard Controller, the Mac worker, and the local API. No production Firebase service account is needed. The Auth endpoint is http://127.0.0.1:9099 and the Emulator UI is http://127.0.0.1:4000/auth. When restarting, the command stops the old build-job-worker and waits for running jobs to finish while the server remains available for saving results and deleting their VMs. It then rebuilds and starts the application containers. See the emulator setup for standalone start and stop commands.

The /internal seed and cleanup API is disabled by default. genuineci dev start automatically enables it by passing ENABLE_INTERNAL_API=true to Docker Compose. Requests also require INTERNAL_API_KEY. For genuineci dev start --seed, the CLI always reads it from the checkout's .env file and only seeds a local server. The dev command also excludes a shell-exported INTERNAL_API_KEY from Docker Compose so the server and seed request use the same key.

To also prepare a development Auth user and queue the default smoke-test build job:

genuineci dev start --seed

The CLI first creates an email/password user in the local demo-openci Auth Emulator and marks its email as verified:

  • Email: test@openci.org
  • Password: 123456

These fixed credentials are only for local development. Repeated seeding reuses the same user and sets emailVerified=true without resetting its password. If the account already exists with a different password or is disabled, seeding fails without changing it. Check the account in the Auth Emulator UI. An Auth failure stops seeding before the team/job request. Emulator users are lost when the emulator restarts; --seed creates the user again.

The local Compose override configures test@openci.org as the allowed email by default. Set LOCAL_ALLOWED_USER_EMAILS in .env to use another comma-separated allowlist; an empty value configures a deny-all policy. API enforcement is tracked in #2942.

The CLI sends that user's UID to the seed API, which registers it as a member of test-team before queuing the smoke-test job. Repeated seeding preserves the existing membership. If team or membership creation fails, the seed request fails without queuing a job. Direct /internal/seed callers can omit userId to seed only the team and job.

The server seeds test-team and one macos-latest job for openci-org/openci's openci/worker_smoke.dart, pinned to commit 5cb05f76d8941ea1edc3593eb753ce808f0f290e on test/build-job-worker-smoke-openci. The workflow checks macOS and Flutter versions and runs the worker's unit tests. It uses this fixed fixture, not uncommitted files in your local checkout.

The server resolves the installation ID using the GitHub App already configured in Docker Compose. That App must have access to openci-org/openci. Webhook reception and planning are separate from this smoke test.

Press Ctrl+C to stop the Mac Orchard worker and the local Docker Compose stack. The same shutdown also runs when the command exits or receives SIGTERM. Named volumes and local data are kept.

To log in locally, keep genuineci dev start --seed running, then run:

genuineci login --local

Enter test@openci.org and 123456, or the credentials of another user you created in the Auth Emulator UI. The CLI signs in through the fixed address 127.0.0.1:9099, sends the resulting Firebase ID token to http://localhost:8080/teams, and selects test-team only if the API returns it among your memberships. --seed prepares this membership for test@openci.org; you can use the same account in the local Dashboard. If test-team is missing, check that seeding completed and that you are using the seeded account.

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 genuineci 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.

Local login does not read Docker's INTERNAL_API_KEY. Worker and seed operations still use that key, and --seed does not require CLI login.

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

genuineci 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:

genuineci 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 getters that read environment variables at workflow runtime. Secret values are never downloaded or written to this file. Run it again after adding or removing secrets. Fetch or generation failures leave the existing file unchanged.

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

genuineci 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.

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.

0
likes
0
points
1.2k
downloads

Publisher

unverified uploader

Weekly Downloads

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

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

args, characters, cli_completion, cli_util, 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 genuineci_cli