Skip to main content
Version: 1.6.0

Setting Up in CI/CD

SAFE runs as a step in your CI pipeline. When vulnerabilities are found it exits with a non-zero code, blocking the build. This page covers how to integrate SAFE into common CI systems.

Approaches​

There are three ways to add SAFE to a pipeline:

  1. Binary download — download the SAFE binary as part of the job. No changes to your project files.
  2. Mix plugin (mix_safe) — add mix_safe to your mix.exs dependencies. The plugin manages the SAFE binary automatically and integrates with the Mix workflow. See mix_safe.
  3. Rebar3 plugin (rebar_safe) — add rebar_safe to your rebar.config plugins. The plugin manages the SAFE binary automatically and integrates with the Rebar3 workflow. See rebar_safe.

Prerequisites​

Before running SAFE in CI, make sure:

  • .safe/config.json is committed to your repository. Generate it locally with safe setup and commit the result.
  • The SAFE_LICENSE secret is configured in your CI system.

GitHub Actions​

.github/workflows/safe.yml
name: SAFE Security Scan

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

jobs:
safe:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: erlef/setup-beam@v1
with:
otp-version: '26'
elixir-version: '1.16'

- name: Restore plugin binary cache
uses: actions/cache@v4
with:
path: _build/safe
key: safe-binary-${{ hashFiles('safe.lock') }}
restore-keys: safe-binary-

- name: Restore analysis state cache
uses: actions/cache@v4
with:
path: ~/.safe
key: safe-state-${{ github.ref }}-${{ github.sha }}
restore-keys: |
safe-state-${{ github.ref }}-
safe-state-

- name: Install dependencies
run: mix deps.get

- name: Compile
run: mix compile

- name: Run SAFE analysis
run: mix safe analyse
env:
SAFE_LICENSE: ${{ secrets.SAFE_LICENSE }}

GitLab CI​

info

GitLab CI cache paths must be relative to the project directory. SAFE's analysis state lives at ~/.safe, which is outside that scope, so incremental state caching is not available on GitLab. Each run performs a full analysis.

.gitlab-ci.yml
safe:
stage: test
image: elixir:1.16
variables:
SAFE_LICENSE: $SAFE_LICENSE
before_script:
- mix deps.get
- mix compile
script:
- mix safe analyse

CircleCI​

info

CircleCI requires environment variables to be configured in the project settings or as a context. Set SAFE_LICENSE there rather than hard-coding it in the config.

.circleci/config.yml
version: 2.1

jobs:
safe:
docker:
- image: cimg/elixir:1.17
steps:
- checkout

- restore_cache:
keys:
- safe-binary-{{ checksum "safe.lock" }}
- safe-binary-

- restore_cache:
keys:
- safe-state-{{ .Branch }}-{{ .Revision }}
- safe-state-{{ .Branch }}-
- safe-state-

- run:
name: Install dependencies
command: mix deps.get

- run:
name: Compile
command: mix compile

- run:
name: Run SAFE analysis
command: mix safe analyse

- save_cache:
key: safe-binary-{{ checksum "safe.lock" }}
paths:
- _build/safe

- save_cache:
key: safe-state-{{ .Branch }}-{{ .Revision }}
paths:
- ~/.safe

workflows:
version: 2
security:
jobs:
- safe

Caching​

Caching reduces scan time significantly on repeat runs. There are two directories worth caching:

DirectoryPurposeCache key recommendation
~/.safeSAFE analysis state. Stores the results of previous runs so only changed files are re-analysed.Branch + commit SHA, falling back to branch
_build/safePlugin binary cache (mix_safe / rebar_safe). Avoids re-downloading the SAFE binary on every run.Contents of safe.lock (mix_safe) or branch (rebar_safe)
tip

Always restore the ~/.safe cache before the compile step, and save it after the SAFE step. This ensures the analysis state reflects the compiled BEAM files from the current run.

Exit Codes​

CodeMeaning
0Analysis completed successfully — no vulnerabilities found.
1An error occurred (bad configuration, missing license, etc.).
2Analysis completed — vulnerabilities were found.