genuineci_cli 0.0.3
genuineci_cli: ^0.0.3 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 the same install command to update to the latest published version.
The remote commands work from any directory. Local development commands require an OpenCI checkout and the services described below.
Usage #
Show the saved active profile, server, and selected team ID:
genuineci status
For example:
Profile: remote
Server: https://ci.example.com
Selected team ID: team-123
This reads the local configuration and works offline. It does not refresh tokens or verify server access. Team IDs are shown because team names are not stored in the profile. A fresh installation shows a login hint. Unreadable credentials or a missing active profile return exit code 1.
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.
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/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.
With an authenticated credential profile, generate typed secret definitions from your workflow project:
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/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/paths.g.dart following the directory hierarchy. For example,
apps/dashboard/android/app becomes
WorkspacePaths.root.apps.dashboard.android.app. Hidden directories, symlinks,
and generated or dependency directories (build, coverage, node_modules,
Pods, ephemeral, and xcuserdata) are excluded at every level. Directory
names determine the getters; package names are used to validate the workspace.
Run it again after adding, moving or renaming workspace directories. Read or
generation failures preserve the existing file.
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 '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.