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
26 changes: 26 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Repository metadata and local credentials are not runtime inputs.
.git
**/.git
.env
**/.env
.env.*
**/.env.*
.ssh
**/.ssh
.aws
**/.aws
.netrc
**/.netrc
.pypirc
**/.pypirc
**/github_token
**/hf_token
.mcp-tasks
**/.mcp-tasks

# Local development environments and generated caches.
.venv
venv
**/__pycache__
.pytest_cache
.mypy_cache
11 changes: 11 additions & 0 deletions .github/workflows/sensevoice-container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
paths:
- Dockerfile
- .dockerignore
- docker-compose.yaml
- requirements.txt
- api.py
Expand All @@ -15,6 +16,7 @@ on:
- CONTRIBUTING.md
- .github/workflows/sensevoice-container.yml
- tests/test_container_contract.py
- tests/test_docker_context.sh
- tests/test_device_env.py
push:
branches:
Expand All @@ -23,6 +25,7 @@ on:
- "v*"
paths:
- Dockerfile
- .dockerignore
- docker-compose.yaml
- requirements.txt
- api.py
Expand All @@ -34,6 +37,7 @@ on:
- CONTRIBUTING.md
- .github/workflows/sensevoice-container.yml
- tests/test_container_contract.py
- tests/test_docker_context.sh
- tests/test_device_env.py
workflow_dispatch:

Expand All @@ -50,6 +54,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Validate container contract
run: python -m unittest tests.test_container_contract tests.test_device_env

Expand All @@ -58,8 +64,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: docker/setup-buildx-action@v3

- name: Verify Docker build context exclusions
run: bash tests/test_docker_context.sh

- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
Expand Down
20 changes: 20 additions & 0 deletions tests/test_container_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@


class ContainerContractTest(unittest.TestCase):
def test_build_context_excludes_local_credentials_and_git_metadata(self):
ignore = ROOT / '.dockerignore'
self.assertTrue(ignore.is_file(), 'COPY . requires an explicit context boundary')
patterns = {line.strip() for line in ignore.read_text().splitlines()
if line.strip() and not line.lstrip().startswith('#')}
required = {'.git', '**/.git', '.env', '**/.env', '.env.*', '**/.env.*',
'.ssh', '**/.ssh', '.aws', '**/.aws', '.netrc', '**/.netrc',
'.pypirc', '**/.pypirc', '**/github_token', '**/hf_token',
'.mcp-tasks', '**/.mcp-tasks'}
self.assertTrue(required <= patterns, required - patterns)
self.assertFalse(any(pattern.startswith('!') for pattern in patterns))

def test_context_changes_run_ci_without_persisting_checkout_credentials(self):
workflow = (ROOT / '.github/workflows/sensevoice-container.yml').read_text()
self.assertEqual(workflow.count('- .dockerignore'), 2)
self.assertEqual(workflow.count('persist-credentials: false'),
workflow.count('uses: actions/checkout@v4'))
self.assertEqual(workflow.count('- tests/test_docker_context.sh'), 2)
self.assertIn('run: bash tests/test_docker_context.sh', workflow)

def test_container_base_matches_repository_torch_floor(self):
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")

Expand Down
27 changes: 27 additions & 0 deletions tests/test_docker_context.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail

cd "$(dirname "$0")/.."
probe=$(mktemp -d .container-context-probe.XXXXXX)
output=$(mktemp -d "${TMPDIR:-/tmp}/sensevoice-context.XXXXXX")
trap 'rm -rf -- "$probe" "$output"' EXIT

# Harmless sentinels exercise Docker's matcher without reading credentials.
mkdir -p "$probe/.git" "$probe/.ssh" "$probe/.aws"
touch "$probe/.git/sentinel" "$probe/.ssh/sentinel" "$probe/.aws/sentinel"
touch "$probe/.env" "$probe/.env.test" "$probe/.netrc" "$probe/.pypirc"
touch "$probe/github_token" "$probe/hf_token" "$probe/runtime-sentinel.txt"

docker buildx build --file - --output "type=local,dest=$output" . <<'DOCKERFILE'
FROM scratch
COPY . /
DOCKERFILE

test ! -e "$output/.git"
for excluded in .git .ssh .aws .env .env.test .netrc .pypirc github_token hf_token; do
test ! -e "$output/$probe/$excluded"
done
for required in api.py model.py requirements.txt utils/device_env.py "$probe/runtime-sentinel.txt"; do
test -f "$output/$required"
done
printf '%s\n' 'Docker context exclusions and runtime inputs verified.'
Loading