diff --git a/.github/workflows/build-test-lint.yml b/.github/workflows/build-test-lint.yml index 14722dcc..abef2b57 100644 --- a/.github/workflows/build-test-lint.yml +++ b/.github/workflows/build-test-lint.yml @@ -1,63 +1,91 @@ -name: Python Build, Lint +name: Python Build, Lint and Test on: - push: + push: + branches: [master] + pull_request: + types: [opened, reopened, synchronize] + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: - build-test-lint: - runs-on: ubuntu-latest - - steps: - - name: Checkout xero-python repo - uses: actions/checkout@v4 - with: - repository: XeroAPI/xero-python - path: xero-python - - - name: Set up Python environment - uses: actions/setup-python@v5 - with: - python-version: '3.8' - cache: 'pip' - - - name: Install dependencies - run: | - python -m venv venv - source venv/bin/activate - pip install --upgrade pip - pip install black - sudo pip install flake8 - sudo pip install pip-audit - pip install -r requirements.txt -r requirements/dev.txt - working-directory: xero-python - - - name: Run Flake8 - run: flake8 xero_python - working-directory: xero-python - - - name: Build package - run: python setup.py sdist - working-directory: xero-python - - - name: Set up Node environment - uses: actions/setup-node@v2 - with: - node-version: 20 - - - name: Install Prism - run: npm install -g @stoplight/prism-cli - - - name: Start PRISM Server - run: ./start-prism.sh & sleep 15 - working-directory: xero-python/tests/utils - - - name: Run Test - run: | - source venv/bin/activate - pytest -v - working-directory: xero-python - - - name: Stop PRISM - if: success() || failure() - run: pkill -f prism - working-directory: xero-python + build-test-lint: + name: Python ${{ matrix.python }} + runs-on: ubuntu-latest + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + python: ["3.8", "3.12", "3.13"] + + steps: + - name: Check out event source + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.2.2 + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: ${{ matrix.python }} + cache: pip + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt -r requirements/dev.txt + python -m pip check + + - name: Lint + run: python -m flake8 --select E9,F63,F7,F82 xero_python + + - name: Audit runtime dependencies + run: python -m pip_audit -r requirements.txt + + - name: Build package + if: matrix.python == '3.13' + run: python -m build + + - name: Set up Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: 24.18 + + - name: Install Prism + run: npm install --global @stoplight/prism-cli@5.16.0 + + - name: Start Prism + env: + XERO_OPENAPI_REF: ee65f8bddb16f37cde3e32741d465eeed6507a74 + run: | + bash tests/utils/start-prism.sh "$XERO_OPENAPI_REF" + python - <<'PY' + import socket + import time + + def listening(port): + try: + with socket.create_connection(("127.0.0.1", port), timeout=1): + return True + except OSError: + return False + + ports = range(4010, 4019) + deadline = time.monotonic() + 60 + while time.monotonic() < deadline: + if all(listening(port) for port in ports): + break + time.sleep(1) + else: + raise SystemExit("Prism did not open ports 4010-4018 within 60 seconds") + PY + + - name: Test + run: python -m pytest -v + + - name: Stop Prism + if: always() + run: pkill -f "[p]rism mock" || true diff --git a/requirements/dev.txt b/requirements/dev.txt index ced8db1c..81c1c4a4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,3 +1,5 @@ -r test.txt -r flake8.txt +build +pip-audit==2.10.1 tox diff --git a/requirements/flake8.txt b/requirements/flake8.txt index d266010c..39304807 100644 --- a/requirements/flake8.txt +++ b/requirements/flake8.txt @@ -1 +1 @@ -flake8-black +flake8 diff --git a/tests/utils/start-prism.sh b/tests/utils/start-prism.sh index be7c09e3..8eff015d 100755 --- a/tests/utils/start-prism.sh +++ b/tests/utils/start-prism.sh @@ -1,12 +1,18 @@ #!/bin/bash -branchName=${1:-"master"} +specRef=${1:?pass an immutable Xero-OpenAPI commit SHA} +if ! [[ "$specRef" =~ ^[0-9a-f]{40}$ ]]; then + echo "Xero-OpenAPI ref must be a full commit SHA" >&2 + exit 2 +fi -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero_accounting.yaml --host 127.0.0.1 --port 4010 & -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero-app-store.yaml --host 127.0.0.1 --port 4011 & -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero_assets.yaml --host 127.0.0.1 --port 4012 & -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero_bankfeeds.yaml --host 127.0.0.1 --port 4013 & -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero-finance.yaml --host 127.0.0.1 --port 4014 & -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero-payroll-uk.yaml --host 127.0.0.1 --port 4015 & -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero-payroll-nz.yaml --host 127.0.0.1 --port 4016 & -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero-payroll-au.yaml --host 127.0.0.1 --port 4017 & -prism mock https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/refs/heads/$branchName/xero-projects.yaml --host 127.0.0.1 --port 4018 +specUrl="https://raw.githubusercontent.com/XeroAPI/Xero-OpenAPI/$specRef" + +prism mock "$specUrl/xero_accounting.yaml" --host 127.0.0.1 --port 4010 & +prism mock "$specUrl/xero-app-store.yaml" --host 127.0.0.1 --port 4011 & +prism mock "$specUrl/xero_assets.yaml" --host 127.0.0.1 --port 4012 & +prism mock "$specUrl/xero_bankfeeds.yaml" --host 127.0.0.1 --port 4013 & +prism mock "$specUrl/xero-finance.yaml" --host 127.0.0.1 --port 4014 & +prism mock "$specUrl/xero-payroll-uk.yaml" --host 127.0.0.1 --port 4015 & +prism mock "$specUrl/xero-payroll-nz.yaml" --host 127.0.0.1 --port 4016 & +prism mock "$specUrl/xero-payroll-au.yaml" --host 127.0.0.1 --port 4017 & +prism mock "$specUrl/xero-projects.yaml" --host 127.0.0.1 --port 4018