Updating Circle CI Pipeline
CircleCI Pipeline Configuration Guide
Overview
This CircleCI pipeline ensures secure and automated building, testing, and publishing of the Okta Python SDK.
Pipeline Structure
1. Security Scanning Jobs (Run First in Parallel)
Snyk Scan (snyk-scan)
- Runs first before build/test
- Scans for security vulnerabilities in dependencies
- Runs on every commit (including non-main branches)
- Uses the
static-analysiscontext
Reversing Labs (reversing-labs)
- Runs first before build/test (in parallel with Snyk)
- Malware scanning using Reversing Labs scanner
- Runs on every commit
- Uses the
okta-dcpcontext
2. Build Job (build)
- Only runs after security scans pass
- Runs on Python 3.10
- Installs dependencies from
requirements.txt - Builds distribution packages (sdist and wheel)
- Persists workspace for downstream jobs
- Does NOT run tests (separated into dedicated test jobs)
3. Unit Tests Job (unit_tests)
- Only runs after build succeeds
- Runs unit tests from
tests/unitdirectory - Fast-running tests that validate individual components
- Generates code coverage reports
- Stores test results and coverage artifacts
4. Integration Tests Job (integration_tests)
- Only runs after unit tests pass
- Runs integration tests from
tests/integrationdirectory - Tests interactions between components and external systems
- Generates code coverage reports
- Stores test results and coverage artifacts
5. Publishing Job (publish_to_pypi)
- Only runs on master branch or version tags (v*)
- Only runs after all tests succeed
- Verifies distribution packages with
twine check - Publishes to PyPI using token authentication
Workflows
non-prod (Runs on PRs and non-master branches - internal contributors)
- Step 1: Snyk scan and Reversing Labs run in parallel
- Step 2: Build only if both security scans pass
- Step 3: Unit tests only if build succeeds
- Step 4: Integration tests only if unit tests pass
- Does NOT publish
- Full security scanning with context access
contributors (Runs on PRs from forked repositories - external contributors)
- Step 1: Build runs immediately (no security scans)
- Step 2: Unit tests only if build succeeds
- Step 3: Integration tests only if unit tests pass
- Does NOT publish
- Security scans are skipped (no context access for forked PRs)
- Maintainers should review code before merging
prod (Runs only on master branch)
- Step 1: Snyk scan and Reversing Labs run in parallel
- Step 2: Build only if both security scans pass
- Step 3: Unit tests only if build succeeds
- Step 4: Integration tests only if unit tests pass
- Step 5: Publish to PyPI only if all tests succeed
- Triggered by:
- Commits to
masterbranch - Version tags matching
v*pattern
- Commits to
Required CircleCI Project Settings
Forked Pull Request Settings
To properly handle forked PRs, configure these settings in CircleCI:
- Go to Project Settings → Advanced Settings
- Configure the following:
- ✅ Enable "Only build pull requests" - Only builds PRs, not random branches
- ✅ Enable "Build forked pull requests" - Allows community contributions
- ❌ Disable "Pass secrets to builds from forked pull requests" - Critical for security!
- Optional: Enable "Require approval for fork pull request builds" (if available)
This configuration ensures:
- Forked PRs are built, but without access to secrets/contexts
- Security scans that require contexts will be skipped on forked PRs
- Internal team can manually trigger builds with secrets if needed
- Prevents malicious PRs from accessing your PyPI tokens or AWS credentials
Required CircleCI Contexts
You need to configure the following contexts in CircleCI:
1. static-analysis
- Used for Snyk scanning
- Should contain Snyk authentication tokens
2. okta-dcp
- Used for Reversing Labs malware scanning
- Should contain:
RESOURCE_TOKEN- Token for downloading RL scannerAWS_ARN- AWS role ARN for authentication
3. pypi-publish
- Used for publishing to PyPI
- Should contain:
PYPI_TOKEN- PyPI API token (use__token__as username)
Setting Up PyPI Token
- Go to https://pypi.org/manage/account/token/
- Create a new API token with appropriate scope (ideally scoped to the
oktapackage) - Add it to CircleCI:
- Go to Project Settings → Contexts
- Create or edit the
pypi-publishcontext - Add environment variable:
PYPI_TOKEN=<your-token>
Security Features
- Security-First Approach:
- Security scans (Snyk + Reversing Labs) run before building/testing code
- Build and test only proceed if security scans pass
- Parallel execution of security scans for efficiency
- Forked PR Protection via CircleCI Settings:
- Go to Project Settings → Advanced
- Enable "Only build pull requests" - This prevents arbitrary commits from forks from running
- Enable "Build forked pull requests" - Allows PRs from forks after review
- Disable "Pass secrets to builds from forked pull requests" - Prevents secrets exposure
- This means forked PRs won't have access to contexts/secrets automatically
- Publishing restricted to master: The publish job only runs on the master branch
- Publishing requires successful build: Publish only runs if build and tests succeed
- Token-based authentication: Uses secure token authentication for PyPI
- Context protection: Security contexts (okta-dcp, static-analysis) should have restricted access
Testing the Pipeline
On Forked Repository PRs
When an external contributor submits a PR from a forked repository:
# External contributor forks and creates PR
Expected:
- Build and test jobs run (if "Build forked pull requests" is enabled)
- Security scans are skipped (no access to contexts)
- Maintainer reviews the code changes
- If approved, maintainer can manually re-run workflows with secrets enabled
- No publishing occurs
To manually approve and run with secrets:
- Go to CircleCI workflow for the forked PR
- Click "Rerun workflow from failed"
- Enable "Rerun with SSH" or use API to run with secrets (requires admin)
On Feature Branches (Internal Repository)
git checkout -b feature/my-feature
# Make changes
git commit -m "Add new feature"
git push origin feature/my-feature
Expected: Build, test, and security scans run automatically. No publishing.
On Master Branch
git checkout master
git merge feature/my-feature
git push origin master
Expected: Build, test, security scans run, and then publishes to PyPI.
Using Version Tags
git tag v3.0.1
git push origin v3.0.1
Expected: Triggers the publish workflow with full pipeline.
Troubleshooting
How to Find Coverage Reports
Coverage reports are generated for both unit and integration tests and stored as CircleCI artifacts.
To access coverage reports:
- Go to CircleCI: Navigate to your workflow run
- Click on the test job: Either
unit_testsorintegration_tests - Click the "Artifacts" tab
- Find coverage files:
- Unit Test Coverage:
coverage-unit/folder - Integration Test Coverage:
coverage-integration/folder
- Unit Test Coverage:
Coverage report formats available:
coverage.xml- Machine-readable XML format (for CI/CD tools)html/index.html- Human-readable HTML report with detailed line-by-line coverage- Terminal output - Coverage summary displayed in the job logs
To view the HTML coverage report:
- Click on
coverage-unit/html/index.htmlorcoverage-integration/html/index.html - The interactive HTML report shows:
- Overall coverage percentage
- Per-file coverage breakdown
- Line-by-line highlighting of covered/uncovered code
- Missing lines details
Coverage Metrics Explained:
- Statements: Percentage of code statements executed
- Missing: Number of lines not covered by tests
- Branch: Conditional branches (if/else) coverage
- Partial: Partially covered branches
Example Coverage Summary in Logs:
Name Stmts Miss Cover Missing
------------------------------------------------------
okta/__init__.py 12 0 100%
okta/client.py 156 23 85% 45-67, 89
okta/models/user.py 89 5 94% 23, 45-48
------------------------------------------------------
TOTAL 257 28 89%
Forked PRs Not Building
- Check Project Settings → Advanced → "Build forked pull requests" is enabled
- Verify "Only build pull requests" is enabled
- Ensure the PR is actually a pull request (not just a branch push)
Security Scans Failing on Forked PRs
- This is expected! Forked PRs don't have access to contexts
- Security scans will be skipped or fail due to missing credentials
- After code review, maintainers can manually trigger with secrets
Publishing Fails
- Verify
PYPI_TOKENis set correctly in thepypi-publishcontext - Check that you're on the master branch
- Ensure version in
setup.pyhasn't been published before
Security Scans Fail
- Check context configurations (
static-analysis,okta-dcp) - Verify tokens and credentials are valid
- Review security scan logs for specific issues
Build Fails
- Check Python version compatibility
- Verify all dependencies in
requirements.txtare available - Review test failures in CircleCI artifacts
Best Practices
- Always create feature branches: Don't commit directly to master
- Wait for CI to pass: Ensure all checks pass on feature branches before merging
- Review security scan results: Address vulnerabilities before merging to master
- Use semantic versioning: Tag releases with
vprefix (e.g.,v3.0.1) - Update version in setup.py: Before merging to master for a release
Pipeline Diagram
Non-Prod (Internal PRs and Feature Branches)
┌─────────────────────────────────────────────────────────────┐
│ Non-Prod: Internal Contributors │
├─────────────────────────────────────────────────────────────┤
│ Step 1: Security Scans (Run in Parallel) │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ snyk-scan │ │ reversing-labs │ │
│ │ • Vuln scanning │ │ • Malware scan │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │ │ │
│ └────────┬────────────────┘ │
│ ↓ │
│ Step 2: Build (Only if security scans pass) │
│ ┌────────────────────────────────────────┐ │
│ │ build │ │
│ │ • Install dependencies │ │
│ │ • Build distribution packages │ │
│ └────────────────────────────────────────┘ │
│ ↓ │
│ Step 3: Unit Tests (Only if build succeeds) │
│ ┌────────────────────────────────────────┐ │
│ │ unit_tests │ │
│ │ • Run tests/unit │ │
│ └────────────────────────────────────────┘ │
│ ↓ │
│ Step 4: Integration Tests (Only if unit tests pass) │
│ ┌────────────────────────────────────────┐ │
│ │ integration_tests │ │
│ │ • Run tests/integration │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
### Contributors (External/Forked Repository PRs)
┌─────────────────────────────────────────────────────────────┐ │ Contributors: External Contributors │ ├─────────────────────────────────────────────────────────────┤ │ ⚠️ Security scans skipped (no context access) │ │ │ │ Step 1: Build │ │ ┌────────────────────────────────────────┐ │ │ │ build │ │ │ │ • Install dependencies │ │ │ │ • Build distribution packages │ │ │ └────────────────────────────────────────┘ │ │ ↓ │ │ Step 2: Unit Tests (Only if build succeeds) │ │ ┌────────────────────────────────────────┐ │ │ │ unit_tests │ │ │ │ • Run tests/unit │ │ │ └────────────────────────────────────────┘ │ │ ↓ │ │ Step 3: Integration Tests (Only if unit tests pass) │ │ ┌────────────────────────────────────────┐ │ │ │ integration_tests │ │ │ │ • Run tests/integration │ │ │ └────────────────────────────────────────┘ │ │ │ │ ⚠️ Maintainer should review code before merging │ └─────────────────────────────────────────────────────────────┘
Prod (Master Branch Only + Version Tags)
┌─────────────────────────────────────────────────────────────┐
│ Prod: Master Branch Only │
├─────────────────────────────────────────────────────────────┤
│ Step 1: Security Scans (Run in Parallel) │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ snyk-scan │ │ reversing-labs │ │
│ │ • Vuln scanning │ │ • Malware scan │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ │ │ │
│ └────────┬────────────────┘ │
│ ↓ │
│ Step 2: Build (Only if security scans pass) │
│ ┌────────────────────────────────────────┐ │
│ │ build │ │
│ │ • Install dependencies │ │
│ │ • Build distribution packages │ │
│ └────────────────────────────────────────┘ │
│ ↓ │
│ Step 3: Unit Tests (Only if build succeeds) │
│ ┌────────────────────────────────────────┐ │
│ │ unit_tests │ │
│ │ • Run tests/unit │ │
│ └────────────────────────────────────────┘ │
│ ↓ │
│ Step 4: Integration Tests (Only if unit tests pass) │
│ ┌────────────────────────────────────────┐ │
│ │ integration_tests │ │
│ │ • Run tests/integration │ │
│ └────────────────────────────────────────┘ │
│ ↓ │
│ Step 5: Publish (Only if all tests succeed) │
│ ┌────────────────────────────────────────┐ │
│ │ publish_to_pypi │ │
│ │ • Verify distribution │ │
│ │ • Upload to PyPI │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Notes
- The pipeline uses dependency caching to speed up builds
- Test results are stored as artifacts for review
- Distribution files are verified before publishing
- All jobs use Python 3.10 Docker image for consistency