Integration - SQL Server workflow (MotleyAI/slayer)
The Integration - SQL Server workflow from MotleyAI/slayer, explained and optimized by Latchkey.
CI health: D - needs work
Point runs-on at Latchkey and get caching, run de-duplication, job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Integration - SQL Server workflow from the MotleyAI/slayer repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: Integration - SQL Server
# DEV-1564: per-dialect CI for SQL Server - pytest suite + verify.py
# end-to-end check. Path-gated to T-SQL dialect file, the SQL Server
# example, the shared SQL generator + dialect base, and this file.
#
# SQL Server is the only Tier-1 dialect that had no CI before this
# workflow existed.
on:
pull_request:
branches: [main]
paths:
- 'slayer/sql/dialects/tsql.py'
- 'slayer/sql/dialects/base.py'
- 'slayer/sql/generator.py'
- 'examples/sqlserver/**'
- 'tests/integration/test_integration_sqlserver.py'
- '.github/workflows/integration-sqlserver.yml'
push:
branches: [main]
paths:
- 'slayer/sql/dialects/tsql.py'
- 'slayer/sql/dialects/base.py'
- 'slayer/sql/generator.py'
- 'examples/sqlserver/**'
- 'tests/integration/test_integration_sqlserver.py'
- '.github/workflows/integration-sqlserver.yml'
workflow_dispatch:
jobs:
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install ODBC Driver 18 for SQL Server
# The pytest suite uses pyodbc IN-PROCESS on the runner host (unlike
# verify-example, where pyodbc lives only inside Docker containers
# built from examples/sqlserver/Dockerfile). Microsoft's apt repo
# is the canonical install path on Ubuntu - derive the version from
# /etc/os-release so this keeps working when `ubuntu-latest` rolls
# from 24.04 → 26.04 etc.
run: |
UBUNTU_VERSION="$(. /etc/os-release && echo "$VERSION_ID")"
echo "Installing msodbcsql18 for Ubuntu ${UBUNTU_VERSION}"
curl -sSL https://packages.microsoft.com/keys/microsoft.asc \
| sudo tee /etc/apt/trusted.gpg.d/microsoft.asc > /dev/null
curl -sSL "https://packages.microsoft.com/config/ubuntu/${UBUNTU_VERSION}/prod.list" \
| sudo tee /etc/apt/sources.list.d/mssql-release.list > /dev/null
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 unixodbc-dev
- name: Install Poetry
run: pip install poetry
- name: Install dependencies
run: poetry install -E all --with dev
- name: Verify testcontainers[mssql] extra is importable
run: poetry run python -c "import testcontainers.mssql"
- name: Verify ODBC Driver 18 is visible to pyodbc
run: |
poetry run python -c "import pyodbc; \
drivers = pyodbc.drivers(); \
assert 'ODBC Driver 18 for SQL Server' in drivers, \
f'Missing ODBC Driver 18 - installed: {drivers!r}'; \
print('ODBC drivers:', drivers)"
- name: Run SQL Server integration tests
timeout-minutes: 25
run: |
poetry run pytest tests/integration/test_integration_sqlserver.py \
-v -m integration --timeout=300
verify-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install verify.py dependencies
# verify.py talks HTTP to the slayer container; the ODBC driver lives
# inside the container (built from examples/sqlserver/Dockerfile) so
# the runner host doesn't need msodbcsql18 here.
run: pip install sqlalchemy
- name: Make slayer_data writable for container user
working-directory: examples/sqlserver
run: chmod -R 777 slayer_data
- name: Build images
working-directory: examples/sqlserver
run: docker compose build
- name: Start DB + seed
working-directory: examples/sqlserver
run: docker compose up -d --wait --wait-timeout 300 seed
- name: Start slayer service
working-directory: examples/sqlserver
run: docker compose up -d slayer
- name: Wait for SLayer API to accept connections
working-directory: examples/sqlserver
run: |
for i in $(seq 1 120); do
if curl -sf http://localhost:5143/datasources >/dev/null; then
echo "SLayer API ready after ${i} attempts"
exit 0
fi
if [ "$(docker compose ps -q slayer | xargs -r docker inspect -f '{{.State.Running}}')" = "false" ]; then
echo "slayer container exited before becoming ready - logs:" >&2
docker compose logs --no-color slayer
exit 1
fi
sleep 2
done
echo "SLayer API never came up - logs:" >&2
docker compose logs --no-color slayer
exit 1
- name: Run verify.py
timeout-minutes: 5
run: python examples/sqlserver/verify.py
- name: Dump all container logs
if: always()
working-directory: examples/sqlserver
run: docker compose logs --no-color
- name: Tear down stack
if: always()
working-directory: examples/sqlserver
run: docker compose down -v
The same workflow, on Latchkey
Estimated ~20% faster on cache hits, plus fewer wasted runs and a safer supply chain. Added and changed lines are highlighted.
name: Integration - SQL Server # DEV-1564: per-dialect CI for SQL Server - pytest suite + verify.py # end-to-end check. Path-gated to T-SQL dialect file, the SQL Server # example, the shared SQL generator + dialect base, and this file. # # SQL Server is the only Tier-1 dialect that had no CI before this # workflow existed. on: pull_request: branches: [main] paths: - 'slayer/sql/dialects/tsql.py' - 'slayer/sql/dialects/base.py' - 'slayer/sql/generator.py' - 'examples/sqlserver/**' - 'tests/integration/test_integration_sqlserver.py' - '.github/workflows/integration-sqlserver.yml' push: branches: [main] paths: - 'slayer/sql/dialects/tsql.py' - 'slayer/sql/dialects/base.py' - 'slayer/sql/generator.py' - 'examples/sqlserver/**' - 'tests/integration/test_integration_sqlserver.py' - '.github/workflows/integration-sqlserver.yml' workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: pytest: timeout-minutes: 30 runs-on: latchkey-small steps: - uses: actions/checkout@v4 - name: Set up Python 3.11 uses: actions/setup-python@v5 with: cache: 'pip' python-version: "3.11" - name: Install ODBC Driver 18 for SQL Server # The pytest suite uses pyodbc IN-PROCESS on the runner host (unlike # verify-example, where pyodbc lives only inside Docker containers # built from examples/sqlserver/Dockerfile). Microsoft's apt repo # is the canonical install path on Ubuntu - derive the version from # /etc/os-release so this keeps working when `ubuntu-latest` rolls # from 24.04 → 26.04 etc. run: | UBUNTU_VERSION="$(. /etc/os-release && echo "$VERSION_ID")" echo "Installing msodbcsql18 for Ubuntu ${UBUNTU_VERSION}" curl -sSL https://packages.microsoft.com/keys/microsoft.asc \ | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc > /dev/null curl -sSL "https://packages.microsoft.com/config/ubuntu/${UBUNTU_VERSION}/prod.list" \ | sudo tee /etc/apt/sources.list.d/mssql-release.list > /dev/null sudo apt-get update sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 unixodbc-dev - name: Install Poetry run: pip install poetry - name: Install dependencies run: poetry install -E all --with dev - name: Verify testcontainers[mssql] extra is importable run: poetry run python -c "import testcontainers.mssql" - name: Verify ODBC Driver 18 is visible to pyodbc run: | poetry run python -c "import pyodbc; \ drivers = pyodbc.drivers(); \ assert 'ODBC Driver 18 for SQL Server' in drivers, \ f'Missing ODBC Driver 18 - installed: {drivers!r}'; \ print('ODBC drivers:', drivers)" - name: Run SQL Server integration tests timeout-minutes: 25 run: | poetry run pytest tests/integration/test_integration_sqlserver.py \ -v -m integration --timeout=300 verify-example: timeout-minutes: 30 runs-on: latchkey-small steps: - uses: actions/checkout@v4 - name: Set up Python 3.11 uses: actions/setup-python@v5 with: cache: 'pip' python-version: "3.11" - name: Install verify.py dependencies # verify.py talks HTTP to the slayer container; the ODBC driver lives # inside the container (built from examples/sqlserver/Dockerfile) so # the runner host doesn't need msodbcsql18 here. run: pip install sqlalchemy - name: Make slayer_data writable for container user working-directory: examples/sqlserver run: chmod -R 777 slayer_data - name: Build images working-directory: examples/sqlserver run: docker compose build - name: Start DB + seed working-directory: examples/sqlserver run: docker compose up -d --wait --wait-timeout 300 seed - name: Start slayer service working-directory: examples/sqlserver run: docker compose up -d slayer - name: Wait for SLayer API to accept connections working-directory: examples/sqlserver run: | for i in $(seq 1 120); do if curl -sf http://localhost:5143/datasources >/dev/null; then echo "SLayer API ready after ${i} attempts" exit 0 fi if [ "$(docker compose ps -q slayer | xargs -r docker inspect -f '{{.State.Running}}')" = "false" ]; then echo "slayer container exited before becoming ready - logs:" >&2 docker compose logs --no-color slayer exit 1 fi sleep 2 done echo "SLayer API never came up - logs:" >&2 docker compose logs --no-color slayer exit 1 - name: Run verify.py timeout-minutes: 5 run: python examples/sqlserver/verify.py - name: Dump all container logs if: always() working-directory: examples/sqlserver run: docker compose logs --no-color - name: Tear down stack if: always() working-directory: examples/sqlserver run: docker compose down -v
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Cancel superseded runs when a branch or PR gets a newer push.
- Cache dependency installs on the setup step so they are served from cache.
- Add a job timeout so a hung step cannot burn hours of runner time.
What Latchkey heals here
This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:
- Dependency installs
- Network fetches
This workflow runs 2 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.