Request a diagnosis

A specialist reads your repositories with you, on a call.

Measure

Continuous measurement in your pipeline

Add the ScaleQuality check to your CI so every build re-measures the repository, and, when you want it to, fails below the maturity line you set.

How it works

A diagnosis run inside the product is a photograph: the state of a repository at one moment. The CI check turns it into a film. On every build the CLI sends the checked-out repository to ScaleQuality, the engine re-measures it, and the result lands on that repository's timeline in the product.

The same step is also a gate. Give it a code-maturity cutoff and it exits non-zero below that number, which fails the job. Leave the cutoff off and it measures on every build without ever blocking a merge. Only a measurement fails the job: when ScaleQuality cannot measure a build, the step says it was not measured and exits 0.

A project can be one folder of a monorepo. The check asks ScaleQuality which folders the upload measures (the module in --project and the modules the change touched) and reads the durability of each folder on its own, from the same history, as ScaleQuality reads a module. A check before 0.5.8 sends only the whole repository's, and the module shows durability as not measured.

StageWhat happens
CheckoutYour job checks out the repository with its full git history. The check reads the working tree from disk, so it has to run after the checkout step and from the repository root.
UploadThe CLI packs the working tree and the .git directory, skipping build output and dependency folders, and sends it to ScaleQuality over HTTPS.
MeasureThe engine re-runs the code maturity analysis, security, reliability, maintainability and supply chain, plus the AI durability attribution that reads git blame.
ReportThe score is written to the repository's timeline in the product. The CLI prints the result and exits 0, or 1 when the score sits below your cutoff.

Before you start

What you need

  • A ScaleQuality account on the Pro plan or above. Continuous measurement is part of Pro.
  • A repository already connected to ScaleQuality, with at least one diagnosis run. That run is what gives you the project id.
  • Permission to add secrets to the pipeline you are about to edit.
  • Node.js 18 or later on the CI runner, so npx can fetch the check.
  • A checkout with full git history in the job. AI durability is attributed with git blame, and a shallow clone truncates it.
Note. If the setup dialog shows a lock instead of a token, the organization is on a plan below Business. Everything on this page still applies once the plan changes; nothing about the snippet is different.

Set it up

Four steps, around ten minutes. Steps 1 and 4 happen inside ScaleQuality, steps 2 and 3 inside your CI.

Create your SQ token

The token is what lets a pipeline report a measurement. It is scoped to your organization, so one token could serve every repository, but create one per pipeline: revoking a single pipeline's access should not stop the others from reporting.

  1. In ScaleQuality, open the repository's diagnosis.
  2. Choose Turn on continuous measurement.
  3. Press Generate token.
Important. The full secret is shown once, at creation. Copy it before closing the dialog. Afterwards only the prefix is visible, and a lost token is replaced, never recovered.

Tokens default to a 90-day expiry. To rotate one without a gap in coverage:

  1. Generate the new token and copy it.
  2. Update the CI secret in every pipeline that uses the old one.
  3. Revoke the old token under Settings, Security, CI tokens.

Revoking takes effect immediately: the next run of any pipeline still holding that token fails to report. Revoke last, never first.

Store the token as a CI secret

The snippets read the token from the environment as SQ_TOKEN and never carry the value itself. Add it to your CI's secret store under exactly that name.

CIWhere to add itName
GitHub ActionsSettings, Secrets and variables, Actions, New repository secret.SQ_TOKEN
GitLab CISettings, CI/CD, Variables. Tick Masked, and Protected if only protected branches run the job.SQ_TOKEN
BitbucketRepository settings, Repository variables, with Secured ticked.SQ_TOKEN
Azure PipelinesPipeline, Variables, with Keep this value secret ticked, or a variable group backed by Key Vault.SQ_TOKEN
CircleCIProject settings, Environment variables, or a context when several projects share the token.SQ_TOKEN
JenkinsManage Jenkins, Credentials, Secret text, then bind it with withCredentials in the job.SQ_TOKEN
Important. Never commit the token, and never paste it into the YAML. A token that reaches a build log is a token you have to rotate, and build logs are usually readable by more people than the repository is.

Add the check to your pipeline

Pick your CI below and copy the step. Paste your project id first and every snippet on this page fills it in; you will find it in the URL of the repository's diagnosis, and in the snippet the setup dialog shows.

GitHub Actions
# .github/workflows/scalequality.yml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0   # full history: durability is measured with git blame
- run: npx @scalequality/check@0.5.8 --token "$SQ_TOKEN" --project <projectId> --host https://app.scalequality.io
  env:
    SQ_TOKEN: ${{ secrets.SQ_TOKEN }}
Full git history is required. Every snippet checks the repository out with full history: fetch-depth 0 on GitHub Actions, GIT_DEPTH 0 on GitLab, clone depth full on Bitbucket, fetchDepth 0 on Azure Pipelines. AI durability is attributed with git blame, and a shallow clone hides the commits that attribution needs. With a shallow clone the check sends nothing: the build is reported as not measured, with the fix in the warning, instead of a number that comes out wrong.

The step has to run after the checkout and from the repository root. A job that runs it earlier uploads an empty folder and still produces a score.

Break the build below your line

The gate is a single number: the code maturity score, from 0 to 100, under which the build fails.

  1. Back in the setup dialog, tick fail builds.
  2. Drag the slider to the score you want to hold.
  3. Press Turn on.

Leave fail builds unticked and the check still measures every build and still writes the timeline, but it never blocks a merge and the exit code stays 0.

Note. Run track-only for a week first, look at where the repository actually sits, then set the line at or just below today's score. A line above the current score fails every build from day one, which teaches the team to bypass the gate rather than to raise the score.

Measured test coverage

Nothing computes coverage by looking at a repository, and no tool does: coverage only exists once the tests actually run and the runner writes a report. What ScaleQuality does is import that report. What is different is that you do not have to tell us where it is.

Run your tests with coverage before the check step and the verdict shows line coverage, labelled MEASURED, naming the report it came from. Without a report the verdict answers a narrower question, and says which one.

Produce the report

Add coverage to the test command you already run in this job. Each of these writes to a path the check finds on its own.

StackAdd to your test stepWrites
Mavenmvn -B verifytarget/site/jacoco/jacoco.xml
Gradle./gradlew test jacocoTestReportbuild/reports/jacoco/test/jacocoTestReport.xml
Jest / Vitestnpm test -- --coveragecoverage/lcov.info
pytestpytest --cov --cov-report=xmlcoverage.xml
.NETdotnet test --collect:"XPlat Code Coverage"**/TestResults/**/coverage.cobertura.xml
Gogo test -coverprofile=coverage.out ./...coverage.out
PHPUnitvendor/bin/phpunit --coverage-clover build/logs/clover.xmlbuild/logs/clover.xml
cargo-llvm-covcargo llvm-cov --lcov --output-path lcov.infolcov.info
SimpleCovbundle exec rspeccoverage/lcov.info
Note. The check looks in the default output directory of every build tool above, and picks up a report anywhere in the tree by name. A multi-module build is summed across its modules. You only need the file below when your report lands somewhere unusual.

scalequality.properties

Optional, at the repository root, in the same shape as sonar-project.properties so nobody has to learn a second dialect. Unknown keys are ignored, so a file written for a newer engine never breaks an older one.

scalequality.properties
# Optional. Only needed when the defaults do not fit.

# Where your coverage report lands, if it is not in the usual place.
sq.coverage.reportPaths=target/site/jacoco/jacoco-aggregate.xml

# Files that are not application modules, so they stop counting as untested.
sq.exclusions=src/main/java/**/generated/**,**/*.pb.go

# Test roots and class suffixes your house uses that nobody else does.
sq.test.inclusions=qa
sq.test.suffixes=Should,Fixture
KeyMeaning
sq.coverage.reportPathsComma-separated report paths. Set this only when discovery does not find yours.
sq.exclusionsGenerated code, migrations, DTOs. Excluded files stop counting as untested modules.
sq.test.inclusionsExtra directories that hold tests, for a layout outside the usual test/ and src/test/.
sq.test.suffixesClass suffixes that mark a test in your house style, on top of Test, Tests, IT and Spec.
Without a report, the number answers a different question. The verdict then shows the share of source modules that have a test file, labelled as such, and never as a line percentage. It is a real signal about test structure, and it is not line coverage: one test class exercising five production classes reads as one covered module out of five. A report removes the ambiguity.

CLI reference

The check is published on npm as @scalequality/check. The snippets pin a version on purpose: a gate that can break a build must not change engine between two runs of the same commit.

npx @scalequality/check@0.5.8 --help

Options

FlagEnvDefaultMeaning
--tokenSQ_TOKENRequiredYour SQ token.
--projectSQ_PROJECTRequiredThe ScaleQuality project id.
--hostSQ_HOSTapp.scalequality.ioAPI host. Only change it if you are pointing at a different ScaleQuality environment.
--imageSQ_IMAGEoffAlso scan the container image this job just built, for example myorg/api:sha. Needs trivy on PATH; without it the image scan is skipped, it says so in the log, and the build continues.
--timeout—600Seconds to wait for the measurement before giving up, and separately for a measurement of the same project that is already running.
--strictSQ_STRICToffFail the step with exit code 2 when the build could not be measured. Without it, such a build is reported as NOT MEASURED and exits 0.
--json—offPrint the raw result as JSON instead of the human summary.
--help——Print the options and exit.

Exit codes

Exit codeMeaning
0Measured and passed, measured in track-only mode, or not measured: ScaleQuality could not measure this build, or the platform refused it (an expired token, the plan, continuous turned off). The log and the job summary then say NOT MEASURED, never that the gate passed.
1Measured: the score is below your cutoff, or a new dependency carries a license your organization blocks. The job fails.
2Not measured, with --strict. Without --strict the same build exits 0.

Only a measurement fails the build. A problem on our side, an expired token or a lapsed plan never turns your build red unless you pass --strict, and such a build never reads as passed: the step leaves a warning on the run and in the job summary saying it was not measured, and why.

Troubleshooting

The failures below are the ones that show up in real pipelines, roughly in the order they tend to happen.

What you seeWhat it meansFix
ScaleQuality: NOT MEASURED. --token and --project are requiredThe secret never reached the step. On GitHub Actions this is almost always a missing env block on the run step. Pull requests from a fork or from Dependabot never receive repository secrets, so they are reported as not measured by design.Map the secret onto the step explicitly, exactly as the snippet does with its env block.
ScaleQuality: NOT MEASURED. The SQ token was refused / The SQ token expiredThe token expired, was revoked, or belongs to another organization.Issue a new token in the setup dialog, update the CI secret, then revoke the old one, in that order.
ScaleQuality: NOT MEASURED. Continuous measurement needs an active subscription, or a lock in the setup dialogContinuous measurement is part of the Pro plan and the organization sits below it.Change the plan and reopen the dialog. The token and the snippet are unaffected.
ScaleQuality: NOT MEASURED. Continuous measurement is turned off for this projectSomeone turned continuous measurement off for this project in ScaleQuality. The step keeps running and measures nothing.Turn it back on in the setup dialog, or remove the step from the pipeline.
ScaleQuality: NOT MEASURED. Git durability collection failed (shallow Git history ...)The job used a shallow clone, so the 90-day history git blame needs is not there and nothing is sent.Set full history on the checkout: fetch-depth 0, GIT_DEPTH 0, clone depth full, or fetchDepth 0, depending on the CI.
A score came back but the repository looks emptyThe check ran before the checkout, so it uploaded an empty working directory.Move the step after the checkout and make sure it runs from the repository root.
ScaleQuality: NOT MEASURED. Timed out after 600s waiting for measurementA large repository can take longer than the default 600 seconds to measure.Raise --timeout. Start at 1200 and bring it back down once you know the real duration.
Container findings are missing after passing --imagetrivy is not on PATH in that job, so the image scan was skipped. The log says so.Install trivy in the job before the check, or drop --image if you do not scan images there.
The pipeline passes but nothing appears on the timelineThe project id points at a different repository than the one the job checked out.Compare the id in the step with the one in the URL of that repository's diagnosis.

See it on your own code.

Request a diagnosis. A specialist reads your repositories with you, on a call, and you leave with the first reading in hand.

Trusted AI. Measured, governed and proven.