1515 workflow_dispatch :
1616 inputs :
1717 pypiVersion :
18- description : ' Exact stamped PyPI release (overrides PYRIT_PYPI_VERSION )'
18+ description : ' Exact published PyPI version (defaults to the latest stable release )'
1919 required : false
2020 type : string
2121
@@ -31,6 +31,45 @@ concurrency:
3131 cancel-in-progress : true
3232
3333jobs :
34+ validate-compose-bindings :
35+ name : Validate Compose Bindings
36+ runs-on : ubuntu-latest
37+ steps :
38+ - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
39+
40+ - name : Require loopback-only published ports
41+ working-directory : docker
42+ shell : pwsh
43+ run : |
44+ $ErrorActionPreference = 'Stop'
45+ $env:PYRIT_SOURCE_COMMIT = git rev-parse --verify HEAD
46+ if ($LASTEXITCODE -ne 0) {
47+ throw "Cannot determine source commit."
48+ }
49+ $sourceStatus = git status --porcelain
50+ if ($LASTEXITCODE -ne 0) {
51+ throw "Cannot determine source dirty state."
52+ }
53+ $env:PYRIT_SOURCE_DIRTY = if ($sourceStatus) { 'true' } else { 'false' }
54+ $json = docker compose --file docker-compose.yaml --profile gui --profile jupyter config --format json
55+ if ($LASTEXITCODE -ne 0) {
56+ throw "Docker Compose configuration resolution failed."
57+ }
58+ $services = ($json | ConvertFrom-Json).services
59+ $expectedPorts = @{ 'pyrit-gui' = 8000; 'pyrit-jupyter' = 8888 }
60+ foreach ($service in $expectedPorts.Keys) {
61+ $port = $expectedPorts[$service]
62+ $bindings = @($services.$service.ports)
63+ if ($bindings.Count -ne 1 -or
64+ $bindings[0].host_ip -ne '127.0.0.1' -or
65+ $bindings[0].published -ne "$port" -or
66+ $bindings[0].target -ne $port -or
67+ $bindings[0].protocol -ne 'tcp') {
68+ throw "Expected only 127.0.0.1:${port}:${port}/tcp for ${service}; got $($bindings | ConvertTo-Json -Compress)."
69+ }
70+ Write-Output "${service}: verified 127.0.0.1:${port}:${port}/tcp"
71+ }
72+
3473 build-and-test-local :
3574 name : Build and test (local)
3675 runs-on : ubuntu-latest
@@ -108,7 +147,8 @@ jobs:
108147 build-and-test-pypi :
109148 name : Build and test (PyPI)
110149 runs-on : ubuntu-latest
111- if : github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
150+ # Restore main-push checks after validating 1.2.0: https://github.com/microsoft/PyRIT/issues/3007
151+ if : github.event_name == 'workflow_dispatch'
112152 outputs :
113153 production : ${{ steps.production.outcome }}
114154 import : ${{ steps.import.outcome }}
@@ -117,20 +157,17 @@ jobs:
117157 steps :
118158 - uses : actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
119159
120- - name : Select coordinated PyPI release
160+ - name : Select PyPI release
121161 id : pypi-version
162+ timeout-minutes : 5
122163 env :
123- PYRIT_PYPI_VERSION : ${{ inputs.pypiVersion || vars.PYRIT_PYPI_VERSION }}
164+ PYRIT_PYPI_VERSION : ${{ inputs.pypiVersion }}
124165 shell : bash
125166 run : |
126167 set -euo pipefail
127- version_pattern='^[0-9]+\.[0-9]+\.[0-9]+((a|b|rc)[0-9]+)?(\.post[0-9]+)?(\.dev[0-9]+)?$'
128- if [[ ! "${PYRIT_PYPI_VERSION:-}" =~ $version_pattern ]]; then
129- echo "::error::Set pypiVersion or repository variable PYRIT_PYPI_VERSION to an exact coordinated PyPI release."
130- exit 1
131- fi
132- echo "version=$PYRIT_PYPI_VERSION" >> "$GITHUB_OUTPUT"
133- echo "Testing coordinated PyPI release: $PYRIT_PYPI_VERSION"
168+ version=$(python3 build_scripts/select_pypi_version.py --version "${PYRIT_PYPI_VERSION:-}")
169+ echo "version=$version" >> "$GITHUB_OUTPUT"
170+ echo "Testing published PyPI release: $version"
134171
135172 - name : Set up cached devcontainer builder
136173 uses : docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
@@ -219,7 +256,7 @@ jobs:
219256 pypi-production-check :
220257 name : Build Production (PyPI)
221258 needs : build-and-test-pypi
222- if : ${{ always() && (github.ref == 'refs/heads/main' || github. event_name == 'workflow_dispatch') }}
259+ if : ${{ always() && (github.event_name == 'workflow_dispatch') }}
223260 runs-on : ubuntu-latest
224261 steps :
225262 - name : Check execution and stage results
@@ -237,7 +274,7 @@ jobs:
237274 pypi-import-check :
238275 name : Test Import (PyPI)
239276 needs : build-and-test-pypi
240- if : ${{ always() && (github.ref == 'refs/heads/main' || github. event_name == 'workflow_dispatch') }}
277+ if : ${{ always() && (github.event_name == 'workflow_dispatch') }}
241278 runs-on : ubuntu-latest
242279 steps :
243280 - name : Check execution and stage results
@@ -255,7 +292,7 @@ jobs:
255292 pypi-gui-check :
256293 name : Test GUI (PyPI)
257294 needs : build-and-test-pypi
258- if : ${{ always() && (github.ref == 'refs/heads/main' || github. event_name == 'workflow_dispatch') }}
295+ if : ${{ always() && (github.event_name == 'workflow_dispatch') }}
259296 runs-on : ubuntu-latest
260297 steps :
261298 - name : Check execution and stage results
@@ -273,7 +310,7 @@ jobs:
273310 pypi-jupyter-check :
274311 name : Test Jupyter (PyPI)
275312 needs : build-and-test-pypi
276- if : ${{ always() && (github.ref == 'refs/heads/main' || github. event_name == 'workflow_dispatch') }}
313+ if : ${{ always() && (github.event_name == 'workflow_dispatch') }}
277314 runs-on : ubuntu-latest
278315 steps :
279316 - name : Check execution and stage results
0 commit comments