CI/CD Quality Gate
Skylos integrates with your CI/CD pipeline to automatically block pull requests that introduce security vulnerabilities or code quality issues.
Overview
There are two ways to use Skylos in CI/CD:
| Approach | Plan | Enforcement |
|---|---|---|
| GitHub Actions only | Free | Soft (can be bypassed) |
| GitHub App + Actions | Pro | Hard (server-controlled) |
GitHub Actions (Tokenless OIDC) — Recommended
OIDC authentication requires zero secrets — no tokens to manage, rotate, or leak.
GitHub Actions can authenticate with Skylos Cloud using OpenID Connect (OIDC). This is the most secure and convenient approach.
Setup
Create .github/workflows/skylos.yml:
name: Skylos Quality Gate
on:
pull_request:
branches: [main, master]
permissions:
contents: read
id-token: write # Required for tokenless auth
jobs:
skylos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Skylos
run: pip install skylos
- name: Run Skylos Scan
run: skylos . --danger --upload
That's it. No secrets to configure.
How OIDC Works
- GitHub Actions generates a short-lived OIDC token for the workflow run
- The token contains claims:
repository,actor,ref,sha - Skylos verifies the token against GitHub's public JWKS endpoint
- Skylos matches the repository to your project in the database
- Upload proceeds — no secrets involved
OIDC vs Manual Token
| Aspect | OIDC (Recommended) | Manual Token |
|---|---|---|
| Secrets to manage | 0 | 1 |
| Token lifetime | Minutes | Until revoked |
| Setup steps | Add permission line | Create secret + add to workflow |
| Key rotation | Automatic | Manual |
| Can be stolen/reused | ❌ | ⚠️ If secret leaks |
| Bound to repository | ✅ | ❌ |
Free Tier: GitHub Actions
Basic Setup
Create .github/workflows/skylos.yml:
name: Skylos Quality Gate
on:
pull_request:
branches: [main, master]
jobs:
skylos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Skylos
run: pip install skylos
- name: Run Skylos Scan
run: skylos . --danger --gate
How It Works
- Developer opens a pull request
- GitHub Actions runs the workflow
skylos . --danger --gatescans the code- Exit code
1= check fails, exit code0= check passes
Limitations
With the free tier, anyone with write access to the repository can:
- Delete or modify the workflow file
- Bypass the check via admin privileges
- Merge without the check passing
Pro Tier: GitHub App Integration
The Pro tier uses a server-controlled GitHub check that cannot be bypassed by developers.
How It Works
Key Difference
The GitHub App check is created and updated by the Skylos server, not by the Actions workflow. This means:
- ✅ Developers cannot delete or modify the check
- ✅ Developers cannot bypass the check (unless they're admins)
- ✅ The check persists even if the workflow is deleted
- ✅ Only the Skylos server can mark the check as passed
Setup
Step 1: Connect Skylos
pip install skylos
skylos sync setup
Follow the prompts to:
- Authenticate (browser opens automatically)
- Install git hooks (optional)
- Create GitHub Actions workflow
Step 2: Install GitHub App
- Go to Dashboard → Settings
- Select your project
- Click Install GitHub App
- Select the repository you want to protect
- Authorize the requested permissions
Step 3: Configure Branch Protection
- Go to your GitHub repository
- Navigate to Settings → Branches
- Click Add rule (or edit existing)
- Set branch name pattern:
main(or your default branch) - Enable these options:
- ✅ Require status checks to pass before merging
- Select "Skylos Quality Gate" from the list
- ✅ Do not allow bypassing the above settings (for strict enforcement)
Step 4: Add Workflow
For OIDC (recommended):
permissions:
contents: read
id-token: write
pull-requests: write
checks: write
For manual tokens, add SKYLOS_TOKEN to repository secrets (Settings → Secrets → Actions).
Generated Workflow
After running skylos sync setup, this workflow is created:
name: Skylos Quality Gate
on:
pull_request:
branches: [main, master]
permissions:
contents: read
id-token: write
pull-requests: write
checks: write
jobs:
skylos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Skylos
run: pip install skylos
- name: Run Skylos Scan
run: skylos . --danger --upload
Generating Workflows with Upload
Use skylos cicd init --upload to generate a workflow that automatically uploads scan results to the Skylos cloud dashboard:
# Basic workflow with upload
skylos cicd init --upload
# Upload + LLM review
skylos cicd init --upload --llm --model claude-sonnet-4-20250514
This adds --upload to the analysis step and sets SKYLOS_TOKEN: ${{ secrets.SKYLOS_TOKEN }} as an environment variable. Add SKYLOS_TOKEN to your repo secrets (Settings → Secrets → Actions).
Other CI Systems
GitLab CI
skylos:
image: python:3.11
script:
- pip install skylos
- skylos . --danger --upload
variables:
SKYLOS_TOKEN: $SKYLOS_TOKEN
Add SKYLOS_TOKEN as a CI/CD variable (Settings → CI/CD → Variables).
CircleCI
- run:
name: Skylos Scan
command: skylos . --danger --upload
environment:
SKYLOS_TOKEN: $SKYLOS_TOKEN
Jenkins
stage('Skylos Scan') {
environment {
SKYLOS_TOKEN = credentials('skylos-token')
}
steps {
sh 'pip install skylos'
sh 'skylos . --danger --upload'
}
}
Migrating from Manual Tokens to OIDC
If you're already using SKYLOS_TOKEN in GitHub Actions, here's how to switch to tokenless OIDC:
Before:
- name: Run Skylos Scan
env:
SKYLOS_TOKEN: ${{ secrets.SKYLOS_TOKEN }}
run: skylos . --danger --upload
After:
# Add at the top level of your workflow
permissions:
contents: read
id-token: write
# Update the step (remove SKYLOS_TOKEN)
- name: Run Skylos Scan
run: skylos . --danger --upload
Steps:
- Add
id-token: writepermission to your workflow - Remove the
SKYLOS_TOKENenv from the step - Push the change — uploads now use OIDC
- (Optional) Keep the
SKYLOS_TOKENsecret around for rollback
Comparison Table
| Feature | Free | Pro |
|---|---|---|
Local scans (skylos .) | ✅ | ✅ |
Exit codes (--gate) | ✅ | ✅ |
| GitHub Actions workflow | ✅ | ✅ |
| Tokenless OIDC | ✅ | ✅ |
One-click setup (sync setup) | ❌ | ✅ |
| Server-controlled check | ❌ | ✅ |
| Cannot be bypassed | ❌ | ✅ |
| PR diff analysis | ❌ | ✅ |
| Inline PR comments | ❌ | ✅ |
| Suppressions | ❌ | ✅ |
| Slack notifications | ❌ | ✅ |
| Discord notifications | ❌ | ✅ |
| Dashboard history | Limited | Full |
CLI Reference
Basic Scan
skylos .
Scan with Security Checks
skylos . --danger
Quality Gate Mode
skylos . --danger --gate
Returns exit code 1 if issues are found.
Upload to Cloud
skylos . --danger --upload
Force Bypass (Local Only)
skylos . --danger --gate --force
The --force flag only bypasses the local exit code. It does not affect the server-controlled GitHub App check for Pro users.
Troubleshooting
Check is stuck on "Queued"
The GitHub App check was created but never updated. This usually means:
- The scan didn't run or didn't upload results
- Authentication failed (check OIDC permissions or
SKYLOS_TOKEN) - The commit SHA doesn't match
Fix: Ensure the workflow has correct permissions (id-token: write for OIDC) or SKYLOS_TOKEN is set, and the workflow runs successfully.
Check shows "Expected"
The branch protection is configured but the check hasn't been created yet.
Fix: Push a new commit or re-run the workflow to trigger the check.
"No commit found for SHA" error
The scan ran before the commit was pushed to GitHub.
Fix: Always push your commits before running skylos . --gate locally, or let GitHub Actions handle it.
OIDC auth fails
Make sure your workflow has the correct permissions:
permissions:
contents: read
id-token: write
Also verify:
- You're using Skylos CLI v3.3.0 or later
- The repository URL in your Skylos project matches the GitHub repo
- The workflow is running on GitHub Actions (not a self-hosted runner with OIDC disabled)
Token not working
Make sure you're using the correct token for your environment:
- Production: Get token from https://skylos.dev/dashboard/settings
- Local dev: Consider using browser auth instead (
skylos . --upload)
See Authentication for the full token priority and troubleshooting guide.