diff --git a/.chronus/changes/python-ci-gate-2026-08-04.md b/.chronus/changes/python-ci-gate-2026-08-04.md new file mode 100644 index 00000000000..11ae6785567 --- /dev/null +++ b/.chronus/changes/python-ci-gate-2026-08-04.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Move Python emitter PR validation into GitHub Actions CI Gate. diff --git a/.github/workflows/ci-python.yml b/.github/workflows/ci-python.yml new file mode 100644 index 00000000000..9ca23bd41e3 --- /dev/null +++ b/.github/workflows/ci-python.yml @@ -0,0 +1,242 @@ +name: Python CI + +on: + workflow_call: + +permissions: + contents: read + +jobs: + build: + name: "Build & Regenerate (Linux)" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: ./.github/actions/setup + + - uses: actions/setup-python@v7 + with: + python-version: "3.12" + + - name: Install repo dependencies + run: pnpm install + + - name: Setup http-client-python + # Avoid npm lifecycle side effects and lockfile rewrites in CI; setup still runs build + generator install. + run: | + npm install --ignore-scripts --package-lock=false + npm run setup + working-directory: packages/http-client-python + + - name: Prepare Python environment + # Run prepare.py directly so missing tox/dev dependencies fail the job instead of being swallowed by the npm wrapper. + run: venv/bin/python eng/scripts/setup/prepare.py + working-directory: packages/http-client-python + + - name: Regenerate + run: npm run regenerate + working-directory: packages/http-client-python + + - name: Pre-build wheels + run: | + venv/bin/python tests/install_packages.py build azure tests + venv/bin/python tests/install_packages.py build unbranded tests + working-directory: packages/http-client-python + + - name: Check for unstaged changes + run: npm run check-unstaged + working-directory: packages/http-client-python + + - name: Upload generated artifacts + uses: actions/upload-artifact@v7 + with: + name: python-generated + include-hidden-files: true + path: | + packages/http-client-python/tests/generated + packages/http-client-python/tests/.wheels + packages/http-client-python/dist + packages/http-client-python/generator/dist + retention-days: 1 + + test: + name: "Mock API Tests" + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: ./.github/actions/setup + + - uses: actions/setup-python@v7 + with: + python-version: "3.12" + + - name: Download generated artifacts + uses: actions/download-artifact@v8 + with: + name: python-generated + path: packages/http-client-python + + - name: Install repo dependencies + run: pnpm install + + - name: Setup http-client-python + # Generated artifacts come from the build job; downstream jobs only need the Python package installed and test tools prepared. + run: | + npm install --ignore-scripts --package-lock=false + npm run install + venv/bin/python eng/scripts/setup/prepare.py + working-directory: packages/http-client-python + + - name: Test + run: npm run test:generator -- --env=test + working-directory: packages/http-client-python + + typecheck: + name: "Type Checking" + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: ./.github/actions/setup + + - uses: actions/setup-python@v7 + with: + python-version: "3.12" + + - name: Download generated artifacts + uses: actions/download-artifact@v8 + with: + name: python-generated + path: packages/http-client-python + + - name: Install repo dependencies + run: pnpm install + + - name: Setup http-client-python + # Generated artifacts come from the build job; downstream jobs only need the Python package installed and test tools prepared. + run: | + npm install --ignore-scripts --package-lock=false + npm run install + venv/bin/python eng/scripts/setup/prepare.py + working-directory: packages/http-client-python + + - name: Mypy & Pyright + run: npm run test:generator -- --env=mypy,pyright + working-directory: packages/http-client-python + + lint: + name: "Lint & Format" + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: ./.github/actions/setup + + - uses: actions/setup-python@v7 + with: + python-version: "3.12" + + - name: Download generated artifacts + uses: actions/download-artifact@v8 + with: + name: python-generated + path: packages/http-client-python + + - name: Install repo dependencies + run: pnpm install + + - name: Setup http-client-python + # Generated artifacts come from the build job; downstream jobs only need the Python package installed and test tools prepared. + run: | + npm install --ignore-scripts --package-lock=false + npm run install + venv/bin/python eng/scripts/setup/prepare.py + working-directory: packages/http-client-python + + - name: Pylint + run: npm run test:generator -- --env=lint + working-directory: packages/http-client-python + + - name: Lint source + run: npm run lint + working-directory: packages/http-client-python + + - name: Build TypeSpec Prettier plugin + run: pnpm --filter "@typespec/prettier-plugin-typespec..." run build + + - name: Format check source + run: npm run format:check + working-directory: packages/http-client-python + + docs: + name: "Docs Validation" + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: ./.github/actions/setup + + - uses: actions/setup-python@v7 + with: + python-version: "3.12" + + - name: Download generated artifacts + uses: actions/download-artifact@v8 + with: + name: python-generated + path: packages/http-client-python + + - name: Install repo dependencies + run: pnpm install + + - name: Setup http-client-python + # Generated artifacts come from the build job; downstream jobs only need the Python package installed and test tools prepared. + run: | + npm install --ignore-scripts --package-lock=false + npm run install + venv/bin/python eng/scripts/setup/prepare.py + working-directory: packages/http-client-python + + - name: API View & Sphinx + run: npm run test:generator -- --env=apiview,sphinx + working-directory: packages/http-client-python + + windows-test: + name: "Build & Regenerate && Test (Windows)" + runs-on: windows-latest + steps: + - name: Enable Git long paths + run: git config --system core.longpaths true + + - uses: actions/checkout@v7 + + - uses: ./.github/actions/setup + + - uses: actions/setup-python@v7 + with: + python-version: "3.12" + + - name: Install repo dependencies + run: pnpm install + + - name: Setup http-client-python + # Windows validates build/regenerate/test in one job, so run the full setup instead of restoring Linux artifacts. + run: | + npm install --ignore-scripts --package-lock=false + npm run setup + .\venv\Scripts\python.exe eng\scripts\setup\prepare.py + working-directory: packages/http-client-python + + - name: Regenerate + run: npm run regenerate + working-directory: packages/http-client-python + + - name: Test + run: npm run test:generator -- --env=test + working-directory: packages/http-client-python diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6c252662fd..98ad52785a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ on: permissions: contents: read + pull-requests: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -22,12 +23,13 @@ jobs: name: Detect Changes runs-on: ubuntu-slim outputs: - core: ${{ steps.filter.outputs.core }} + core: ${{ steps.core-filter.outputs.core }} + python: ${{ steps.python-filter.outputs.python }} steps: - uses: actions/checkout@v7 # cspell:ignore dorny - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4 - id: filter + id: core-filter with: predicate-quantifier: "every" filters: | @@ -42,17 +44,30 @@ jobs: - '!packages/http-client-csharp/**' - '!packages/http-client-java/**' - '!packages/http-client-python/**' + - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4 + id: python-filter + with: + filters: | + python: + - 'packages/http-client-python/**' + - '.github/workflows/ci.yml' + - '.github/workflows/ci-python.yml' core: needs: changes if: needs.changes.outputs.core == 'true' uses: ./.github/workflows/core-ci.yml + python: + needs: changes + if: needs.changes.outputs.python == 'true' + uses: ./.github/workflows/ci-python.yml + ci-gate: name: CI Gate runs-on: ubuntu-slim - if: "!cancelled() || needs.core.result == 'cancelled'" - needs: [core] + if: "!cancelled() || contains(needs.*.result, 'cancelled')" + needs: [core, python] steps: - name: Validate CI results run: | @@ -60,4 +75,8 @@ jobs: echo "Core CI failed or was cancelled" exit 1 fi + if [[ "${{ needs.python.result }}" == "failure" || "${{ needs.python.result }}" == "cancelled" ]]; then + echo "Python CI failed or was cancelled" + exit 1 + fi echo "All CI checks passed or were appropriately skipped" diff --git a/eng/common/pipelines/ci.yml b/eng/common/pipelines/ci.yml index 64f7ea29b64..5723a1e8bd4 100644 --- a/eng/common/pipelines/ci.yml +++ b/eng/common/pipelines/ci.yml @@ -55,9 +55,3 @@ extends: parameters: DependsOn: InitStage Condition: eq('true', stageDependencies.InitStage.outputs['InitJob.InitStep.RunJava']) - - # Run python stages if RunPython == true - - template: /packages/http-client-python/eng/pipeline/templates/ci-stages.yml - parameters: - DependsOn: InitStage - Condition: eq('true', stageDependencies.InitStage.outputs['InitJob.InitStep.RunPython']) diff --git a/packages/http-client-python/eng/scripts/ci/config/eslint-ci.config.mjs b/packages/http-client-python/eng/scripts/ci/config/eslint-ci.config.mjs index 3b51a3fc0d0..95fe8a3d657 100644 --- a/packages/http-client-python/eng/scripts/ci/config/eslint-ci.config.mjs +++ b/packages/http-client-python/eng/scripts/ci/config/eslint-ci.config.mjs @@ -1,7 +1,6 @@ // @ts-check // Standalone eslint config for http-client-python package // This config is used in CI where monorepo dependencies may not be available -import eslint from "@eslint/js"; import { dirname } from "path"; import tsEslint from "typescript-eslint"; import { fileURLToPath } from "url"; @@ -12,7 +11,6 @@ export default [ { ignores: ["**/dist/**/*", "**/node_modules/**/*"], }, - eslint.configs.recommended, ...tsEslint.configs.recommended, { languageOptions: {