Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
- 'pytest.ini'
- 'setup.py'
- 'tests/**'
pull_request_target:
pull_request:
branches:
- master
paths:
Expand All @@ -37,8 +37,8 @@ jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: '3.10'
- run: pip install uv
Expand Down Expand Up @@ -68,9 +68,9 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -84,4 +84,4 @@ jobs:

- name: Run tests
run: |
IPINFO_TOKEN='${{ secrets.IPINFO_TOKEN }}' REDIS_URL='redis://localhost:6379' REDIS_PREFIX='test:tornadoapi_guard:' uv run pytest -v --cov=tornadoapi_guard
IPINFO_TOKEN='${{ secrets.IPINFO_TOKEN || 'test_token' }}' REDIS_URL='redis://localhost:6379' REDIS_PREFIX='test:tornadoapi_guard:' uv run pytest -v --cov=tornadoapi_guard --cov-fail-under=100
2 changes: 1 addition & 1 deletion .github/workflows/code-ql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/container-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Set version tag
id: set_version
Expand All @@ -33,7 +33,7 @@ jobs:
echo "IS_RELEASE=false" >> $GITHUB_ENV
fi
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.10'

Expand Down
49 changes: 49 additions & 0 deletions .github/workflows/docksec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: DockSec

on:
push:
branches: [master]
paths:
- 'Dockerfile'
- '.github/workflows/docksec.yml'
pull_request:
paths:
- 'Dockerfile'
- '.github/workflows/docksec.yml'
schedule:
- cron: '0 6 * * 1'

permissions:
contents: read

jobs:
scan:
name: Scan Dockerfile
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
security-events: write
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Run DockSec
uses: OWASP/DockSec@d52c8e928552dc39003abda21ac6bc4662b06c39 # v2026.9.21
with:
dockerfile: Dockerfile
scan_only: 'true'
sarif: 'true'
output_dir: ${{ github.workspace }}/docksec-results

- name: Upload SARIF to Code Scanning
# Not `if: always()`: on a fork pull_request, GITHUB_TOKEN is read-only and this
# upload would fail every time, turning a routine fork contribution into a red
# required check. Skip it there instead of forcing a failure.
if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v3
with:
sarif_file: docksec-results
category: docksec
8 changes: 6 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ on:
permissions:
contents: write

concurrency:
group: docs-deploy
cancel-in-progress: false

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/setup-python@v6
- uses: actions/setup-python@v7
with:
python-version: 3.x

Expand Down
36 changes: 36 additions & 0 deletions .github/workflows/issue-link.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Issue link

on:
pull_request:
types: [opened, edited, reopened, synchronize, labeled, unlabeled]

permissions:
issues: read
pull-requests: read

jobs:
issue-link:
name: issue-link
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v9
with:
script: |
const pr = context.payload.pull_request;
if (pr.user.type === 'Bot') return core.info(`bot author ${pr.user.login}, exempt`);
if (pr.labels.some((label) => label.name === 'no-issue')) return core.info('no-issue label, exempt');
const pattern = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?|delivers?(?:\s+issue)?)\b:?\s*(?:https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/issues\/|#)(\d+)/gi;
const numbers = [...new Set([...(pr.body || '').matchAll(pattern)].map((match) => Number(match[1])))];
if (numbers.length === 0) return core.setFailed('PR body must say "Closes #N" for an open issue in this repo, or the PR must carry the no-issue label');
const reasons = [];
for (const number of numbers) {
try {
const { data } = await github.rest.issues.get({ ...context.repo, issue_number: number });
if (data.pull_request) reasons.push(`#${number} is a pull request, not an issue`);
else if (data.state !== 'open') reasons.push(`#${number} is ${data.state}`);
else return core.info(`delivers open issue #${number}`);
} catch (error) {
reasons.push(`#${number}: ${error.message}`);
}
}
core.setFailed(reasons.join('; '));
2 changes: 1 addition & 1 deletion .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
pull-requests: write

steps:
- uses: actions/labeler@v6
- uses: actions/labeler@v7
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
configuration-path: .github/labeler.yml
Expand Down
121 changes: 121 additions & 0 deletions .github/workflows/live-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Live Smoke

on:
push:
branches:
- master
paths:
- 'tornadoapi_guard/**'
- 'examples/**'
- '.github/workflows/live-smoke.yml'
pull_request:
paths:
- 'tornadoapi_guard/**'
- 'examples/**'
- '.github/workflows/live-smoke.yml'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: live-smoke-${{ github.ref }}
cancel-in-progress: true

jobs:
live-smoke:
name: Dockerized smoke run (simple_app)
runs-on: ubuntu-latest
timeout-minutes: 15
env:
LIVE_SMOKE_PROJECT: tornadoapi-guard-live-smoke

steps:
- name: Checkout code
uses: actions/checkout@v7
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: '3.12'

- name: Install uv
run: |
pip install uv

- name: Build the tornadoapi-guard wheel from this checkout
run: |
uv build --wheel --out-dir examples/live_smoke_wheels

- name: Stage the example app with the checkout wheel
run: |
rm -rf live_smoke_stack
cp -r examples/simple_app live_smoke_stack
wheel=$(ls examples/live_smoke_wheels/tornadoapi_guard-*.whl | head -n 1)
cp "$wheel" live_smoke_stack/
# Install the wheel from this checkout instead of the PyPI release;
# the wheel must be COPYed into the image before pip runs
python3 - <<'EOF'
import pathlib
stack = pathlib.Path("live_smoke_stack")
wheel = next(stack.glob("tornadoapi_guard-*.whl"))
req = stack / "requirements.txt"
lines = [line for line in req.read_text().splitlines() if not line.startswith("tornadoapi-guard")]
lines.append(f"tornadoapi-guard @ file:///app/{wheel.name}")
req.write_text("\n".join(lines) + "\n")
dockerfile = stack / "Dockerfile"
df = dockerfile.read_text()
anchor = "COPY requirements.txt /app/"
assert anchor in df
dockerfile.write_text(df.replace(anchor, anchor + f"\nCOPY {wheel.name} /app/"))
EOF

- name: Bring the smoke stack up
run: |
docker compose -p "$LIVE_SMOKE_PROJECT" -f live_smoke_stack/docker-compose.yml up --build -d --wait

- name: Run smoke assertions
run: |
set -euo pipefail
base="http://localhost:8000"

# Wait for the app to finish initializing (engine redis + ipinfo setup)
ready=""
for i in $(seq 1 30); do
if [ "$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 "$base/")" = "200" ]; then
ready=1; break
fi
sleep 2
done
if [ -z "$ready" ]; then
echo "FAIL: app never became ready"; exit 1
fi

check() {
local name="$1" expected="$2"; shift 2
local status
status=$(curl -sS --max-time 10 -o /tmp/smoke_body -w '%{http_code}' "$@") || {
echo "FAIL: $name (curl error)"; exit 1
}
if [ "$status" != "$expected" ]; then
echo "FAIL: $name (expected $expected, got $status)"; cat /tmp/smoke_body; exit 1
fi
echo "ok: $name -> $status"
}

check "GET / serves" 200 "$base/"
check "GET /basic/health serves" 200 "$base/basic/health"
# The guard's custom_request_check blocks debug=true with a 403 body
check "GET /?debug=true blocked by custom check" 403 "$base/?debug=true"

- name: Dump container logs on failure
if: failure()
run: |
docker compose -p "$LIVE_SMOKE_PROJECT" -f live_smoke_stack/docker-compose.yml logs --no-color

- name: Bring the smoke stack down
if: always()
run: |
docker compose -p "$LIVE_SMOKE_PROJECT" -f live_smoke_stack/docker-compose.yml down -v --remove-orphans
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.10'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
pip install build twine

- name: Build package
run: |
python setup.py sdist bdist_wheel
python -m build

- name: Publish package
env:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/scheduled-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.10'

Expand All @@ -32,9 +32,9 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.10'

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sync-labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
labeler:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Sync labels from .github/labels.yml
uses: crazy-max/ghaction-github-labeler@v5
Expand Down
Loading
Loading