CI/CD Integration with sbsh¶
sbsh profiles enable reproducible environments that work identically in local development and CI/CD pipelines. Define your environment once, use it everywhere — from pre-commit hooks to GitHub Actions, GitLab CI/CD, and Jenkins pipelines.
Key Benefits for CI/CD¶
- Reproducibility: Same profile works locally and in CI — test what you ship
- Debugging: Failed CI runs create persistent terminals for inspection — no more "works on my machine"
- Version Control: Profiles are checked into your repo — environments as code
- Structured Logs: Complete I/O capture available as CI artifacts — full audit trail
- Team Consistency: Everyone uses the same environment configuration — eliminate setup drift
- Inspection: Reattach to failed CI runs to debug issues interactively
Local CI/CD Workflows¶
Use profiles for pre-commit hooks, local testing, and development workflows:
Pre-commit Hook Example¶
# ~/.sbsh/profiles.yaml
apiVersion: sbsh/v1beta1
kind: TerminalProfile
metadata:
name: test-env
spec:
shell:
cwd: "~/project"
env:
NODE_ENV: "test"
CI: "true"
stages:
onInit:
- script: docker-compose up -d
- script: npm install
- script: npm run test
GitHub Actions¶
Setup¶
Install sbsh in your GitHub Actions workflow:
- name: Setup sbsh
run: |
wget -O sbsh https://github.com/eminwux/sbsh/releases/download/v0.6.0/sbsh-linux-amd64
chmod +x sbsh && sudo install -m 0755 sbsh /usr/local/bin/sbsh
sudo ln -f /usr/local/bin/sbsh /usr/local/bin/sb
Complete Workflow Example¶
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup sbsh
run: |
wget -O sbsh https://github.com/eminwux/sbsh/releases/download/v0.6.0/sbsh-linux-amd64
chmod +x sbsh && sudo install -m 0755 sbsh /usr/local/bin/sbsh
sudo ln -f /usr/local/bin/sbsh /usr/local/bin/sb
- name: Create test profile
run: |
mkdir -p ~/.sbsh
cat > ~/.sbsh/profiles.yaml <<EOF
apiVersion: sbsh/v1beta1
kind: TerminalProfile
metadata:
name: ci-test
spec:
shell:
cwd: "${{ github.workspace }}"
env:
CI: "true"
NODE_ENV: "test"
stages:
onInit:
- script: npm install
- script: npm run test
EOF
- name: Run tests
run: sbsh -p ci-test --name "ci-${{ github.run_id }}"
- name: Upload terminal logs
if: failure()
uses: actions/upload-artifact@v3
with:
name: terminal-logs-${{ github.run_id }}
path: ~/.sbsh/run/terminals/ci-${{ github.run_id }}/*
retention-days: 7
Best Practices for GitHub Actions¶
- Use unique terminal names: Include
github.run_idorgithub.run_numberto avoid conflicts - Upload artifacts on failure: Capture terminal logs for debugging failed runs
- Set retention: Limit artifact retention to manage storage costs
- Use profiles from repo: Consider checking in profiles to your repository for version control
- Reusable workflows: Create reusable workflows for common test patterns
Using Profiles from Repository¶
Instead of creating profiles inline, you can use profiles checked into your repository:
- name: Copy profiles from repository
run: |
mkdir -p ~/.sbsh
cp .github/profiles/ci-test.yaml ~/.sbsh/profiles.yaml
# Or combine multiple profiles
cat .github/profiles/*.yaml > ~/.sbsh/profiles.yaml
GitLab CI/CD¶
Setup¶
Install sbsh in your GitLab CI pipeline:
before_script:
- |
if ! command -v sbsh &> /dev/null; then
wget -O sbsh https://github.com/eminwux/sbsh/releases/download/v0.6.0/sbsh-linux-amd64
chmod +x sbsh && sudo install -m 0755 sbsh /usr/local/bin/sbsh
sudo ln -f /usr/local/bin/sbsh /usr/local/bin/sb
fi
Complete Pipeline Example¶
# .gitlab-ci.yml
test:
image: ubuntu:latest
before_script:
- apt-get update && apt-get install -y wget procps
- |
wget -O sbsh https://github.com/eminwux/sbsh/releases/download/v0.6.0/sbsh-linux-amd64
chmod +x sbsh && sudo install -m 0755 sbsh /usr/local/bin/sbsh
sudo ln -f /usr/local/bin/sbsh /usr/local/bin/sb
script:
- |
mkdir -p ~/.sbsh
cat > ~/.sbsh/profiles.yaml <<EOF
apiVersion: sbsh/v1beta1
kind: TerminalProfile
metadata:
name: gitlab-test
spec:
shell:
cwd: "$CI_PROJECT_DIR"
env:
CI_JOB_ID: "$CI_JOB_ID"
CI_PROJECT_NAME: "$CI_PROJECT_NAME"
stages:
onInit:
- script: npm install
- script: npm run test
EOF
- sbsh -p gitlab-test --name "gitlab-$CI_JOB_ID"
artifacts:
when: always
paths:
- ~/.sbsh/run/terminals/gitlab-$CI_JOB_ID/*
expire_in: 1 week
Best Practices for GitLab CI/CD¶
- Use CI_JOB_ID: Unique job IDs ensure terminal names don't conflict
- Artifacts on always: Use
when: alwaysto capture logs even on success (useful for debugging) - Set expiration: Use
expire_into manage artifact storage - Use GitLab variables: Leverage
$CI_PROJECT_DIR,$CI_COMMIT_REF_NAME, etc. for dynamic configuration - Separate profiles: Store profiles in
.gitlab/profiles/directory for organization
Using Profiles from Repository¶
test:
script:
- mkdir -p ~/.sbsh
- cp .gitlab/profiles/ci-test.yaml ~/.sbsh/profiles.yaml
- sbsh -p ci-test --name "gitlab-$CI_JOB_ID"
Jenkins¶
Setup¶
Install sbsh in your Jenkins pipeline:
sh '''
wget -O sbsh https://github.com/eminwux/sbsh/releases/download/v0.6.0/sbsh-linux-amd64
chmod +x sbsh && sudo mv sbsh /usr/local/bin/
sudo ln -f /usr/local/bin/sbsh /usr/local/bin/sb
'''
Declarative Pipeline Example¶
// Jenkinsfile
pipeline {
agent any
environment {
SBSH_VERSION = 'v0.6.0'
}
stages {
stage('Setup') {
steps {
sh '''
wget -O sbsh https://github.com/eminwux/sbsh/releases/download/${SBSH_VERSION}/sbsh-linux-amd64
chmod +x sbsh && sudo install -m 0755 sbsh /usr/local/bin/sbsh
sudo ln -f /usr/local/bin/sbsh /usr/local/bin/sb
'''
}
}
stage('Create Profile') {
steps {
sh '''
mkdir -p ~/.sbsh
cat > ~/.sbsh/profiles.yaml <<EOF
apiVersion: sbsh/v1beta1
kind: TerminalProfile
metadata:
name: jenkins-test
spec:
shell:
cwd: "${WORKSPACE}"
env:
BUILD_NUMBER: "${BUILD_NUMBER}"
JOB_NAME: "${JOB_NAME}"
stages:
onInit:
- script: npm install
- script: npm run test
EOF
'''
}
}
stage('Run Tests') {
steps {
sh 'sbsh -p jenkins-test --name "jenkins-${BUILD_NUMBER}"'
}
}
}
post {
always {
archiveArtifacts artifacts: '~/.sbsh/run/terminals/jenkins-*/**', allowEmptyArchive: true
}
failure {
echo 'Tests failed. Check terminal logs in archived artifacts.'
}
}
}
Scripted Pipeline Example¶
// Jenkinsfile (Scripted)
node {
def buildNumber = env.BUILD_NUMBER
stage('Setup') {
sh '''
wget -O sbsh https://github.com/eminwux/sbsh/releases/download/v0.6.0/sbsh-linux-amd64
chmod +x sbsh && sudo install -m 0755 sbsh /usr/local/bin/sbsh
sudo ln -f /usr/local/bin/sbsh /usr/local/bin/sb
'''
}
stage('Create Profile') {
sh """
mkdir -p ~/.sbsh
cat > ~/.sbsh/profiles.yaml <<EOF
apiVersion: sbsh/v1beta1
kind: TerminalProfile
metadata:
name: jenkins-test
spec:
shell:
cwd: "\${WORKSPACE}"
env:
BUILD_NUMBER: "${buildNumber}"
JOB_NAME: "\${JOB_NAME}"
stages:
onInit:
- script: npm install
- script: npm run test
EOF
"""
}
stage('Run Tests') {
sh "sbsh -p jenkins-test --name jenkins-${buildNumber}"
}
stage('Archive Artifacts') {
archiveArtifacts artifacts: "~/.sbsh/run/terminals/jenkins-${buildNumber}/**", allowEmptyArchive: true
}
}
Best Practices for Jenkins¶
- Use BUILD_NUMBER: Ensure unique terminal names across builds
- Archive artifacts: Use
archiveArtifactsto preserve terminal logs - Post actions: Use
post { always { } }to archive even on failure - Workspace variables: Use
$WORKSPACEand other Jenkins environment variables - Version pinning: Store sbsh version in environment variable for easy updates
Using Profiles from Repository¶
stage('Copy Profiles') {
steps {
sh '''
mkdir -p ~/.sbsh
cp jenkins/profiles/ci-test.yaml ~/.sbsh/profiles.yaml
'''
}
}
Best Practices (All Platforms)¶
Profile Management¶
- Version control profiles: Store profiles in your repository (
.github/profiles/,.gitlab/profiles/,jenkins/profiles/) - Reuse local profiles: Same profiles work locally and in CI — test locally first
- Profile naming: Use descriptive names like
ci-test,ci-lint,ci-build - Environment variables: Use CI-specific variables for dynamic configuration
Terminal Naming¶
- Unique names: Always include unique identifiers (run ID, job ID, build number)
- Descriptive names: Include job type for easier identification:
ci-test-123,ci-build-456 - Avoid conflicts: Never reuse terminal names across concurrent runs
Artifact Management¶
- Upload on failure: Capture logs when tests fail for debugging
- Set retention: Limit artifact retention to manage storage costs
- Include metadata: Terminal metadata files contain valuable debugging information
- Compress if needed: Consider compressing large artifacts
Debugging Failed Runs¶
When a CI run fails, you can:
- Download artifacts: Terminal logs are available in CI artifacts
- Inspect logs: Check
~/.sbsh/run/terminals/<name>/capture.logfor full I/O - Check metadata: Review
meta.jsonfor terminal state and environment - Recreate locally: Use the same profile locally to reproduce issues
Common Patterns¶
Multi-stage builds:
stages:
onInit:
- script: docker build -t myapp .
- script: docker run -d --name test myapp
- script: npm run test
Environment-specific profiles:
Parallel test execution:
# GitHub Actions
jobs:
test:
strategy:
matrix:
test-suite: [unit, integration, e2e]
steps:
- run: sbsh -p ci-test-${{ matrix.test-suite }} --name "ci-${{ github.run_id }}-${{ matrix.test-suite }}"
Troubleshooting¶
Profile Not Found¶
Problem: sbsh says "profile not found" in CI
Solutions:
- Verify profile file path:
~/.sbsh/profiles.yamlby default - Check profile name matches exactly (case-sensitive)
- Ensure profile is created before running
sbsh -p - Use
sb get profilesto list available profiles
Terminal Not Starting¶
Problem: Terminal fails to start in CI
Solutions:
- Check working directory exists:
cwdmust be valid - Verify commands in
onInitare available in CI environment - Check file permissions and paths
- Review terminal logs in artifacts
Artifacts Not Uploaded¶
Problem: Terminal logs not available after CI run
Solutions:
- Verify artifact paths match terminal name pattern
- Use
when: always(GitLab) orif: always()(GitHub Actions) to capture on success - Check artifact retention settings
- Ensure terminal completed (check exit codes)
Environment Variables Not Set¶
Problem: Environment variables not available in terminal
Solutions:
- Use CI-specific variable syntax:
${{ github.workspace }},$CI_PROJECT_DIR,$WORKSPACE - Check
inheritEnvsetting in profile - Verify variable expansion in profile YAML
- Use absolute paths if variable expansion fails
See Also¶
- Profiles Guide - Learn how to create and customize profiles