diff --git a/.github/workflows/ci-config.yml b/.github/workflows/ci-config.yml new file mode 100644 index 00000000000..1bde5381894 --- /dev/null +++ b/.github/workflows/ci-config.yml @@ -0,0 +1,76 @@ +--- +name: Qiskit CI Workflow (Docs, Lint, and Tests) +on: + push: + branches: [main, "stable/*"] + pull_request: + branches: [main, "stable/*"] + merge_group: + +concurrency: + group: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + lint-docs: + name: Lint and Docs + runs-on: ubuntu-latest + steps: + - uses: ./.github/workflows/lint_docs.yml + preliminary-tests: + name: Preliminary Tests + runs-on: ubuntu-latest + steps: + - uses: ./.github/workflows/test-linux.yml + with: + python-version: "3.9" + # A PR is more likely to fail CI because it introduces a logic error + # into an existing test than because it adds a new test / optional + # dependency that isn't accounted for in the test-skipping logic + # (and such a failure would need fewer iterations to fix). We want + # to fail fast in the first CI stage. + install-optionals: true + test-rust: true + test-images: true + main-tests-ubuntu: + runs-on: ubuntu-latest + name: Main Tests (ubuntu-latest) + needs: [lint-docs, preliminary-tests] + strategy: + matrix: + python_version: ["3.13"] + steps: + - uses: ./.github/workflows/test-linux.yml + with: + python-version: ${{ matrix.python_version }} + test-rust: false + test-images: false + install-from-sdist: true + install-optionals: false + + main-tests-mac: + runs-on: macos-14 + name: Main Tests (ubuntu-latest) + needs: [lint-docs, preliminary-tests] + strategy: + matrix: + python_version: ["3.9", "3.13"] + steps: + - uses: ./.github/workflows/test-macos.yml + with: + python-version: ${{ matrix.python_version }} + install-optionals: ${{ matrix.python_version == '3.9'}} + + main-tests-windows: + runs-on: macos-14 + name: Main Tests (ubuntu-latest) + needs: [lint-docs, preliminary-tests] + strategy: + matrix: + python_version: ["3.9", "3.13"] + steps: + - uses: ./.github/workflows/test-windows.yml + with: + python-version: ${{ matrix.python_version }} + install-optionals: ${{ matrix.python_version == '3.9'}} + diff --git a/.github/workflows/lint_docs.yml b/.github/workflows/lint_docs.yml index 941c20bd1c7..e4bab1bdd2c 100644 --- a/.github/workflows/lint_docs.yml +++ b/.github/workflows/lint_docs.yml @@ -1,10 +1,6 @@ --- name: Lint and Docs on: - push: - branches: [ main, 'stable/*' ] - pull_request: - branches: [ main, 'stable/*' ] merge_group: concurrency: diff --git a/.github/workflows/test-mac.yml b/.github/workflows/test-mac.yml new file mode 100644 index 00000000000..703f0a0964e --- /dev/null +++ b/.github/workflows/test-mac.yml @@ -0,0 +1,88 @@ +--- +name: Test Windows +on: + workflow_call: + inputs: + python-version: + description: > + Python version to currently test + type: string + required: true + + install-optionals: + description: > + Decides whether we install optyional dependencies + type: boolean + default: false + + merge_group: + +jobs: + tests-mac: + if: github.repository_owner == 'Qiskit' + name: macOS-arm64-tests-Python-${{ inputs.python-version }} + runs-on: macOS-14 + env: + QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y + PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip + QISKIT_TEST_CAPTURE_STREAMS: 1 + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@1.70 + if: inputs.python-version == '3.9' + - name: Set up Python ${{ inputs.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + architecture: arm64 + - name: Cache stestr + uses: actions/cache@v4 + with: + key: 'stestr | "${{ runner.os }}" | "${{ inputs.python-version }}" | "${{ github.run_id }}"' + restore-keys: | + stestr | "${{ runner.os }}" | "${{ inputs.python-version }}" + stestr | "${{ runner.os }}" + stestr + path: .stestr + - name: "Install dependencies" + run: | + set -e + python -m pip install --upgrade pip setuptools wheel virtualenv + virtualenv test-job + source test-job/bin/activate + python -m pip install -U pip setuptools wheel + python -m pip install -U \ + -c constraints.txt \ + -r requirements.txt \ + -r requirements-dev.txt \ + -e . + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. + pip check + - name: "Install optionals" + run: | + set -e + source test-job/bin/activate + pip install -r requirements-optional.txt -c constraints.txt + if: ${{ inputs.install-optionals }} + - name: "Run tests" + run: | + set -e + source test-job/bin/activate + python tools/report_numpy_state.py + export PYTHONHASHSEED=$(python -S -c "import random; print(random.randint(1, 4294967295))") + echo "PYTHONHASHSEED=$PYTHONHASHSEED" + stestr run + env: + QISKIT_PARALLEL: FALSE + RUST_BACKTRACE: 1 + - name: Copy and Publish images + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: copied-images + path: "**/*.png" + if-no-files-found: error diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml new file mode 100644 index 00000000000..48d1a33f06a --- /dev/null +++ b/.github/workflows/test-windows.yml @@ -0,0 +1,88 @@ +--- +name: Test Windows +on: + workflow_call: + inputs: + python-version: + description: > + Python version to currently test + type: string + required: true + + install-optionals: + description: > + Decides whether we install optyional dependencies + type: boolean + default: false + + merge_group: + +jobs: + test-windows: + if: github.repository_owner == 'Qiskit' + name: windows-latest-tests-Python-${{ inputs.python-version }} + runs-on: windows-latest + env: + QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y + QISKIT_TEST_CAPTURE_STREAMS: 1 + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@1.70 + if: inputs.python-version == '3.9' + - name: Set up Python ${{ inputs.python-version }} + id: python-outputs + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + - name: Cache stestr + uses: actions/cache@v4 + with: + key: 'stestr | "${{ runner.os }}" | "${{ inputs.python-version }}" | "${{ github.run_id }}"' + restore-keys: | + stestr | "${{ runner.os }}" | "${{ inputs.python-version }}" + stestr | "${{ runner.os }}" + stestr + path: .stestr + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel virtualenv + python -m virtualenv test-job + .\test-job\Scripts\activate + python -m pip install -U pip setuptools wheel + python -m pip install -U ` + -c constraints.txt ` + -r requirements.txt ` + -r requirements-dev.txt ` + -e . + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. + pip check + - name: Install Optional packages + run: | + .\test-job\Scripts\activate + pip install -c constraints.txt -r requirements-optional.txt + pip check + if: ${{ inputs.install-optionals }} + - name: Run Tests + run: | + chcp.com 65001 + .\test-job\Scripts\activate + python tools/report_numpy_state.py + $Env:PYTHONHASHSEED=$(python -S -c "import random; print(random.randint(1, 1024))") + echo "PYTHONHASHSEED=$PYTHONHASHSEED" + stestr run + env: + LANG: "C.UTF-8" + PYTHONIOENCODING: "utf-8:backslashreplace" + QISKIT_PARALLEL: FALSE + RUST_BACKTRACE: 1 + - name: Copy and Publish images + uses: actions/upload-artifact@v4 + if: ${{ failure() }} + with: + name: copied-images + path: "**/*.png" + if-no-files-found: error diff --git a/.github/workflows/tests-linux.yml b/.github/workflows/tests-linux.yml new file mode 100644 index 00000000000..23c3ff3976b --- /dev/null +++ b/.github/workflows/tests-linux.yml @@ -0,0 +1,171 @@ +--- +name: Test Linux +on: + workflow_call: + inputs: + python-version: + description: > + Python version to currently test + type: string + required: true + test-rust: + description: > + Decides whether we perform rust tests + type: boolean + required: true + test-images: + description: > + Decides whether we perform image tests + type: boolean + required: true + install-optionals: + description: > + Decides whether we install optyional dependencies + type: boolean + default: false + + install-from-sdist: + description: > + Decides whether we perform rust tests + type: boolean + default: false + + merge_group: + +jobs: + tests-linux: + if: github.repository_owner == 'Qiskit' + name: ubuntu-latest-tests-Python-${{ inputs.python-version }} + runs-on: ubuntu-latest + env: + QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y + PIP_CACHE_DIR: ${{ github.workspace }}/.pip + QISKIT_TEST_CAPTURE_STREAMS: 1 + HAVE_VISUAL_TESTS_RUN: false + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@1.70 + if: inputs.python-version == '3.9' + - name: Set up Python ${{ inputs.python-version }} + id: python-outputs + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + - name: Cache stestr + uses: actions/cache@v4 + with: + key: 'stestr | "${{ runner.os }}" | "${{ inputs.python-version }}" | "${{ github.run_id }}"' + restore-keys: | + stestr | "${{ runner.os }}" | "${{ inputs.python-version }}" + stestr | "${{ runner.os }}" + stestr + path: .stestr + - name: Run Rust Tests + if: inputs.test-rust == true + # We need to avoid linking our crates into full Python extension libraries during Rust-only + # testing because Rust/PyO3 can't handle finding a static CPython interpreter. + run: cargo test --no-default-features + env: + # On Linux we link against `libpython` dynamically, but it isn't written into the rpath + # of the test executable (I'm not 100% sure why ---Jake). It's easiest just to forcibly + # include the correct place in the `dlopen` search path. + LD_LIBRARY_PATH: "${{ steps.python-outputs.outputs.python-path }}/lib:$LD_LIBRARY_PATH" + - name: Prepare venv + run: | + set -e + python -m pip install --upgrade pip setuptools wheel virtualenv + virtualenv test-job + - name: Install Qiskit from sdist + if: ${{ inputs.install-from-sdist }} + run: | + set -e + # Use stable Rust, rather than MSRV, to spot-check that stable builds properly. + rustup override set stable + source test-job/bin/activate + python -m pip install -U pip + python -m pip install -U build + python -m build --sdist . + python -m pip install -U \ + -c constraints.txt \ + -r requirements.txt \ + -r requirements-dev.txt \ + dist/qiskit-*.tar.gz + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. + - name: Install Qiskit directly + if: ${{ !inputs.install-from-sdist }} + run: | + set -e + source test-job/bin/activate + python -m pip install -U \ + -c constraints.txt \ + -r requirements.txt \ + -r requirements-dev.txt \ + -e . + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. + - name: Install Optional packages + run: | + set -e + source test-job/bin/activate + python -m pip install -r requirements-optional.txt -c constraints.txt + python -m pip check + if: ${{ inputs.install-optionals }} + - name: Install optional non-Python dependencies + run: | + set -e + sudo apt-get update + sudo apt-get install -y graphviz + if: ${{ inputs.install-optionals }} + - name: Run Python tests + run: | + set -e + source test-job/bin/activate + python tools/report_numpy_state.py + mkdir -p /tmp/terra-tests + cp -r test /tmp/terra-tests/. + cp .stestr.conf /tmp/terra-tests/. + cp -r .stestr /tmp/terra-tests/. || : + pushd /tmp/terra-tests + export PYTHONHASHSEED=$(python -S -c "import random; print(random.randint(1, 4294967295))") + echo "PYTHONHASHSEED=$PYTHONHASHSEED" + stestr run + popd + env: + QISKIT_PARALLEL: FALSE + RUST_BACKTRACE: 1 + - name: Install Image dependencies + run: | + set -e + virtualenv image_tests + image_tests/bin/python -m pip install -U \ + -c constraints.txt \ + -r requirements.txt \ + -r requirements-dev.txt \ + -r requirements-optional.txt \ + -e . + sudo apt-get update + sudo apt-get install -y graphviz pandoc + image_tests/bin/pip check + if: ${{ inputs.test-images }} + - name: Run image tests + run: | + echo "##vso[task.setvariable variable=HAVE_VISUAL_TESTS_RUN;]true" + image_tests/bin/python -m unittest discover -v test/visual + if: ${{ inputs.test-images }} + env: + # Needed to suppress a warning in jupyter-core 5.x by eagerly migrating to + # a new internal interface that will be the default in jupyter-core 6.x. + # This variable should become redundant on release of jupyter-core 6. + JUPYTER_PLATFORM_DIRS: 1 + - name: Store image tests diff + uses: actions/upload-artifact@v4 + if: ${{ inputs.test-images && failure()}} + with: + name: image-test-failure-img-diffs + path: | + ./test/visual/mpl/graph/graph_results + if-no-files-found: error diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 63be7bd4716..00000000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,316 +0,0 @@ ---- -name: Tests -on: - push: - branches: [ main, 'stable/*' ] - pull_request: - branches: [ main, 'stable/*' ] - workflow_call: - inputs: - test-rust: - description: > - Decides whether we perform rust tests - type: boolean - - test-images: - description: > - Decides whether we perform image tests - type: boolean - - install-optionals: - description: > - Decides whether we install optyional dependencies - type: boolean - default: false - - install-from-dist: - description: > - Decides whether we perform rust tests - type: boolean - default: false - - merge_group: - -concurrency: - group: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }} - cancel-in-progress: true -jobs: - tests-linux: - if: github.repository_owner == 'Qiskit' - name: ubuntu-latest-tests-Python-${{ matrix.python-version }} - runs-on: ubuntu-latest - env: - QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y - PIP_CACHE_DIR: ${{ github.workspace }}/.pip - QISKIT_TEST_CAPTURE_STREAMS: 1 - HAVE_VISUAL_TESTS_RUN: false - strategy: - fail-fast: false - matrix: - python-version: ["3.9", "3.13"] - steps: - - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.70 - if: matrix.python-version == '3.9' - - name: Set up Python ${{ matrix.python-version }} - id: python-outputs - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Cache stestr - uses: actions/cache@v4 - with: - key: 'stestr | "${{ runner.os }}" | "${{ matrix.python-version }}" | "${{ github.run_id }}"' - restore-keys: | - stestr | "${{ runner.os }}" | "${{ matrix.python-version }}" - stestr | "${{ runner.os }}" - stestr - path: .stestr - - name: Run Rust Tests - if: inputs.test-rust == true - # We need to avoid linking our crates into full Python extension libraries during Rust-only - # testing because Rust/PyO3 can't handle finding a static CPython interpreter. - run: cargo test --no-default-features - env: - # On Linux we link against `libpython` dynamically, but it isn't written into the rpath - # of the test executable (I'm not 100% sure why ---Jake). It's easiest just to forcibly - # include the correct place in the `dlopen` search path. - LD_LIBRARY_PATH: '${{ steps.python-outputs.outputs.python-path }}/lib:$LD_LIBRARY_PATH' - - name: Prepare venv - run: | - set -e - python -m pip install --upgrade pip setuptools wheel virtualenv - virtualenv test-job - - name: Install Qiskit from sdist - if: ${{ inputs.install-from-dist }} - run: | - set -e - # Use stable Rust, rather than MSRV, to spot-check that stable builds properly. - rustup override set stable - source test-job/bin/activate - python -m pip install -U pip - python -m pip install -U build - python -m build --sdist . - python -m pip install -U \ - -c constraints.txt \ - -r requirements.txt \ - -r requirements-dev.txt \ - dist/qiskit-*.tar.gz - # Build and install both qiskit and qiskit-terra so that any optionals - # depending on `qiskit` will resolve correctly. - - name: Install Qiskit directly - if: ${{ !inputs.install-from-dist }} - run: | - set -e - source test-job/bin/activate - python -m pip install -U \ - -c constraints.txt \ - -r requirements.txt \ - -r requirements-dev.txt \ - -e . - # Build and install both qiskit and qiskit-terra so that any optionals - # depending on `qiskit` will resolve correctly. - - name: Install Optional packages - run: | - set -e - source test-job/bin/activate - python -m pip install -r requirements-optional.txt -c constraints.txt - python -m pip check - if: ${{ inputs.install-optionals }} - - name: Install optional non-Python dependencies - run: | - set -e - sudo apt-get update - sudo apt-get install -y graphviz - if: ${{ inputs.install-optionals }} - - name: Run Python tests - run: | - set -e - source test-job/bin/activate - python tools/report_numpy_state.py - mkdir -p /tmp/terra-tests - cp -r test /tmp/terra-tests/. - cp .stestr.conf /tmp/terra-tests/. - cp -r .stestr /tmp/terra-tests/. || : - pushd /tmp/terra-tests - export PYTHONHASHSEED=$(python -S -c "import random; print(random.randint(1, 4294967295))") - echo "PYTHONHASHSEED=$PYTHONHASHSEED" - stestr run - popd - env: - QISKIT_PARALLEL: FALSE - RUST_BACKTRACE: 1 - - name: Install Image dependencies - run: | - set -e - virtualenv image_tests - image_tests/bin/python -m pip install -U \ - -c constraints.txt \ - -r requirements.txt \ - -r requirements-dev.txt \ - -r requirements-optional.txt \ - -e . - sudo apt-get update - sudo apt-get install -y graphviz pandoc - image_tests/bin/pip check - if: ${{ inputs.test-images }} - - name: Run image tests - run: | - echo "##vso[task.setvariable variable=HAVE_VISUAL_TESTS_RUN;]true" - image_tests/bin/python -m unittest discover -v test/visual - if: ${{ inputs.test-images }} - env: - # Needed to suppress a warning in jupyter-core 5.x by eagerly migrating to - # a new internal interface that will be the default in jupyter-core 6.x. - # This variable should become redundant on release of jupyter-core 6. - JUPYTER_PLATFORM_DIRS: 1 - - name: Store image tests diff - uses: actions/upload-artifact@v4 - if: ${{ inputs.test-images && failure()}} - with: - name: image-test-failure-img-diffs - path: | - ./test/visual/mpl/graph/graph_results - if-no-files-found: error - - tests-mac: - if: github.repository_owner == 'Qiskit' - name: macOS-arm64-tests-Python-${{ matrix.python-version }} - runs-on: macOS-14 - env: - QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y - PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip - QISKIT_TEST_CAPTURE_STREAMS: 1 - strategy: - fail-fast: false - matrix: - python-version: ["3.9", "3.13"] - steps: - - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.70 - if: matrix.python-version == '3.9' - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - architecture: arm64 - - name: Cache stestr - uses: actions/cache@v4 - with: - key: 'stestr | "${{ runner.os }}" | "${{ matrix.python-version }}" | "${{ github.run_id }}"' - restore-keys: | - stestr | "${{ runner.os }}" | "${{ matrix.python-version }}" - stestr | "${{ runner.os }}" - stestr - path: .stestr - - name: 'Install dependencies' - run: | - set -e - python -m pip install --upgrade pip setuptools wheel virtualenv - virtualenv test-job - source test-job/bin/activate - python -m pip install -U pip setuptools wheel - python -m pip install -U \ - -c constraints.txt \ - -r requirements.txt \ - -r requirements-dev.txt \ - -e . - # Build and install both qiskit and qiskit-terra so that any optionals - # depending on `qiskit` will resolve correctly. - pip check - - name: 'Install optionals' - run: | - set -e - source test-job/bin/activate - pip install -r requirements-optional.txt -c constraints.txt - if: ${{ inputs.install-optionals }} - - name: 'Run tests' - run: | - set -e - source test-job/bin/activate - python tools/report_numpy_state.py - export PYTHONHASHSEED=$(python -S -c "import random; print(random.randint(1, 4294967295))") - echo "PYTHONHASHSEED=$PYTHONHASHSEED" - stestr run - env: - QISKIT_PARALLEL: FALSE - RUST_BACKTRACE: 1 - - name: Copy and Publish images - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: copied-images - path: '**/*.png' - if-no-files-found: error - test-windows: - if: github.repository_owner == 'Qiskit' - name: windows-latest-tests-Python-${{ matrix.python-version }} - runs-on: windows-latest - env: - QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y - QISKIT_TEST_CAPTURE_STREAMS: 1 - strategy: - fail-fast: false - matrix: - python-version: ["3.9", "3.13"] - steps: - - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.70 - if: matrix.python-version == '3.9' - - name: Set up Python ${{ matrix.python-version }} - id: python-outputs - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Cache stestr - uses: actions/cache@v4 - with: - key: 'stestr | "${{ runner.os }}" | "${{ matrix.python-version }}" | "${{ github.run_id }}"' - restore-keys: | - stestr | "${{ runner.os }}" | "${{ matrix.python-version }}" - stestr | "${{ runner.os }}" - stestr - path: .stestr - - name: Install dependencies - run: | - python -m pip install --upgrade pip setuptools wheel virtualenv - python -m virtualenv test-job - .\test-job\Scripts\activate - python -m pip install -U pip setuptools wheel - python -m pip install -U ` - -c constraints.txt ` - -r requirements.txt ` - -r requirements-dev.txt ` - -e . - # Build and install both qiskit and qiskit-terra so that any optionals - # depending on `qiskit` will resolve correctly. - pip check - - name: Install Optional packages - run: | - .\test-job\Scripts\activate - pip install -c constraints.txt -r requirements-optional.txt - pip check - if: ${{ inputs.install-optionals }} - - name: Run Tests - run: | - chcp.com 65001 - .\test-job\Scripts\activate - python tools/report_numpy_state.py - $Env:PYTHONHASHSEED=$(python -S -c "import random; print(random.randint(1, 1024))") - echo "PYTHONHASHSEED=$PYTHONHASHSEED" - stestr run - env: - LANG: 'C.UTF-8' - PYTHONIOENCODING: 'utf-8:backslashreplace' - QISKIT_PARALLEL: FALSE - RUST_BACKTRACE: 1 - - name: Copy and Publish images - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: copied-images - path: '**/*.png' - if-no-files-found: error