Continuous Integration
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
Written against MATLAB R2026b. Everything on this page runs MATLAB on a CI server, and neither MATLAB nor a CI service is available in the Octave 11.3.0 container used for this section, so no example was run: the commands, workflow files and licensing rules are checked against the R2026b documentation and the README files of the MathWorks actions as read on 2026-10-23, and are labelled "not run here". The README files change with each action release and the licensing terms are set by MathWorks, so check them again before you rely on them; the places where that matters are marked.
Continuous integration means that every change pushed to a repository is built and tested automatically on a machine that is not the author’s, so a broken change is found within minutes. For a MATLAB project the machine has to start MATLAB without a person at the keyboard, run the build and the tests, and report success or failure to the CI service through the process exit code, with test and coverage results as files. MATLAB supports this directly: the matlab -batch startup option, the build tool (see Build Automation with buildtool) and ready-made integrations for the common CI platforms. The tests themselves are on Unit Testing with matlab.unittest and the static analysis on Code Analyzer and Code Quality.
matlab -batch
Every CI integration comes down to one command line. The -batch startup option runs a statement non-interactively; -r runs it interactively (see the table of commonly used startup options in the references). From the documentation of the CI platforms: matlab -batch "myscript" starts MATLAB noninteractively and runs the commands in the file myscript.m. MATLAB terminates automatically, with exit code 0 if the script, function or statement ran without error, and with a nonzero exit code otherwise. A CI service marks a step as failed when the command it runs exits nonzero, so the exit code is the entire contract.
matlab -batch "buildtool"
matlab -batch "results = runtests('tests'); assertSuccess(results);"
matlab -batch "myscript"
Not run here. Three consequences follow:
-
Failures must be errors.
runtestsreturns the results without throwing, so a script that only calls it exits with 0 even when tests fail.assertSuccess(results)throws if any test failed or was incomplete, which makes the exit code nonzero. The build tool does this itself: a failingTestTaskfails the build. To fail in other conditions useassertorerror. -
There is nobody to answer. A statement that waits for the keyboard or opens a dialog (
input,uigetfile, the secret prompt of the vault) cannot complete in a noninteractive session. Keep interactive code out of the path that CI runs, and take credentials from the environment (see Secrets and Protecting Code). -
Quoting belongs to the shell. The statement is one argument, so wrap it in double quotes. Text inside it can use single quotes, which MATLAB reads as character vectors; how a shell treats the outer quotes differs between POSIX shells and Windows, so test the command line on the platform of the runner.
Other startup options are passed before -batch; the page lists, among others, -c for the license (a license file path or a port@host license server address) and -singleCompThread. The MathWorks actions below accept a startup-options input for this, but they do not allow -batch or -r in it, because the action supplies its own command.
Platforms
The documentation lists the platforms that have a MathWorks integration and the file in which you write the pipeline:
| Platform | Pipeline file | How MATLAB is added |
|---|---|---|
GitHub Actions |
|
MathWorks actions, used in the workflow. GitHub-hosted or self-hosted runners. |
GitLab CI/CD |
|
A component whose jobs run MATLAB builds. |
Jenkins |
Freestyle or matrix project, or a pipeline as code |
A plugin installed on the Jenkins agent. |
Azure DevOps |
|
An extension installed in the organization; Microsoft-hosted or self-hosted agents. |
CircleCI |
|
A third-party orb that the organization allows in its security settings. |
Bamboo, TeamCity |
Server configuration |
A plugin on the CI server that provides steps to run a MATLAB build, tests, scripts, functions and statements. |
Any other platform |
Its own pipeline file |
Run |
The sections below treat GitHub Actions in detail, because its configuration is the most directly documented, and then the generic matlab -batch form that works everywhere. The MathWorks site also keeps repositories of CI examples and a guide to integrating MATLAB and Simulink into CI/CD pipelines on GitHub (links in the references).
GitHub Actions
The MathWorks organization publishes four actions at https://github.com/matlab-actions. They are third-party code with their own license terms, labelled here as MathWorks-published and used by tagging a major version (@v3 in the README files read for this page). A workflow lives in .github/workflows and each action is one step.
| Action | Purpose |
|---|---|
|
Installs MATLAB (release R2021a or later) and, optionally, other MathWorks products on the runner and puts MATLAB on the |
|
Runs the build tool: the default tasks of |
|
Runs tests written with the MATLAB unit testing framework (or Simulink Test) and produces test and coverage artifacts. Needs R2013b or later; some inputs need newer releases or products. |
|
Runs a script, a function or MATLAB statements; fails the step on a nonzero exit. |
On a GitHub-hosted runner the sequence is: check out the repository, set up MATLAB, then run a build, tests or a command. On a self-hosted runner that already has MATLAB, setup is skipped and the runner uses the topmost MATLAB on the system path.
Inputs
All inputs are optional unless stated. These are the ones used most; the READMEs have the complete tables.
| Action | Input | Meaning |
|---|---|---|
|
|
The release, such as |
|
|
Further products, as names separated by spaces, installed with the MATLAB Package Manager ( |
|
|
|
|
|
|
|
|
The task names, separated by spaces; arguments in parentheses. With no value the default tasks run. |
|
|
Options of the |
|
|
|
|
|
Folder with the source code, added with its subfolders to the top of the MATLAB path; coverage is measured on it. |
|
|
Build the test suite from some folders only, or only tests with a tag. |
|
|
Apply strict checks, run in parallel (needs Parallel Computing Toolbox), and set display and logging levels, as in |
|
|
Where to write the test results as JUnit-style XML, HTML or PDF. |
|
|
Where to write coverage, and which metrics ( |
|
|
Required. The script, function or statements to run; several are separated by commas or semicolons. A script is named without |
|
|
Extra startup options (not |
A workflow for heatflow
The heatflow project has a buildfile.m with the tasks check, test and package (see Build Automation with buildtool). This workflow builds it on the three runner platforms with the release the project is written for:
name: heatflow
on: [push, pull_request]
jobs:
build:
name: Build and test on ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Check out repository
uses: actions/checkout@v7
- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v3
with:
release: R2026b
cache: true
- name: Run build
uses: matlab-actions/run-build@v3
with:
tasks: check test
Not run here. The structure (checkout, setup-matlab, run-build with tasks, and a matrix over os) follows the examples in the README files; the version tags are the ones those README files showed on 2026-10-23, so use the current ones. The job fails if check finds a warning or test fails, because the build tool then stops with an error and the action exits nonzero. The package task is left out of the pull request run; a release workflow would add it.
To run only the tests, without a build file, use run-tests, which also writes the artifacts that CI services can display:
- name: Run tests
uses: matlab-actions/run-tests@v3
with:
source-folder: .
select-by-folder: tests
test-results-junit: test-results/results.xml
code-coverage-cobertura: code-coverage/coverage.xml
Not run here. The two output inputs are the README example; source-folder and select-by-folder are inputs described in the README, and the values . and tests are assumptions for the heatflow layout (a project with the code in the root folder).
To run something that is not a build or a test, use run-command. The statement form shown in the README is command: results = runtests, assertSuccess(results);, which runs the tests in the current folder and fails on a failure. A script works the same way: command: ci/runChecks runs ci/runChecks.m (given without the extension, from a folder that is on the path).
Caching
Two inputs save time, and they cache different things. setup-matlab with cache: true stores the installed MATLAB and products, so later runs skip the download. run-build with cache: true stores the .buildtool folder, which with the -outputCache build option (R2026b) lets the build tool skip tasks whose cached outputs are still valid:
- name: Run build
uses: matlab-actions/run-build@v3
with:
build-options: -outputCache
cache: true
Not run here (the README example). The cache is only as valid as the build file: if you change the code a task analyzes or tests, its inputs change and the task reruns. See the output cache section on Build Automation with buildtool.
Jenkins, GitLab CI/CD and Azure DevOps
On the other platforms the choices are the same two: use the MathWorks integration, or call MATLAB yourself. The integrations are described on the documentation page for CI platforms. The Jenkins plugin goes on the agent and lets you run MATLAB in freestyle and matrix projects or from a pipeline as code. The GitLab integration is a component for .gitlab-ci.yml. The Azure DevOps extension is installed in the organization and used in azure-pipelines.yml. Their step and input names are defined by those products, so look them up in the plugin or extension listing and not here.
Where you only need MATLAB to run, the generic form works on every platform once MATLAB is on the agent’s PATH:
# .gitlab-ci.yml, on a runner that has MATLAB installed
test:
script:
- matlab -batch "buildtool check test"
// Jenkinsfile, on an agent that has MATLAB installed
pipeline {
agent any
stages {
stage('Build and test') {
steps {
sh 'matlab -batch "buildtool check test"'
}
}
}
}
Not run here, and not taken from the MathWorks integrations: these are the plain matlab -batch form. Use bat instead of sh on a Windows Jenkins agent. In both files the MATLAB license has to be available to the agent (see below).
Licensing
A CI machine runs MATLAB with nobody to sign in, so the license has to be supplied in advance. The terms are set by MathWorks and change; this section records what the action READMEs said on 2026-10-23 and the answer to the licensing question is to verify them at the source before you depend on them.
-
Public project. The
run-build,run-testsandrun-commandactions license all products automatically, except the transformation products such as MATLAB Coder and MATLAB Compiler. -
Private project. The actions do not license any product automatically. A MATLAB batch licensing token is a string that lets MATLAB start in a noninteractive environment, and the READMEs point to a MathWorks pilot program for requesting one. When this page was checked (2026-10-23) the MathWorks batch token page said that the pilot "is not currently accepting new requests", and the batch licensing project is described as still in its pilot phase. So for a private project you may have to use a self-hosted runner or another license arrangement instead; ask MathWorks. A token is also needed in a public project that uses transformation products.
-
Using the token. Store it as a secret of the repository and map the secret to an environment variable named
MLM_LICENSE_TOKENin the workflow. The secret is named by you; the README example usesMyToken, the example below uses a placeholder name. -
Self-hosted runners and other platforms. A runner you operate normally uses the license of the machine it runs on, or a license server named with the
-cstartup option (port@host). The licensing of your organization, not these notes, decides what is allowed.
env:
MLM_LICENSE_TOKEN: ${{ secrets.MLM_LICENSE_TOKEN }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: matlab-actions/setup-matlab@v3
- uses: matlab-actions/run-build@v3
Not run here. secrets.MLM_LICENSE_TOKEN is a reference to a secret that you create in the repository settings; the value is never written in the file. Never commit a token, print it with disp, or pass it on a command line that a log would echo, and remember that, under GitHub’s own rules (not MATLAB’s), workflows triggered from forks normally cannot read repository secrets. Related advice for credentials that the code itself needs is on Secrets and Protecting Code. Which products a given license covers is a question for Toolboxes and Add-On Products and for MathWorks.
Source: matlab-actions/setup-matlab (Licensing and Use MATLAB Batch Licensing Token sections), MATLAB Batch Licensing Pilot (status as of 2026-10-23: not accepting new requests)
Test, coverage and code issue reports
A CI service needs files in standard formats to show results. MATLAB writes them in three ways, and they can be combined.
From the build tool. A TestTask produces test results and coverage when its TestResults and CodeCoverageResults properties name files. The extension chooses the format: .xml for JUnit-style test results or Cobertura coverage, .html for a readable report, .pdf for a test report, .mat for MATLAB objects. A CodeIssuesTask can write its findings to a SARIF file with its Results property. This is the route used by the heatflow build file.
From run-tests. The inputs listed above (test-results-junit, code-coverage-cobertura and others) do the same without a build file.
From your own script. The unit testing framework customizes the test runner with plugins from the namespace matlab.unittest.plugins: XMLPlugin writes JUnit-style XML, TAPPlugin a Test Anything Protocol stream, TestReportPlugin a PDF or HTML test report, and CodeCoveragePlugin a coverage report. The documentation’s pattern, with heatflow names:
import matlab.unittest.TestRunner
import matlab.unittest.plugins.XMLPlugin
import matlab.unittest.plugins.CodeCoveragePlugin
import matlab.unittest.plugins.codecoverage.CoberturaFormat
suite = testsuite("tests");
runner = TestRunner.withTextOutput;
runner.addPlugin(XMLPlugin.producingJUnitFormat("junit.xml"))
format = CoberturaFormat("cobertura.xml");
runner.addPlugin(CodeCoveragePlugin.forFile("src/+heatflow/laplacian.m","Producing",format))
results = runner.run(suite);
assertSuccess(results);
Not run here. The calls are the ones of the documentation example (quadraticSolver.m there); the file src/+heatflow/laplacian.m follows the heatflow layout, and the last line makes the exit code nonzero on failure. Save the script as, say, ci/runTests.m and run it with run-command or matlab -batch.
Code issues as SARIF. Static analysis results travel as SARIF (Static Analysis Results Interchange Format), which code hosting and review tools can show on a pull request. In a script, issues = codeIssues("src"); export(issues,"code-issues.sarif"); writes it (see Code Analyzer and Code Quality); in a build, use the Results property of the task. How a given service ingests a SARIF, JUnit or Cobertura file is documented by that service; the MATLAB side only has to produce the file.
The GitHub actions add a job summary for builds and tests by default (generate-summary), so a short report appears on the workflow run page without any upload step. Uploading the files as workflow artifacts or to a coverage service is done with the actions of the CI service, not with MATLAB.
Pitfalls
-
Exit code 0 with failing tests. A script that calls
runtestsand nothing else succeeds even if tests fail. End it withassertSuccess(results)or use the build tool orrun-tests. -
An unpinned release.
latestchanges when MathWorks ships a release, and a green build can turn red overnight because of a new MATLAB. Pinrelease, and add a second job onlatestor a prerelease on purpose if you want an early warning. A build file that uses newer features (see the release table on the build page) fails on an older runner. -
No license on a private repository. The step is expected to fail when MATLAB starts, not in your code (the exact message is not shown here). Check the licensing section above first, including the status of the batch token pilot.
-
Code that needs a person or a display. Dialogs and
inputhang or fail in batch mode. Guard them or keep them out of tested paths. -
Secrets in logs. Do not echo environment variables. Pass the token through
envfrom a secret, as shown. -
The working directory and the path. The build tool runs from the folder of
buildfile.m; the other commands run from the checkout root. A test that needs a data file should locate it relative to the test file, not to the current folder (see Search Path, Startup and Files). -
Time. Starting MATLAB takes a while, and the installation takes longer; use the caches and run the quick
checktask first so that a style error fails the job before the slow tests start.
Where older books differ
The books consulted for this section predate every item on this page. The MATLAB Primer, written for R2010a, runs programs interactively or with matlab -r from a script, and has no unit testing framework (the class-based framework came later), no build tool (R2022b), no -batch (the page for the startup options does not give its release, and general knowledge says R2019a, unconfirmed) and no CI integrations. The documentation describes -r as running the statement interactively and -batch as running it non-interactively, with exit code 0 on success and nonzero on error; that is the behaviour a CI step needs, and it is why the integrations use -batch. The Pérez López book does not discuss CI. The Code Analyzer export to SARIF (R2023b) and the GitHub actions are all newer than both books.
References
-
MATLAB Actions on GitHub (MathWorks-published)
-
MATLAB Batch Licensing Pilot (status as of 2026-10-23: not accepting new requests)