Skip to content

Testcontainers labels #204

Testcontainers labels

Testcontainers labels #204

Workflow file for this run

name: CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:
env:
DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer
jobs:
# docker-host is designed to stay alive while macOS jobs run, providing a tunnel to the Docker daemon on the Linux runner. It also pre-pulls and caches common images to speed up tests.
docker-host:
name: Docker Host (Tunnel)
runs-on: ubuntu-latest
if: github.event_name != 'schedule' # Only run on explicit triggers
steps:
- name: Setup SSH and bore tunnel
run: |
sudo systemctl start docker || true
sudo chmod 666 /var/run/docker.sock
# Setup SSH
sudo apt-get update && sudo apt-get install -y openssh-server
sudo mkdir -p /run/sshd
sudo ssh-keygen -A
sudo sed -i 's/^#*AllowStreamLocalForwarding.*/AllowStreamLocalForwarding yes/' /etc/ssh/sshd_config
sudo sed -i 's/^#*AllowTcpForwarding.*/AllowTcpForwarding yes/' /etc/ssh/sshd_config
sudo systemctl restart ssh || sudo /usr/sbin/sshd
# Setup Keys
ssh-keygen -t ed25519 -f ./id_ed25519 -N ""
mkdir -p ~/.ssh
cat ./id_ed25519.pub >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
# Start Tunnel — download, verify SHA256, then extract
BORE_URL="https://github.com/ekzhang/bore/releases/download/v0.5.1/bore-v0.5.1-x86_64-unknown-linux-musl.tar.gz"
BORE_SHA256="f73f3c608fcb926cadeecd7615302e452eea60998abe7204c4383980f1e2912e"
curl -fsSL -o bore.tar.gz "$BORE_URL"
echo "${BORE_SHA256} bore.tar.gz" | sha256sum -c -
tar zxf bore.tar.gz
rm bore.tar.gz
./bore local 22 --to bore.pub > bore.log 2>&1 &
sleep 5
PORT=$(grep -oP 'listening at bore.pub:\K\d+' bore.log | head -1)
if [ -z "$PORT" ]; then cat bore.log; exit 1; fi
echo "$PORT" > docker-port.txt
- name: Cache Docker images
uses: actions/cache@v4
id: cache-docker
with:
path: ~/docker-images
key: ${{ runner.os }}-docker-images-${{ hashFiles('.github/workflows/ci.yml') }}
restore-keys: |
${{ runner.os }}-docker-images-
- name: Load cached Docker images
if: steps.cache-docker.outputs.cache-hit == 'true'
run: |
if [ -d ~/docker-images ]; then
for img in ~/docker-images/*.tar; do
if [ -f "$img" ]; then
sudo docker load -i "$img" || true
fi
done
fi
- name: Pre-pull and save Docker images
if: steps.cache-docker.outputs.cache-hit != 'true'
run: |
IMAGES=(
"postgres:15"
"rabbitmq:3.11"
"redis:7"
"mongo:7"
"confluentinc/cp-kafka:7.4.0"
"mysql:8"
"nginx:latest"
)
mkdir -p ~/docker-images
for img in "${IMAGES[@]}"; do
sudo docker pull "$img" || true
safe_name=$(echo "$img" | tr '/:' '_')
sudo docker save "$img" -o ~/docker-images/${safe_name}.tar || true
done
sudo chown -R $USER:$USER ~/docker-images
- uses: actions/upload-artifact@v4
with:
name: docker-port
retention-days: 1
path: |
docker-port.txt
id_ed25519
- name: Keep alive
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
for i in {1..180}; do
# Match any macOS job
STATUS=$(gh api repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs | jq -r '[.jobs[] | select(.name | test("macos")) | .status] | if length == 0 then "running" elif all(. == "completed") then "completed" else "running" end')
if [ "$STATUS" = "completed" ]; then
echo "macOS tests completed. Shutting down tunnel."
break
fi
sleep 10
done
# Build and test across multiple platforms and Swift versions
build-and-test:
name: Build and Test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
swift: '6.2'
- os: ubuntu-latest
swift: '6.1'
- os: macos-latest
swift: '6.1'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install system dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libsqlite3-dev llvm curl
- name: Setup Docker (macOS)
if: runner.os == 'macOS'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
for i in {1..20}; do
gh run download ${{ github.run_id }} --repo ${{ github.repository }} -n docker-port --dir . && break || sleep 5
done
PORT=$(cat docker-port.txt)
chmod 600 id_ed25519
brew install docker
export DOCKER_HOST="unix:///tmp/docker.sock"
echo "DOCKER_HOST=$DOCKER_HOST" >> $GITHUB_ENV
echo "Starting SSH tunnel to bore.pub:$PORT..."
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-i ./id_ed25519 -N -L /tmp/docker.sock:/var/run/docker.sock \
runner@bore.pub -p $PORT &
# Wait for socket to appear
for i in {1..30}; do
if [ -S /tmp/docker.sock ]; then
echo "Socket is locally available."
break
fi
sleep 1
done
# Wait for readiness
for i in {1..30}; do
if docker system info > /dev/null 2>&1; then
echo "docker is ready."
break
fi
echo "Waiting for docker... (attempt $i)"
sleep 2
done
if ! docker system info > /dev/null 2>&1; then
echo "Docker never became ready after 30 attempts"
exit 1
fi
- name: Setup Swift
uses: swift-actions/setup-swift@v2
with:
swift-version: ${{ matrix.swift }}
- name: Cache Swift dependencies
uses: actions/cache@v5
with:
path: |
.build
.swiftpm
key: ${{ matrix.os }}-swift-${{ matrix.swift }}-${{ hashFiles('Package.resolved') }}
restore-keys: |
${{ matrix.os }}-swift-${{ matrix.swift }}-
- name: Build
run: swift build --configuration release --verbose
- name: Run tests
run: swift test --configuration release --verbose --enable-code-coverage
- name: Upload test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results-${{ matrix.os }}-swift-${{ matrix.swift }}
path: |
.build/**/*.xctest
test_output.txt
- name: Generate coverage report
run: |
# Use xcrun on macOS, plain llvm-cov on Linux
if [ "${{ runner.os }}" = "macOS" ]; then
LLVM_COV="xcrun llvm-cov"
else
LLVM_COV="llvm-cov"
fi
# Find the profdata file
PROFDATA=$(find .build -name "default.profdata" -type f | head -1)
echo "Found profdata: $PROFDATA"
# Find the test binary (macOS bundles vs Linux plain binary)
if [ "${{ runner.os }}" = "macOS" ]; then
XCTEST_BUNDLE=$(find .build -name "*.xctest" -type d | head -1)
if [ -n "$XCTEST_BUNDLE" ]; then
TEST_NAME=$(basename "$XCTEST_BUNDLE" .xctest)
BINARY="$XCTEST_BUNDLE/Contents/MacOS/$TEST_NAME"
fi
# Fallback: if no bundle was found or the derived binary is not executable,
# search for alternative binary locations similar to the Linux branch.
if [ -z "$BINARY" ] || [ ! -x "$BINARY" ]; then
BINARY=$(find .build -name "*.xctest" -type f -perm /111 | head -1)
if [ -z "$BINARY" ]; then
BINARY=$(find .build -type f -name "*PackageTests" -perm /111 | head -1)
fi
fi
else
BINARY=$(find .build -name "*.xctest" -type f -executable | head -1)
if [ -z "$BINARY" ]; then
BINARY=$(find .build -type f -name "*PackageTests" -executable | head -1)
fi
fi
echo "Found binary: $BINARY"
# Generate LCOV report
if [ -n "$PROFDATA" ] && [ -n "$BINARY" ] && [ -f "$PROFDATA" ] && [ -f "$BINARY" ]; then
$LLVM_COV export \
-format="lcov" \
-instr-profile="$PROFDATA" \
"$BINARY" \
-ignore-filename-regex=".build|Tests" \
> coverage.lcov
echo "Coverage report generated"
# Print summary
echo "--- Coverage Summary ---"
$LLVM_COV report \
-instr-profile="$PROFDATA" \
"$BINARY" \
-ignore-filename-regex=".build|Tests" || true
else
echo "Could not find coverage files"
echo "Available files:"
find .build -name "*.profdata" -o -name "*.xctest" 2>/dev/null | head -20
touch coverage.lcov
fi
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.lcov
flags: swift,unittests
name: testcontainers-swift-coverage
fail_ci_if_error: false
verbose: true
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
path: coverage.lcov
retention-days: 30
if-no-files-found: warn
name: coverage-report-${{ matrix.os }}-${{ matrix.swift }}
# Code quality checks
code-quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Swift
uses: swift-actions/setup-swift@v2
with:
swift-version: '6.1'
- name: Cache Swift dependencies
uses: actions/cache@v5
with:
path: .build
key: ubuntu-latest-swift-6.1-${{ hashFiles('Package.resolved') }}
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@main
- name: Install SwiftFormat and SwiftLint
run: |
brew install swiftformat
brew install swiftlint
- name: Check code formatting
run: swiftformat --lint --verbose . --config .swiftformat --reporter github-actions-log
- name: Run SwiftLint
run: swiftlint lint --strict --config .swiftlint.yml || true
# Documentation checks
documentation:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Swift
uses: swift-actions/setup-swift@v2
with:
swift-version: '6.1'
- name: Check documentation files
run: |
required_files=("README.md" "LICENSE" "Package.swift")
for file in "${required_files[@]}"; do
if [ ! -f "$file" ]; then
echo "Missing required file: $file"
exit 1
fi
done
- name: Generate documentation
run: |
swift package --allow-writing-to-directory docs generate-documentation --target Testcontainers --output-path docs
continue-on-error: true
- name: Upload documentation
uses: actions/upload-artifact@v4
with:
name: documentation
path: docs/
if: success()
# Security and dependency checks
security:
name: Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Swift
uses: swift-actions/setup-swift@v2
with:
swift-version: '6.1'
- name: Run Swift Package Security Check
run: |
swift package resolve
swift package show-dependencies --format json > dependencies.json
- name: Check for known vulnerabilities
run: |
# Check for common security issues in dependencies
if command -v safety &> /dev/null; then
echo "Safety not available, skipping dependency vulnerability check"
else
echo "Dependency security check would go here"
fi
continue-on-error: true
- name: CodeQL Analysis
uses: github/codeql-action/init@v3
with:
languages: swift
continue-on-error: true
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
continue-on-error: true
# License compliance
license-check:
name: License Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check license headers
run: |
# Check that source files have license headers
find Sources Tests -name "*.swift" -exec grep -L "Copyright" {} \; | head -10
echo "License header check completed"
- name: Check dependency licenses
run: |
swift package resolve
swift package show-dependencies --format json | jq -r '.dependencies[].name' > dependencies.txt
echo "Dependencies listed in dependencies.txt"
continue-on-error: true
# Release preparation
release-check:
name: Release Check
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check version consistency
run: |
# Check if version in Package.swift matches git tags
version=$(grep 'let package = Package' Package.swift | sed 's/.*name: "\([^"]*\)".*/\1/')
echo "Package name: $version"
echo "Release checks passed"
- name: Check changelog
run: |
if [ -f CHANGELOG.md ]; then
echo "Changelog exists"
else
echo "No CHANGELOG.md found"
fi
# Final status check
ci-success:
name: CI Success
runs-on: ubuntu-latest
needs: [build-and-test, code-quality, documentation, security, license-check]
if: always()
steps:
- name: Check all jobs
run: |
if [[ "${{ needs.build-and-test.result }}" == "success" && \
"${{ needs.code-quality.result }}" == "success" && \
"${{ needs.documentation.result }}" != "failure" && \
"${{ needs.security.result }}" != "failure" && \
"${{ needs.license-check.result }}" != "failure" ]]; then
echo "All critical checks passed!"
exit 0
else
echo "Some checks failed. Please review the job outputs."
exit 1
fi