Get started with the OyaCloud CLI

Upload test results from any CI pipeline or your own machine. OyaCloud compares every run with the right earlier one.

Sign-ups open shortly

The CLI is already on npm — npm install -g oyacloud works today, and this guide is current. Steps 1 and 2 need an OyaCloud account, available when the app goes live. Get notified.

Quick setup

  1. 1. Create an account

    Sign up — new accounts go through a quick manual approval before the dashboard unlocks.

  2. 2. Create a project and copy its API key

    In Projects, choose + New project. The key is shown as soon as the project is created; you can find it again under Settings → API Keys.

  3. 3. Install the CLI

    For uploads from your own machine. Requires Node.js 14 or later.

    npm install -g oyacloud

    On CI there’s nothing to install — the step in 7 runs it with npx.

  4. 4. Choose your test framework

    Steps 5 and 7 update to match.

  5. 5. Configure your reporter

    Add the JSON reporter to your Playwright config. Projects created with npm init playwright only have the HTML reporter, which OyaCloud can't read — keep it alongside if you use it:

    // playwright.config.ts
    reporter: [
      ['html'],
      ['json', { outputFile: 'test-results/results.json' }],
    ],

    Failure screenshots named in the report are uploaded and linked to their tests. Videos, traces and other files stay on your machine.

    Start each run with a clean results folder

    Nothing to do — Playwright empties test-results/ at the start of every run. That's also why the JSON file goes inside it: Playwright's own example writes results.json to the project root, outside the folder you upload.

    • Sharded runs: upload the merged report from npx playwright merge-reports, or every shard's test-results folder. If both end up in one folder, each test is still counted once.
    • Shards run one after another on one machine: each run empties test-results/, so give each shard its own outputDir, or use the merge step.
  6. 6. Store your API key in your CI

    Save it as a secret named OYACLOUD_API_KEY. Pick your platform — step 7 follows it.

    1. In your repository, open Settings → Secrets and variables → Actions.
    2. Choose New repository secret.
    3. Name it OYACLOUD_API_KEY, paste your key, and save.

    On your own machine, save it once instead:

    oyacloud config set-key YOUR_PROJECT_API_KEY
  7. 7. Add the upload step to your pipeline

    1. In the job that runs your tests, add this step after the test step:

      - name: Upload results to OyaCloud
        if: ${{ !cancelled() }}   # run even when tests fail
        env:
          OYACLOUD_API_KEY: ${{ secrets.OYACLOUD_API_KEY }}
        run: npx oyacloud upload --framework playwright_ts --environment staging --results-dir test-results
    2. Make the workflow run on pushes and on pull requests:

      on:
        push:
          branches: [main]
        pull_request:
    3. Push a commit and open the job log. On a push the upload step shows:

      ✅ Build Type: post-merge (detected from the GitHub Actions trigger)
      ✅ Uploaded · build …

      Results appear on the build shortly. What each run counts as:

      • Push to main → post-merge
      • Pull request → pr, with the PR number and target branch
      • Schedule → regression

      Manual runs (Run workflow) carry no trigger — set OYACLOUD_BUILD_TYPE.

    Good to know

    • --environment is where the tests ran — required for post-merge and regression.
    • The CI image needs Node.js 14 or later, and direct outbound HTTPS to api.oyaplus.com and Amazon S3 (*.amazonaws.com). Proxies (HTTPS_PROXY) aren’t supported yet — contact support@oyaplus.com.
    • Part of your suite? Add --suite-name — Run scope. Preview what would be sent with --dry-run.

    On your own machine, after the tests:

    oyacloud upload --framework playwright_ts --environment staging --results-dir test-results

Build types

Every upload has a build type. It decides what the run is compared with.

Pull request

pr

Tests on a pull request. Compared with the latest post-merge run on the target branch, so you see which tests newly fail, newly pass or became flaky in this PR.

Post-merge

post-merge

Tests after a merge. Compared with the previous post-merge run on the same branch and environment. These runs are the baseline for every PR into that branch.

Regression

regression

Scheduled or on-demand runs not tied to a PR — nightly suites, environment checks. Compared with the previous regression run in the same environment, with their own trend.

Local

local

A run from your own machine. Kept in your build history, but not compared with a baseline and never changes a test’s flaky status. Set with --build-type local.

How the build type is chosen

The first of these that applies wins:

  1. The --build-type flag.
  2. The OYACLOUD_BUILD_TYPE environment variable.
  3. Your CI platform’s trigger — what started the job, not the branch name:

Fully automatic

GitHub Actions · GitLab CI · Azure DevOps

PR, post-merge and scheduled runs are all detected from the platform’s trigger.

PR and post-merge automatic

Jenkins · CircleCI · Bitbucket

For scheduled runs, set OYACLOUD_BUILD_TYPE to regression in that job.

Set it yourself

Other platforms

Pass --build-type or set OYACLOUD_BUILD_TYPE.

  1. Otherwise, from the branch details: a PR number makes it pr; a commit on main, master, develop or your project’s base branch makes it post-merge; anything else is regression.

CI details

The CLI reads branch, commit, PR number, target branch and author from your CI platform's environment and attaches them to every build. A flag always overrides what was detected; a field with no source is left blank.

FieldGitHub ActionsGitLab CICircleCIJenkinsAzure DevOpsBitbucket
Branch✓✓✓Multibranch only✓✓
Commit✓✓✓✓✓✓
PR number✓✓usually*Multibranch only✓✓
Target branch✓✓—Multibranch only✓✓
Author✓✓✓✓✓✓
  • GitHub Actions — PR numbers are read for both pull_request and pull_request_target events. On a pull request the commit is the PR’s own latest commit, not the temporary merge commit GitHub tests.
  • CircleCI* — depending on project settings, CircleCI may not provide the PR number for PRs from the same repository. It never provides the target branch; pass --target-branch.
  • Jenkins — branch, PR number and target branch need a Multibranch Pipeline with PR discovery. Freestyle jobs get the commit only.

Run scope

OyaCloud compares a run only with earlier runs of the same scope, so a smoke run is never measured against your full suite. A full run needs nothing; a partial run needs a name — --suite-name.

Add --suite-name for…

  • ✓Playwright tag runs — the report doesn’t record --grep @smoke. Pass --grep @smoke or --suite-name to the CLI too.
  • ✓Playwright folder runs — running one subfolder looks like a full run otherwise.
  • ✓TestNG group runs — -Dgroups=smoke without a testng.xml carries no suite name of its own.
  • ✓Separate suites — e.g. “Payments” and “Auth” with their own trends.

Not needed for…

  • •Full-suite runs — shown as “Full Execution”.
  • •Named TestNG suites — the suite name in your testng.xml is used.

Sensitive data

OyaCloud analyses what your test reports contain — test names, error messages, stack traces and screenshots. The CLI doesn't redact anything, so keep secrets and personal data out of your reports.

What leaves your runner

  • •The report files for your framework, from the results folder you point the CLI at — including any console output your reporter writes into them.
  • •Screenshots — only those the report names, or that you add with --attachments. Videos and Playwright traces are never uploaded.
  • •Build details — branch, commit, PR number and author.
  • •For the AI summary, failing tests’ names and the first 300 characters of each error message go to AWS Bedrock, in the same AWS infrastructure. Stack traces, screenshots and console output are not sent.

Keep out of your reports

  • ✓Secrets — tokens, API keys, passwords and connection strings. They leak through assertion messages and logged request headers; don’t print them in tests, and keep your CI’s secret masking on.
  • ✓Personal data — test against test accounts and synthetic data, not real customers.
  • ✓Screens with real data — screenshots show whatever was on screen when a test failed.
  • ✓Verbose logging — full request and response bodies in reporter output end up in the report file.

Check before your first upload. --dry-run selects the files and builds the zip without sending anything, and lists exactly what would be uploaded.

How long it’s kept. Uploaded report files and screenshots are deleted after 30 days; test results, error messages and stack traces after 90 days.

If something slipped through, rotate the secret first, then email support@oyaplus.com with the project and build, and we’ll delete it.

CLI reference

All options belong to oyacloud upload.

Required

--frameworkRequired: AlwaysOne of playwright_ts · playwright_py · testng_java · pytest_python · junit_java · cypress_js. Short forms testng, pytest, junit and cypress also work.
--results-dirRequired: Or --resultsThe folder your test run writes its report to. Repeatable. The CLI uploads only the reports for your --framework and the screenshots they name, and lists everything it leaves behind. The folder should hold only this run's files.
--environmentRequired: Post-merge, regressionWhere the tests ran — any label you use (staging, qa, prod). Optional for PR and local builds.
--api-keyRequired: Unless savedYour project API key. Not needed when OYACLOUD_API_KEY is set or a key is saved with oyacloud config set-key.

What to upload

--resultsReport files by pattern instead of a folder, e.g. '**/target/*-reports/TEST-*.xml'. Repeatable; keep the quotes.
--attachmentsScreenshots the report doesn't name (Selenium listeners, JUnit extensions), e.g. 'target/screenshots/*.png'. Repeatable; keep the quotes.
--dry-runShow exactly what would be uploaded, without uploading.
--verboseList every file left behind, instead of a summary per reason.

Build details

Detected automatically on most CI platforms — pass a flag only to override.

--build-typepr · post-merge · regression · local — see Build types.
--branchSee CI details for which platforms provide it.
--commitAs --branch.
--target-branchAs --branch. CircleCI never provides it — pass it there for accurate PR comparisons.
--pr-numberAs --branch.
--authorRead from git on every platform, including your own machine.

Run scope

--suite-nameName for a partial run (a tag, a folder, a group) so it's compared only with runs of the same scope — see Run scope.
--grepThe tag or pattern the run was filtered to, e.g. @smoke — the same value you passed to playwright test --grep. Sets --suite-name when that isn't given.

Exit codes

CodeMeaning
0Uploaded. Results are counted in the background and appear on the build.
2Nothing to upload — the message says what to change.
1Anything else (network, API key, limits).

Limits

Per upload: 100 MB zipped, 200 MB of reports unzipped, 2,000 files, and 50 MB per report. Screenshots over 5 MB are skipped and listed, not treated as an error.

Network

On a self-hosted runner behind a firewall, allow outbound HTTPS to both:

  • api.oyaplus.com — creates the build and returns its upload link
  • *.s3.amazonaws.com — receives your reports as one zip, through a short-lived signed link
Outbound proxies aren’t supported yet. The CLI doesn’t read HTTPS_PROXY, HTTP_PROXY or NO_PROXY, so runners that must go through a proxy can’t upload.

Supported frameworks

--frameworkToolDefault results folder
playwright_tsPlaywright (TypeScript/JS)test-results/
playwright_pyPlaywright (Python)test-results/ (pytest --junitxml output)
testng_javaTestNG (Java)target/surefire-reports/
pytest_pythonpytest (Python)pytest-results/ (wherever --junitxml writes)
junit_javaJUnit (Java)build/test-results/test/ (Gradle) · app/build/test-results/test/ (gradle init) · target/surefire-reports/ (Maven)
cypress_jsCypresscypress/results/ (with mocha-junit-reporter)

Troubleshooting

The CLI says…

MessageWhat to do
API key requiredNo --api-key, no OYACLOUD_API_KEY, and no saved key. oyacloud config show shows what's saved on this machine. On CI, check the secret's name, that GitLab's Protect variable is off, and whether the run is a pull request from a fork — GitHub and CircleCI don’t pass secrets to those.
API Error 401: Invalid or expired API keyThe key is wrong or was revoked. Check it under Settings → API Keys.
API Error 401: CLI uploads require a project keyThat's a company key. Use the key from the project's settings.
API Error 426: This OyaCloud CLI version is no longer supportedYour CLI is older than 1.4. Run npm install -g oyacloud@latest and try again.
Results folder not found--results-dir doesn't exist relative to where the command ran — common on CI when the working directory isn't what you expect. Try an absolute path.
No … report found in …The folder holds no report for your --framework. The message lists what it did find and what to add — for Playwright, usually the JSON reporter (Quick setup step 5).
… this looks like … — use --framework … / This is TestNG output--framework doesn't match what your tests wrote. Use the value the message names. Nothing was uploaded.
… over the 50 MB per-report limit / … the limit is 100 MB per build / … the limit is 2000 per build / … the limit is 200 MB per buildAn upload limit was hit — see Limits. Point --results-dir at a narrower folder or split the run.
getaddrinfo ENOTFOUND / connection errorsThe runner can't reach api.oyaplus.com or the upload host — see Network.
Invalid JSON response containing HTMLA firewall, proxy or WAF answered instead of the API. Check your network setup.
API Error 403: trial expired / subscription cancelled or suspendedYour subscription is blocking uploads; the message says why. Check Settings → Billing or contact support@oyaplus.com.
API Error 429: Test result limit reached for this periodYour plan's test results for this period are used up, including the 20% allowance (the message shows usage). Move to a bigger plan in Settings → Billing, or contact support@oyaplus.com.
API Error 429: Upload limit reached for this periodThe fair-use upload limit for this period is reached. Contact support@oyaplus.com if your pipelines need more.

Your dashboard shows…

On the buildWhat to do
More tests than your suite hasThe folder held reports for tests that no longer exist — usually a renamed or deleted Maven test class. Start each run with a clean folder (Quick setup step 5); the build also lists reports much older than the rest.
Cypress: skipped tests missingSet includePending: true in mocha-junit-reporter's reporterOptions (Quick setup step 5).
1 of N reports could not be readA report is damaged — usually cut off because the test run was killed while writing it. The build is marked failed rather than shown with tests missing. Run the tests again.
Conflicting results for N test(s)Two reports hold the same tests with different results, and nothing says which is newer — usually an old copy left in the folder. Start with a clean folder.
The upload never completedThe CI job stopped (cancelled, timed out) after the build was created but before the upload finished. Run the upload again.