Skip to main content
Version: 1.5.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.