Simply stated, a parallel build is a CI build that runs multiple CI jobs in parallel.
From the standpoint of CI, it means you’ll run tests and send coverage reports to Coveralls for different segments of your project, in different individual jobs, or steps, or stages of your CI build script.
From the standpoint of Coveralls, it means the Coveralls API will receive multiple coverage reports for the same build (at its /jobs
endpoint), until you tell it that all jobs were sent and it’s time to close the build, merge the reports together and calculate total coverage for the build.*
* The way you tell Coveralls to close a build is by sending a request to the Parallel Build Webhook.
To set up a parallel build, you’ll need to do a few things:
We’ll cover each step in detail below, but, in the meantime, here’s a complete example showing all three steps in a Github Actions Workflow:
on: ["push", "pull_request"]
name: Coveralls Parallel Build Example
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
test_number:
- 1
- 2
steps:
- uses: actions/checkout@3
- name: Use Node.js 16.x
uses: actions/setup-node@3
with:
node-version: 16.x
- name: Install dependencies
run: npm install
- name: Run tests
run: make test-coverage-$
- name: Send coverage to Coveralls (parallel)
uses: coverallsapp/github-action@v1
with:
parallel: true
flag-name: run-$
finish:
needs: test
if: $
runs-on: ubuntu-latest
steps:
- name: Close parallel build
uses: coverallsapp/github-action@v1
with:
parallel-finished: true
carryforward: "run-1,run-2"
Note that this example uses Github Actions’ configuration syntax (in YAML). If you’re using a different CI, you’ll need to translate the syntax to your CI’s configuration language.
Note also that this example is using a matrix strategy to run two (2) jobs in parallel. If you’re not using a matrix strategy, you’ll need to define each of your jobs in a series of individual steps.
Finally, note that this example is using the Coveralls Github Action, which is a Coveralls Integration with built-in support for parallel builds that simplifies the steps of sending coverage reports, and closing a build, with dedicated config parameters. If you’re using a different Coveralls Integration, your CI build script could have different commands, and be more, or less, verbose than this example.
This means setting up your CI build script to run multiple jobs that send coverage reports to Coveralls.
Here’s an example using Travis CI syntax from a .travis.yml
config file:
language: node_js
node_js:
- "node"
env:
global:
- COVERALLS_PARALLEL=true
jobs:
include:
- script: COVERALLS_FLAG_NAME=test-1 make test-coveralls-1
- script: COVERALLS_FLAG_NAME=test-2 make test-coveralls-2
Here we’re configuring a couple of parallel jobs to run by listing them in sequence, without using a matrix strategy.
Also, note the use of environment variables here, to: (A) configure the Coveralls Integration for parallel builds (COVERALLS_PARALLEL=true
); and (B) assign a “flag name” to each parallel job to help identify it at Coveralls (COVERALLS_FLAG_NAME=test-1
).
This means telling Coveralls that each job coming its way is a parallel job—in other words, that it should expect multiple jobs for the same build, and keep the build open until further notice.
One option for doing this is to set the purpose-specific environment variable, COVERALLS_PARALLEL=true
, somewhere in your CI build script or config file.
We already saw this example above, in Step 1. It’s this part:
env:
global:
- COVERALLS_PARALLEL=true
Here, a global env var in your config file declares: “All (Coveralls) jobs are parallel.”
With another Coveralls Integration the equivalent could be a pre-defined input option, like this:
- name: Coveralls Parallel Build Example
uses: coverallsapp/github-action@v1
with:
parallel: true # <-- this is the parallel option for the Coveralls Github Action integration
Finally, another equivalent could be passing the same env var on the command-line, like this:
COVERALLS_PARALLEL=true bundle exec rake coveralls:push
This means telling Coveralls that all jobs have been sent, so it can close the build.
This step is special because it involves sending a request to an API endpoint called the Parallel Build Webhook—or the (Close) Parallel Build Webhook—since that’s what it for, closing a parallel build.
Here’s an example we saw in our complete example above, using a Github Actions Workflow with the Coveralls Github Action:
finish:
needs: test
if: $
runs-on: ubuntu-latest
steps:
- name: Close parallel build
uses: coverallsapp/github-action@v1
with:
parallel-finished: true
carryforward: "run-1,run-2"
Here, using the configuration syntax of Github Actions, we’re calling the Coveralls Github Action with a special input option, parallel-finished: true
, which tells Coveralls to close the build associated with the given Workflow.
Under the hood, the Coveralls Github Action is calling the Parallel Build Webhook on our behalf, making a POST
request to the /webhook
endpoint, with a payload that looks like this:
{
"payload": {
"build_num": $BUILD_NUMBER,
"status": "done"
}
}
Where build_num
is the build number that our CI service (Github Actions) assigned to the build, and status
is the status of the build, which is always "done"
when closing a build.
Another example could be for a very pared-down CI setup—a shell script, for instance—in which you’d need to make that POST
request to the Parallel Build Webhook yourself, like this:
curl -k $COVERALLS_ENDPOINT/webhook?repo_token=$COVERALLS_REPO_TOKEN -d "payload[build_num]=$BUILD_NUMBER&payload[status]=done"
Where COVERALLS_ENDPOINT
is the base URL for your Coveralls instance (if it’s not the default for Coveralls Cloud, https://coveralls.io
), and COVERALLS_REPO_TOKEN
is your Coveralls Repo Token.
Even in a modern CI environment with a robust integration like the Coveralls Github Action, there might be a reason to make this call directly. For instance, in order to pass a different build number than the one you know your integration will be sourcing from your environment.
Since we’re discussing Parallel Builds here, it’s worth mentioning Carryforward Flags, which is a special feature often used in combination with Parallel Builds, typically for monorepos.
With Carryforward Flags, you can tell Coveralls that, if some coverage reports, for some portions of your project, are missing from a given build, it should carry forward the last known report it has on file for those subprojects. This is useful because it saves you from having to build your entire project on every pull request.
It’s also apt that we just discussed the Parallel Build Webhook, because that’s how the Carryforward Flags feature is enabled, by passing the carryforward
parameter to the (Close) Parallel Build Webhook:
We snuck an example of this into our complete example above, using a Github Actions Workflow with the Coveralls Github Action:
finish:
needs: test
if: $
runs-on: ubuntu-latest
steps:
- name: Close parallel build
uses: coverallsapp/github-action@v1
with:
parallel-finished: true
carryforward: "run-1,run-2"
Note that, in this example, we’re simply using the predefined input option, carryforward
, provided by the Coveralls Github Action, and passing it some comma-separated values, which tells Coveralls to carry forward the last known coverage reports for the run-1
and run-2
flags—if they’re missing from the current build.
Another way to do this would be to make the POST
request to the Parallel Build Webhook yourself, passing the ?carryforward=
URL parameter, like this:
curl --location 'https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN&carryforward=subproject-1,subproject-2,subproject-3' \
--header 'Content-Type: application/json' \
--data '{
"payload": {
"build_num": $BUILD_NUMBER,
"status": "done"
}
}'
Here we’re telling Coveralls to carry forward the last known coverage reports for subproject-1
, subproject-2
, and subproject-3
—again, if they’re missing from the current build.
Any problems, questions or comments about this doc? Let us know.