Documentation
CLI Reference
Upload test results to OyaCloud from any CI/CD pipeline or your local machine.
Sign-ups open shortly
The CLI is already published on npm — npm install -g oyacloud works today, and this reference is current. Steps 1 and 2 below need an OyaCloud account, which unlocks when the app goes live. Get notified.
Quick setup
1. Get access
No account yet? Sign up — new accounts go through a quick manual approval before the dashboard unlocks.
2. Create a project and get your API key
- Go to app.oyaplus.com/analytics/projects → + New project → name it
- Your API key is generated right then and shown with a copy button — grab it now
- You can always find it again later under Settings → API Keys
3. Install the CLI
Requires Node.js 14 or later.
npm install -g oyacloud
4. Choose your test framework
Pick the one you actually use — steps 5 and 7 below change to match it.
5. Configure your reporter
Pick a framework in step 4 above to see this step.
6. Save your API key
Running on CI? Set a secret/variable named exactly OYACLOUD_API_KEY in your CI platform, and every future command needs no --api-key flag at all.
- GitLab CI / CircleCI — that's the whole thing, both expose it to your job automatically
- GitHub Actions / Jenkins — needs one extra line to map it in, pick your platform below to see it
Repo secrets aren’t auto-exposed as env vars — map it explicitly, once, in the workflow:
env: OYACLOUD_API_KEY: ${{ secrets.OYACLOUD_API_KEY }}Not setting up a CI secret at all? Just pass --api-key YOUR_KEY directly on the command instead — or, for local/manual runs, save it once:
oyacloud config set-key YOUR_PROJECT_API_KEY
7. Upload your results
oyacloud upload --framework <framework> --environment staging --results-dir <results-dir>
Pick a framework in step 4 above to fill in the exact command.
A green 🎉 Upload completed successfully! here means the file was received and queued — it doesn't mean it was parsed correctly. Processing happens afterward, asynchronously; if the build doesn't show up looking right, your OyaCloud dashboard is the real source of truth, not this terminal output.
Two things worth knowing before you rely on this data:
- Every build is auto-classified as PR / post-merge / regression from your CI trigger on most platforms — see Automatic build-type detection below for exactly how, or what to do if your platform isn't one of them.
- Running a scoped/filtered subset of your suite (a tag filter, a specific folder) rather than the full thing? Pass --suite-name, or it silently merges into your full-suite history — see the --suite-name guide below.
Automatic build-type detection
OyaCloud figures out whether a run is a PR, a post-merge build, or a scheduled/regression run directly from your CI platform’s own trigger signal — not by guessing from branch names. How much of that is automatic depends on which platform you’re on:
Tier 1
Zero config
GitHub Actions · GitLab CI · Azure DevOps
PR, post-merge, and scheduled runs are all auto-detected from your CI platform’s own trigger signal — nothing to set.
Tier 2
PR + post-merge auto-detected
Jenkins · CircleCI · Bitbucket
Scheduled/cron runs need one line: OYACLOUD_BUILD_TYPE=regression — none of these platforms expose a trigger-source signal reliable enough to detect a schedule automatically.
Tier 3
Explicit only
Everything else
Pass --build-type explicitly — an unrecognized platform is never guessed at.
Passing --build-type or setting OYACLOUD_BUILD_TYPE always overrides detection, on any platform.
The three build types
The same command works for every scenario below and every supported CI platform:
- Branch, commit, PR number, and author are all auto-detected (see "What gets auto-detected" below)
- build_type is detected too, on most platforms — see the tiers above
- --environment is the one flag that's genuinely yours to set — point it at whatever CI variable your own pipeline already uses (shown here as $ENVIRONMENT, illustrative — use your own variable name)
oyacloud upload \ --framework <framework> \ --environment $ENVIRONMENT \ --results-dir <results-dir>
<framework> and <results-dir> are whatever you picked in Quick setup steps 4-5 above. No --api-key shown — see Quick setup step 6 above for how that gets picked up automatically.
PR / Feature branch
pr
Tests run on every pull request. OyaCloud compares your results against the latest post-merge baseline on the target branch, and the dashboard shows which tests newly failed, newly passed, or became flaky in this PR.
Post-merge / Main branch
post-merge
Tests run after a PR merges. These become the health baseline — pass-rate trends, flaky detection, and every future PR comparison are measured against the most recent post-merge run on the target branch.
Standalone regression
regression
Scheduled or on-demand runs not tied to a specific commit or PR — nightly suites, environment health checks. Tracked in their own trend line, separate from PR and post-merge history.
What gets auto-detected
Different from build_type detection above — this is the underlying data (branch, commit, PR number, target branch, author) the CLI reads directly from your CI platform's own environment variables and attaches to every build:
- Nothing to configure — pulled straight from real environment variables
- Nothing ever guessed — a field with no real source (marked — below) is just left blank, exactly like omitting the flag
- Passing the flag explicitly always overrides whatever was detected
| Field | GitHub Actions | GitLab CI | CircleCI | Jenkins | Azure DevOps | Bitbucket |
|---|---|---|---|---|---|---|
| Branch | ✓ | ✓ | ✓ | Multibranch only | ✓ | ✓ |
| Commit | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| PR number | ✓ | ✓ | usually* | Multibranch only | ✓ | ✓ |
| Target branch | ✓ | ✓ | — | Multibranch only | ✓ | ✓ |
| Author | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
- GitHub Actions — PR number detection covers both pull_request and pull_request_target events.
- CircleCI* — PR number isn't always populated for same-repo (non-fork) PRs, depending on project settings. CircleCI's own documented limitation, not something OyaCloud's CLI can work around.
- Jenkins — only gets branch/PR-number/target-branch on a Multibranch Pipeline job configured with PR discovery. A plain freestyle job has none of these (commit still works, via the Git plugin).
Reference
All flags
| Flag | Required | Description |
|---|---|---|
| --framework | Yes | One of: playwright_ts · playwright_py · testng_java · pytest_python · junit_java · cypress_js |
| --environment | Yes | Where tests ran — a free-form label (staging, qa, prod, whatever you use). Not validated against a fixed list. |
| --results-dir | Yes | Path to the folder containing your result files |
| --api-key | Yes* | Project API key. On CI: set it once as OYACLOUD_API_KEY instead (see Quick setup step 6) and skip this flag entirely. |
| --build-type | No* | post-merge · pr · regression · local If omitted, inferred from branch/commit/PR context — see the note below the table. |
| --branch | No | Auto-detected on GitHub Actions, GitLab CI, CircleCI, and Jenkins Multibranch Pipelines. Pass explicitly to override, or on an unsupported CI — see "What gets auto-detected" below. |
| --commit | No | Same auto-detection as --branch. |
| --target-branch | No | Same auto-detection as --branch. Gap: CircleCI has no target-branch equivalent at all — pass it explicitly there if PR-comparison accuracy matters to you. |
| --pr-number | No | Same auto-detection as --branch. |
| --suite-name | No | Display label for partial/scoped runs — see the guide below. Optional for every build type (PR, post-merge, regression, local) equally. |
| --grep | No | Tag/pattern this run was filtered to, e.g. @smoke — matches whatever you passed to playwright test --grep. Auto-fills --suite-name when that flag isn't set explicitly. Playwright's own JSON report has no reliable way to recover this value after the fact, so it has to be passed here too — without it, a tag-filtered run is scoped as a full run and gets compared/flaky-tracked against runs of a completely different size. |
| --author | No | Auto-detected everywhere via git log — doesn't need any CI-specific variable, works identically on every platform and locally. |
How build_type actually gets resolved if you don't pass --build-type, in order:
- --build-type flag, if passed — always wins, on every platform
- else OYACLOUD_BUILD_TYPE environment variable, if set
- else your CI platform's own trigger signal, detected automatically with zero config on GitHub Actions, GitLab CI, and Azure DevOps (see the tiers above)
- last resort, only if none of the above apply — the backend infers it from whatever branch/commit/PR-number it has:
- --pr-number present (explicit or auto-detected) → pr
- else --commit present and --branch is main/master/develop (or matches your project's configured base branch) → post-merge
- otherwise → regression
In practice, step 3 resolves it correctly before it ever reaches the backend's inference at all, on most platforms — pass --build-type explicitly only if you want to be certain, or your branch naming doesn't match your project's configured base branch.
Network requirements
On a self-hosted CI runner behind a firewall, allowlist outbound HTTPS to both of these — uploads use both on every run:
Proxy environments aren't supported yet. The CLI doesn't read HTTPS_PROXY/HTTP_PROXY/NO_PROXY — on a runner that requires an outbound proxy (not just an allowlisted firewall), uploads won't get through no matter how the proxy itself is configured.
Supported frameworks
| --framework value | Tool | Typical results path |
|---|---|---|
| playwright_ts | Playwright (TypeScript/JS) | test-results/ |
| playwright_py | Playwright (Python) | test-results/ (pytest --junitxml output) |
| testng_java | TestNG (Java) | target/surefire-reports/ |
| pytest_python | pytest (Python) | pytest-results/ (varies by --junitxml config) |
| junit_java | JUnit (Java) | build/test-results/test/ (Gradle) · target/surefire-reports/ (Maven) |
| cypress_js | Cypress | cypress/results/ (requires mocha-junit-reporter — see note below) |
Cypress note: Cypress's own test runner doesn't produce OyaCloud-readable output directly — add mocha-junit-reporter (the standard way to get CI-consumable output from Cypress, since Mocha is its test runner underneath) and point --results-dir at wherever it writes its XML.
Short aliases: testng, pytest, junit, and cypress also work as shorthand for testng_java, pytest_python, junit_java, and cypress_js.
--suite-name guide
--suite-name sets the display label shown in the dashboard and becomes the run scope — the key used to group and compare builds over time. It's equally optional across every build type (PR, post-merge, regression, local) — nothing about build type makes it required.
Use it when…
- ✓TestNG group runs — -Dgroups=smoke without a named testng.xml writes the generic "Surefire suite" name, which gets grouped with full runs unless you set this.
- ✓Playwright folder runs — running a subfolder with no --grep filter has no self-describing scope; it'll show as "Full Execution" unless you set this.
- ✓Named test groups — e.g. separate "Payments Suite" from "Auth Suite" trends.
- ✓Playwright tag runs — --grep @smoke filtering isn't visible in Playwright's JSON report, so pass --suite-name explicitly or your scoped run gets merged into the full-suite scope.
Skip it when…
- •Full suite runs — scope defaults to "Full Execution".
- •TestNG suite XML — a real, named testng.xml suite name is read automatically.
Troubleshooting
| What you see | What it means |
|---|---|
| API key required | No --api-key flag, no OYACLOUD_API_KEY env var, and no key saved via oyacloud config set-key. Run oyacloud config show to check what's saved locally. |
| API Error 401: Invalid or expired API key | The key is wrong, revoked, or mistyped. Double-check it against Settings → API Keys. |
| API Error 401: CLI uploads require a project key | You're using a company-level key instead of a project key — generate a project-scoped one from that project's settings. |
| Results directory not found | --results-dir doesn't exist relative to where the command ran — a very common CI issue when the working directory isn't what you expect. Try an absolute path. |
| No supported files found in results directory | The directory exists but nothing in it (recursively) matches .json .xml .html .png .jpg .jpeg .log .txt — usually means your reporter wrote somewhere else, or hasn't run yet. |
| Skipping oversized file | Not an error — a warning. Any single file over 10MB (a chatty test's embedded stdout/stderr is the usual cause) is skipped and not uploaded; everything else in your results directory still uploads normally. |
| Total upload size too large / No files left to upload after skipping oversized files | Your total upload (after any oversized files are skipped) is still over 100MB, or every file in the directory was too large to upload — try uploading a smaller/scoped results directory. |
| Too many files: max 500 per build | Your results directory (recursively) has more than 500 files matching the supported extensions — scope --results-dir to a narrower directory. |
| File type not allowed: … | A file the CLI selected isn't one of the supported extensions after all — this is a backend-side check distinct from the CLI's own (silent) skip of unsupported files; if you see this, it's worth a report to support@oyaplus.com. |
| getaddrinfo ENOTFOUND / connection errors | The runner can't reach api.oyaplus.com or the S3 upload host — check the Network requirements above against your firewall. If your runner requires an outbound proxy rather than a firewall allowlist, see the proxy note above — that's not yet supported. |
| Invalid JSON response containing HTML markup | The request was blocked before it ever reached the API — a firewall, proxy, or WAF returned an HTML error page instead. Check your network/proxy configuration (same guidance as the ENOTFOUND row above), not an API bug. |
| API Error 403: trial expired / subscription cancelled or suspended | Your account's subscription status is blocking uploads — the exact message (trial expired, cancelled, or a failed payment) tells you which. Contact support@oyaplus.com or check Settings → Billing. |
| API Error 429: Build limit reached for this period | You've hit your plan's monthly build limit — the message includes your current usage (e.g. 50/50). Contact support@oyaplus.com to upgrade. |
| Not shown by the CLI — appears as the build's error on your dashboardDeclared framework '…' but no uploaded file matches that format | --framework doesn't match what your reporter actually produced — e.g. passing testng_java for plain JUnit XML output. The CLI itself still prints a green success message either way (see step 7's note above) — this only ever shows up as a failed build on the dashboard. |
Questions? support@oyaplus.com
Get started