License

Public repositories use TestDetta for free. Private repositories pass a license string to CI. A license problem never fails a build: without a valid license, every test runs, as it did before TestDetta.

Public repositories

A public repository needs no license, but TestDetta only knows a repository is public where the CI says so:

On Jenkins, Azure DevOps, Bitbucket, other CI systems and developer machines, every repository counts as private.

Passing the license

TestDetta looks for the license in this order:

  1. TESTDETTA_LICENSE: the license string, from a masked or secret variable;
  2. TESTDETTA_LICENSE_FILE: the path of a file holding it;
  3. ~/.testdetta/license: written by td license install and by automatic refresh;
  4. .testdetta/license in the repository.

A secret variable is the recommended way. In GitHub Actions, pass it through the action's license input:

- uses: <owner>/testdetta@<sha>
  with:
    mode: test
    license: ${{ secrets.TESTDETTA_LICENSE }}

Only td affected and td test check the license. td record runs every test anyway and needs none.

Checking it

td license status shows what the license found covers: its validity, the repositories it covers and the active committer count, without personal data. The same state appears in the log, the job summary and the JSON output whenever it matters: valid, expiring soon, over seats, expired, wrong scope, invalid or missing.

td license status

Machines without CI secrets

Where no secret variable can hold the license, store it once per machine:

td license install <license|file>

It is checked first and then written to ~/.testdetta/license.

Automatic renewal

A license carries the address it renews from. At most once a day, TestDetta fetches the current version and keeps it in ~/.testdetta/license, so renewals and added seats arrive without touching your secrets. The request sends the license in a header and nothing from your repository. It has a 2-second timeout and no retries, and any failure is ignored: an offline or blocked network costs at most 2 seconds a day. HTTPS_PROXY is honoured.

On a network that cannot reach the license service, set TESTDETTA_LICENSE_REFRESH=off and replace the license string yourself when you receive a new one.

Which repositories a license covers

A license names the repositories it covers, such as github.com/acme/*. TestDetta matches them against the repository's origin remote. Credentials embedded in the remote URL, common with Jenkins, are stripped before matching and never logged.

When the remote does not name the repository the license covers, a local mirror for example, set TESTDETTA_LICENSE_SCOPE:

TESTDETTA_LICENSE_SCOPE=git.acme.corp/scm/pay/app

Active committers

TestDetta is priced per active committer per month: a distinct commit author with at least one commit authored in the last 30 days, among the commits reachable from the analysed commit. Authors are counted locally, by email folded through .mailmap, and no email leaves your machines. Bots such as Dependabot, Renovate and github-actions are not counted; add your own patterns in .testdetta/license.json:

{ "bots": ["ci-service-account"] }

Up to 20% over the license's seats (at least one person) is fine. Above that, TestDetta keeps selecting for 30 days and warns in every summary, so there is time to add seats; only if the count is still over after that does every test run, as without a license. See Pricing.

Never a red build: an expired, missing or mismatched license makes TestDetta run every test and say why. It does not fail the job.