diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b1181f..b15d0da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,10 @@ on: workflow_call: workflow_dispatch: +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: false + permissions: contents: read @@ -80,6 +84,7 @@ jobs: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: marcelotduarte/python-msilib + persist-credentials: false - name: Set up Python uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 @@ -90,14 +95,17 @@ jobs: with: activate-environment: true cache-dependency-glob: "pyproject.toml" - enable-cache: true + enable-cache: false - name: Bump new dev version if: github.event_name != 'release' run: ./ci/bump-version.sh build-dev - name: Build sdist and wheels - run: ./ci/build-wheel.sh "${{ matrix.tag }}" + run: ./ci/build-wheel.sh "${MATRIX_TAG}" + env: + BUILD_SDIST: true + MATRIX_TAG: ${{ matrix.tag }} - name: Upload the artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -114,7 +122,7 @@ jobs: name: pypi url: https://pypi.org/p/python-msilib permissions: - id-token: write + id-token: write # MANDATORY: Required for OIDC Trusted Publishing runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67cd8a0..70aa3b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,8 +11,15 @@ concurrency: group: ci-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: pre_commit: + name: Pre commit + permissions: + contents: write # Required for pre-commit-ci lite + id-token: write # Required for pre-commit-ci lite runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -21,6 +28,8 @@ jobs: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - uses: j178/prek-action@e98a699c41eb69ab013a45817a0406469a748f8d # v2.0.5 with: @@ -30,6 +39,7 @@ jobs: if: always() type_check: + name: Type check runs-on: ${{ matrix.os }}-${{ matrix.os_version }} strategy: fail-fast: false @@ -49,6 +59,8 @@ jobs: egress-policy: audit - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: @@ -57,10 +69,10 @@ jobs: enable-cache: true - name: Install dependencies - run: ci/install-tools.sh --dev --tests + run: ci/install-tools.sh --tests - name: Type check - run: ty check + run: uv check --quiet --preview-features=check-command --no-sync build_wheel: name: Build wheels @@ -68,6 +80,9 @@ jobs: needs: - pre_commit - type_check + permissions: + contents: read + id-token: write # Required for nested job 'publish' testpypi: name: Publish package to TestPyPI @@ -80,7 +95,7 @@ jobs: name: ${{ github.event_name }} url: https://test.pypi.org/p/python-msilib permissions: - id-token: write + id-token: write # MANDATORY: Required for OIDC Trusted Publishing runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -104,6 +119,7 @@ jobs: verbose: true tests: + name: Run tests needs: - build_wheel runs-on: ${{ matrix.os }}-${{ matrix.os-version }} @@ -133,6 +149,7 @@ jobs: sparse-checkout: | pyproject.toml tests + persist-credentials: false - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 @@ -152,9 +169,11 @@ jobs: pattern: python-msilib-wheel-${{ matrix.os }}* path: wheelhouse - - name: Install dependencies + - name: Install dependencies to test + run: uv pip install -r pyproject.toml --upgrade --group tests + + - name: Install python-msilib from wheelhouse run: | - uv pip install -r pyproject.toml --upgrade --group tests uv pip install python-msilib -f wheelhouse \ --no-build --no-deps --no-index --prerelease=allow --reinstall @@ -169,12 +188,12 @@ jobs: include-hidden-files: true coverage: + name: Coverage needs: - tests permissions: - actions: read - contents: write - pull-requests: write + contents: write # Required for editing existing comments. + pull-requests: write # Required for publishing new comments in pull requests. runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -196,7 +215,7 @@ jobs: id: coverage_comment uses: py-cov-action/python-coverage-comment-action@5d8df5979747514c914e1c5a12335a7cf9a2745f # v4.1 with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} MERGE_COVERAGE_FILES: true - name: Store Pull Request comment to be posted diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7c1d442..7abd1fb 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,6 +20,10 @@ on: schedule: - cron: "0 0 * * 1" +concurrency: + group: sec-${{ github.ref }} + cancel-in-progress: false + permissions: contents: read @@ -28,9 +32,9 @@ jobs: name: Analyze runs-on: ubuntu-latest permissions: - actions: read + actions: read # MANDATORY contents: read - security-events: write + security-events: write # Needed to upload the results to code-scanning dashboard. strategy: fail-fast: false @@ -47,6 +51,8 @@ jobs: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 11deb5e..29f0bd0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,11 +1,19 @@ name: Post coverage comment -on: +# We're using workflow_run to post a coverage comment on external PRs. This is +# safe because we don't checkout the external code or interact with the +# external code in any way but extracting an artifact containing the comment to +# post, and post it. +on: # zizmor: ignore[dangerous-triggers] workflow_run: workflows: ["CI"] types: - completed +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: false + permissions: contents: read @@ -15,16 +23,13 @@ jobs: runs-on: ubuntu-latest if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' permissions: - # Gives the action the necessary permissions for publishing new - # comments in pull requests. - pull-requests: write - # Gives the action the necessary permissions for editing existing - # comments (to avoid publishing multiple comments in the same PR) - contents: write + pull-requests: write # Required for publishing new comments in pull requests. + # Avoid publishing multiple comments in the same PR. + contents: write # Required for editing existing comments. # Gives the action the necessary permissions for looking up the # workflow that launched this workflow, and download the related # artifact that contains the comment to be published - actions: read + actions: read # Required steps: # DO NOT run actions/checkout here, for security reasons # For details, refer to https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ @@ -36,7 +41,7 @@ jobs: - name: Post comment uses: py-cov-action/python-coverage-comment-action@5d8df5979747514c914e1c5a12335a7cf9a2745f # v4.1 with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} MINIMUM_GREEN: 80 MINIMUM_ORANGE: 50 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 454be8c..f011000 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -9,11 +9,16 @@ name: Dependency Review on: [pull_request] +concurrency: + group: sec-${{ github.ref }} + cancel-in-progress: false + permissions: contents: read jobs: dependency-review: + name: Dependency Review runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -23,5 +28,8 @@ jobs: - name: Checkout Repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Dependency Review uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 743b852..a45ea4b 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -14,25 +14,24 @@ on: push: branches: ["main"] -# Declare default permissions as read only. -permissions: read-all +concurrency: + group: sec-${{ github.ref }} + cancel-in-progress: false + +permissions: {} jobs: analysis: name: Scorecard analysis runs-on: ubuntu-latest permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write + security-events: write # Needed to upload the results to code-scanning dashboard. + id-token: write # Needed to publish results and get a badge (see publish_results below). contents: read - actions: read - # To allow GraphQL ListCommits to work - issues: read - pull-requests: read - # To detect SAST tools - checks: read + actions: read # MANDATORY + issues: read # To allow GraphQL ListCommits to work + pull-requests: read # MANDATORY + checks: read # To detect SAST tools steps: - name: Harden the runner (Audit all outbound calls) diff --git a/.gitignore b/.gitignore index 78ccdff..e01039a 100644 --- a/.gitignore +++ b/.gitignore @@ -87,24 +87,24 @@ ipython_config.py # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. -#Pipfile.lock +Pipfile.lock # UV # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. -#uv.lock +uv.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock +poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock +pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/latest/usage/project/#working-with-version-control diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 03d2d9d..be50d90 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,17 +23,17 @@ repos: hooks: - id: validate-pyproject additional_dependencies: - - validate-pyproject-schema-store[all]>=2026.07.06 + - validate-pyproject-schema-store[all]==2026.07.17 - repo: https://github.com/astral-sh/ruff-pre-commit - rev: c59bba8fb259db0fec2bbb77ad8ba51ea7341b56 # frozen: v0.15.20 + rev: 2700fd5671c633760d912769c041bfcde2b9a01b # frozen: v0.15.22 hooks: - id: ruff-check args: ["--fix", "--show-fixes", "--unsafe-fixes"] - id: ruff-format - repo: https://github.com/codespell-project/codespell - rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2 + rev: 57b21406f092110c18776e39b0bda50d37c945c8 # frozen: v2.4.3 hooks: - id: codespell additional_dependencies: @@ -41,14 +41,14 @@ repos: args: ["--ignore-words-list=lief,LIEF", "-w", "--skip=*.rtf"] - repo: https://github.com/pre-commit/mirrors-clang-format - rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5 + rev: f4d7745e17a28aad7eed2f4874ca8d1568c11c4c # frozen: v22.1.8 hooks: - id: clang-format exclude: (?x)^(src/msilib/include/pythoncapi_compat.h)$ types_or: [c, c++] - repo: https://github.com/rbubley/mirrors-prettier - rev: 39e2973981e6d2f9b6c543b0086a2d2393abdc89 # frozen: v3.9.4 + rev: 0ee178619d696787ca73d210cc191d720868c631 # frozen: v3.9.6 hooks: - id: prettier types_or: [yaml, markdown, html, css, scss, javascript, json] @@ -59,11 +59,17 @@ repos: - id: shellcheck - repo: https://github.com/astral-sh/ty-pre-commit - rev: a6000401e1ca0cf427a8398b42fd636a3a0dcb54 # frozen: v0.0.56 + rev: 1395c6934313bf0c086f3034f10c10747e7efc10 # frozen: v0.0.61 hooks: - id: ty args: [--no-sync] + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: 64a97fb7fa63188393d3215c6e312f5f9c6d0f78 # frozen: v1.27.0 + hooks: + - id: zizmor + args: ["--fix=all", "--no-progress", "-p"] + - repo: local hooks: - id: pythoncapi diff --git a/Makefile b/Makefile index 9f6097f..3396224 100644 --- a/Makefile +++ b/Makefile @@ -38,9 +38,6 @@ update: install prek update --freeze $(MAKE) prek -.PHONY: upgrade -upgrade: update - .PHONY: wheel wheel: ./ci/build-wheel.sh --install diff --git a/ci/build-wheel.sh b/ci/build-wheel.sh index 27c79e5..2c081a7 100755 --- a/ci/build-wheel.sh +++ b/ci/build-wheel.sh @@ -26,7 +26,6 @@ PY_ABI_THREAD=$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('ab IS_CONDA=$([ -n "$CONDA_EXE" ] && echo "1") IS_MINGW=$([[ $PY_PLATFORM == mingw* ]] && echo "1") -IS_WINDOWS=$([[ $PY_PLATFORM == win* ]] && echo "1") PYTHON_TAG=cp$PY_VERSION_NODOT if [ "$IS_CONDA" == "1" ]; then @@ -106,7 +105,7 @@ _get_dirty () { _build_sdist () { if [ "$IS_CONDA" == "1" ] || [ "$IS_MINGW" == "1" ]; then $PYTHON -m build -n -x --sdist -o wheelhouse - else + elif [ "$PY_PLATFORM" == "linux-x86_64" ] || [ "$BUILD_SDIST" == "true" ]; then uv build -p "$PY_VERSION$PY_ABI_THREAD" --sdist -o wheelhouse fi } @@ -122,7 +121,7 @@ _build_wheel () { elif [[ $PY_PLATFORM == macos* ]] && [[ $args == *--only* ]]; then uv build -p "$PY_VERSION$PY_ABI_THREAD" --wheel -o wheelhouse else - if [ "$CI" == "true" ] && [ "$IS_WINDOWS" == "1" ]; then + if [ "$CI" == "true" ] && [[ $PY_PLATFORM == win* ]]; then export UV_LINK_MODE=copy fi "$INSTALL_DIR/cibuildwheel" "$args" diff --git a/ci/install-tools.sh b/ci/install-tools.sh index 834177a..be615fd 100755 --- a/ci/install-tools.sh +++ b/ci/install-tools.sh @@ -10,23 +10,29 @@ if [ -n "$1" ] && [ "$1" == "--help" ]; then exit 1 fi -# Detect environment +# Detect environment. For mingw and conda environments, python is not required +# to be installed, but will be installed by this script. IS_CONDA="0" IS_MINGW="0" IS_UV="0" IS_WINDOWS="0" if [ -n "$CONDA_EXE" ]; then IS_CONDA="1" -elif [ -n "$MINGW_PACKAGE_PREFIX" ]; then - IS_MINGW="1" elif which python &>/dev/null; then PY_PLATFORM=$(python -c "import sysconfig; print(sysconfig.get_platform(), end='')") IS_WINDOWS=$([[ $PY_PLATFORM == win* ]] && echo "1") - IS_UV="1" + IS_MINGW=$([[ $PY_PLATFORM == mingw* ]] && echo "1") + if ! [ "$IS_MINGW" == "1" ]; then + IS_UV="1" + fi python ci/requirements.py else - echo "error: Python is required." - exit 1 + if [ -n "$MINGW_PACKAGE_PREFIX" ]; then + IS_MINGW="1" + else + echo "error: Python is required." + exit 1 + fi fi # Install/update dev tools (including uv if required) @@ -131,46 +137,13 @@ else env UV_INSTALL_DIR="$INSTALL_DIR" sh fi - # Lief is not available for Python free-threaded - PY_ABI_THREAD=$(python -c "import sysconfig; print(sysconfig.get_config_var('abi_thread') or '', end='')") - if [ "$IS_WINDOWS" == "1" ] && [ "$PY_ABI_THREAD" == "t" ]; then - # Packages to install - pkgs=() - - # Dependencies of the project - if [ -f requirements.txt ]; then - while read -r line; do - if [[ $line != *sys_platform* ]] || \ - [[ $line == *sys_platform*==*win32* ]]; then - name=$(echo "$line" | awk -F '[><=]+' '{ print $1 }') - if [ "$name" == "lief" ]; then continue; fi - if [ "$name" == "tomli" ]; then continue; fi - name_and_version=$(echo "$line" | awk '{ print $1 }') - pkgs+=("$name_and_version") - fi - done < requirements.txt - fi - - # pytest and dependencies - if [ "$INSTALL_TESTS" == "1" ] && [ -f tests/requirements.txt ]; then - while read -r line; do - name=$(echo "$line" | awk -F '[><=]+' '{ print $1 }') - pkgs+=("$name") - done < tests/requirements.txt - fi - - echo "Install packages" - uv pip install --upgrade "${pkgs[@]}" - + # Dependencies of the project + echo "Install packages" + if [ "$INSTALL_TESTS" == "1" ]; then + # including pytest and dependencies + uv pip install --upgrade -r pyproject.toml --group tests else - # Dependencies of the project - echo "Install packages" - if [ "$INSTALL_TESTS" == "1" ]; then - # including pytest and dependencies - uv pip install --upgrade -r pyproject.toml --group tests - else - uv pip install --upgrade -r pyproject.toml - fi + uv pip install --upgrade -r pyproject.toml fi fi diff --git a/pyproject.toml b/pyproject.toml index ebc7cfd..43497e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "setuptools~=82.0", + "setuptools~=83.0", ] build-backend = "setuptools.build_meta" @@ -47,12 +47,12 @@ Documentation = "https://docs.python.org/3.12/library/msilib.html" [dependency-groups] dev = [ "cibuildwheel>=4.1.0", - "prek>=0.4.8,<0.5.0", - "ty>=0.0.56", + "prek>=0.4.10,<0.5.0", + "ty>=0.0.61", ] tests = [ "coverage>=7.13.0", - "pytest>=9.0.3", + "pytest>=9.0.3,<9.2.0", ] [tool.setuptools] diff --git a/requirements-dev.txt b/requirements-dev.txt index f00b89f..6413039 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,3 @@ cibuildwheel>=4.1.0 -prek>=0.4.8,<0.5.0 -ty>=0.0.56 +prek>=0.4.10,<0.5.0 +ty>=0.0.61 diff --git a/tests/requirements.txt b/tests/requirements.txt index a1d7a59..db83096 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,2 +1,2 @@ coverage>=7.13.0 -pytest>=9.0.3 +pytest>=9.0.3,<9.2.0