Skip to content
The docs are now organized by product area. Old links still work. Report a broken link

GitHub Actions

Upload test results from GitHub Actions to Qualflare with qualflare-action or the qf CLI: workflow YAML, the QF_TOKEN secret, inputs and pull request branches.

Last updated

To upload test results from GitHub Actions to Qualflare, add the Qualflare/qualflare-action@v1 step after your tests, or install qf and run qf <identifier> collect <report>, with the access token stored as a repository secret named QF_TOKEN. Qualflare/qualflare-action is a composite action that downloads the right qf binary for the runner and runs the same qf login and qf <identifier> collect steps: it's the CLI, not a separate upload path.

Before you begin

  • A project access token with Allow writes off, which you create in Project Settings > Access Tokens Editor+ · project (requires the Editor role or higher in the project). See Access Tokens.
  • A report file your test step writes, such as JUnit XML.
  • The patterns every CI shares are on CI/CD integration.

Store the token as a secret

  1. Go to your repository Settings > Secrets and variables > Actions
  2. Click New repository secret
  3. Name: QF_TOKEN
  4. Value: Your Qualflare access token
  5. Click Add secret

The action takes the secret through its token input, so any secret name works (its README uses QUALFLARE_TOKEN). The manual install reads QF_TOKEN.

Upload with qualflare-action

Add the action after your test step:

- name: Run tests
  run: pytest --junitxml=results.xml

- name: Upload results to Qualflare
  if: ${{ !cancelled() }}   # upload even when tests fail
  uses: Qualflare/qualflare-action@v1
  with:
    token: ${{ secrets.QF_TOKEN }}
    project: myapp
    results: results.xml

Put if: ${{ !cancelled() }} on the upload step so it runs when your tests fail. Do not add continue-on-error: true to the test step: it marks the failed step as passed, so the job and the pull request check turn green even when tests fail.

Because the upload step runs unless the workflow is cancelled, results reach Qualflare when the test step fails, and the job stays failed.

Inputs

InputRequiredDefaultDescription
tokenYes—Qualflare project access token (qfp_...). Store it as an encrypted secret, never inline.
projectYes—A local name for the project, used by qf on the runner. The token decides which project receives the results. Use lowercase letters, digits, - and _, up to 63 characters, starting with a letter or digit, and not a qf command name such as version.
resultsYes—Path or glob to your test result files (e.g. test-results/*.xml). Framework is auto-detected.
versionNolatestqualflare-cli version to install, e.g. 0.4.0. A leading v is optional.

Outputs

None: the action has no outputs: block, so no steps.<id>.outputs.* value (such as a report URL) is there for a later step.

Runner support

Linux and macOS (amd64/arm64) are fully supported. Windows runners are best-effort — if you're on Windows and hit issues, install qf yourself.

What the action runs:

  • Install Qualflare CLI (qf) resolves version (latest by default, through the releases page), downloads qf_<version>_<os>_<arch> and puts it on PATH.
  • Upload results to Qualflare runs qf login <project> <token>, then qf <project> collect <results>.
qualflare-action runs two steps: it installs qf for the runner, then logs in with your token and uploads the results files.

What the action doesn't expose

  • The action has four inputs. qf still reads its QF_ variables inside it, so set QF_ENVIRONMENT, QF_MILESTONE, QF_BRANCH, QF_COMMIT, QF_UPLOAD_ARTIFACTS or QF_PLATFORM in the job's env:, as in Branch and commit.
  • It has no input for the collect flags that have no variable: --format, --shard, --allow-mixed-runs, --artifacts-dir, --dry-run with its --output, and the overall --timeout, which stays at its 30s default, short for large uploads with videos or traces.
  • It passes no --format, so TestNG, Maestro, XCTest and Espresso XML upload as plain junit. It runs qf login without --force: on a self-hosted runner that keeps its home directory between jobs, the second run fails with identifier "myapp" already exists. It passes the token to qf login as a command-line argument rather than through QF_TOKEN.
  • results is not quoted, so the shell expands globs and splits it on spaces: separate several patterns with a space, and don't use a path that contains one.

Install qf yourself for any case in the last three bullets.

Install qf yourself

Install qf in your own step for Windows, a reused runner, a flag above or a token kept out of the process list:

- name: Upload results to Qualflare
  if: ${{ !cancelled() }}  # upload even when tests fail
  env:
    QF_TOKEN: ${{ secrets.QF_TOKEN }}
    QF_BRANCH: ${{ github.head_ref || github.ref_name }}
    QF_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
  run: |
    # Install qf CLI (ubuntu-latest runners don't ship Homebrew)
    VERSION=0.4.0
    curl -L "https://github.com/qualflare/qualflare-cli/releases/download/v${VERSION}/qf_${VERSION}_linux_amd64.tar.gz" -o qf.tar.gz
    tar -xzf qf.tar.gz && sudo mv qf /usr/local/bin/

    # Upload results (qf login reads QF_TOKEN from env)
    qf login ci --force
    qf ci collect results.xml

On other runners, download the matching qf_<version>_<os>_<arch> archive (.zip on Windows); for sharded jobs, upload once from a final job with --shard (Sharded runs).

Branch and commit

qf reads GITHUB_REF_NAME and GITHUB_SHA. On pull_request runs those are the <number>/merge branch and a merge commit, so set the pull request's own values at job level, where the action's steps see them too:

jobs:
  test:
    runs-on: ubuntu-latest
    env:
      QF_ENVIRONMENT: staging  # must exist in the project
      QF_BRANCH: ${{ github.head_ref || github.ref_name }}
      QF_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}

More on this: Pull request branches.

Version pinning

Qualflare/qualflare-action@v1 tracks the latest v1.x release of the action itself. The version input is separate — it pins which qualflare-cli release gets installed underneath, independent of the action's own version. Pin version explicitly in CI for reproducible builds; leave it on latest for local experimentation.

Troubleshooting

  • Error: token cannot be empty: the token input or QF_TOKEN is empty in that run. GitHub gives no secrets to workflows run for pull requests from forks.
  • Error: unknown flag: --version in Install Qualflare CLI (qf): harmless. The action runs qf --version, which qf doesn't have.

More fixes: Troubleshooting on CI/CD integration.