diff --git a/.github/workflows/_pytest.yaml b/.github/workflows/_pytest.yaml new file mode 100644 index 00000000..5ccfdcad --- /dev/null +++ b/.github/workflows/_pytest.yaml @@ -0,0 +1,105 @@ +--- +# Reusable test runner. To be called from other workflows. +name: pytest + +on: + workflow_call: + inputs: + test-files: + description: "Space-separated pytest paths or options/arguments (empty = whole suite)" + type: string + default: "" + python-versions: + description: "JSON array of Python versions for the matrix" + type: string + default: '["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]' + install-native-toolchains: + description: "Install compilers/tools to build & run generated bindings" + type: boolean + default: true + install-go: + description: >- + Install Go via brew on macOS. Default false since Go is + preinstalled on Linux runners and only the golang job needs it. + type: boolean + default: false + install-rust-clippy: + description: "Install the clippy component (needed by tests/test_rust.py's static analysis test)" + type: boolean + default: false + install-go-staticcheck: + description: "Install staticcheck (needed by tests/test_golang.py's static analysis test)" + type: boolean + default: false + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + python-version: ${{ fromJSON(inputs.python-versions) }} + include: + - os: macos-latest + python-version: "3.10" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: ${{ matrix.python-version }} + cache: pip + # jsonld-cli is used by the tests (incl. core), so always install it. + - name: Setup Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24 + cache: npm + cache-dependency-path: scripts/package-lock.json + - name: Install Python build tooling + run: | + python -m pip install --upgrade pip + pip install build + - name: Install jsonld-cli + run: | + npm install -g jsonld-cli + - name: Install Node test/build scripts + run: | + npm --prefix scripts ci + - name: Install Linux packages + if: ${{ inputs.install-native-toolchains && runner.os == 'Linux' }} + run: | + sudo apt update + sudo apt install -y build-essential ccache cppcheck doxygen graphviz + - name: Install macOS packages + if: ${{ inputs.install-native-toolchains && runner.os == 'macOS' }} + run: | + brew update + brew install ccache cppcheck doxygen gcc graphviz + - name: Install Go (macOS) + if: ${{ inputs.install-go && runner.os == 'macOS' }} + run: | + brew install go + - name: Install Rust static analysis tools + if: ${{ inputs.install-rust-clippy }} + run: | + rustup component add clippy + - name: Install Go static analysis tools + if: ${{ inputs.install-go-staticcheck }} + run: | + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + go install honnef.co/go/tools/cmd/staticcheck@latest + - name: Configure ccache + if: ${{ inputs.install-native-toolchains }} + uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 # v1.2.23 + with: + key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.python-version }} + - name: Build package + run: | + python -m build + - name: Install package + run: | + pip install -e .[dev] + - name: Run tests + run: | + pytest -v ${{ inputs.test-files }} diff --git a/.github/workflows/coverage-generate.yaml b/.github/workflows/coverage-generate.yaml new file mode 100644 index 00000000..c371a22e --- /dev/null +++ b/.github/workflows/coverage-generate.yaml @@ -0,0 +1,85 @@ +--- +name: Generate coverage + +on: + pull_request: + # Skip docs and edits to other workflows; later patterns override earlier. + paths: &trigger-paths + - "**" + - "!**/*.md" + - "!LICENSE" + - "!.github/ISSUE_TEMPLATE/**" + - "!.github/workflows/**" + - ".github/workflows/coverage-generate.yaml" + push: + branches: + - "main" + paths: *trigger-paths + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + coverage: + name: Generate coverage report + runs-on: ubuntu-latest + permissions: + # Gives the action the necessary permissions for publishing new + # comments in pull requests. + pull-requests: write + # Gives the action the necessary permissions for pushing data to the + # python-coverage-comment-action branch, and for editing existing + # comments (to avoid publishing multiple comments in the same PR) + contents: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.11" + cache: pip + - name: Setup Node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24 + cache: npm + cache-dependency-path: scripts/package-lock.json + - name: Install dependencies + run: | + sudo apt install -y build-essential ccache cppcheck doxygen graphviz + npm install -g jsonld-cli + npm --prefix scripts ci + - name: Install Go static analysis tools + run: | + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + go install honnef.co/go/tools/cmd/staticcheck@latest + - name: Install Rust static analysis tools + run: | + rustup component add clippy + - name: Configure ccache + uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 # v1.2.23 + with: + key: ${{ github.job }} + - name: Install package + run: | + pip install -e .[dev] + - name: Generate coverage report + run: | + pytest -v + - name: Coverage comment + id: coverage_comment + uses: py-cov-action/python-coverage-comment-action@a05be3d2e8a6272d3ef5fb2840ab20368bb2eb71 # v4.3 + with: + GITHUB_TOKEN: ${{ github.token }} + - name: Store Pull Request comment to be posted + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' + with: + # If you use a different name, update COMMENT_ARTIFACT_NAME accordingly + name: python-coverage-comment-action + # If you use a different name, update COMMENT_FILENAME accordingly + path: python-coverage-comment-action.txt diff --git a/.github/workflows/coverage-report.yaml b/.github/workflows/coverage-report.yaml index c4817bd5..ffe9d621 100644 --- a/.github/workflows/coverage-report.yaml +++ b/.github/workflows/coverage-report.yaml @@ -3,7 +3,7 @@ name: Post coverage comment on: workflow_run: - workflows: ["Test shacl2code"] + workflows: ["Generate coverage"] types: - completed @@ -30,7 +30,7 @@ jobs: # DO NOT run actions/checkout here, for security reasons # For details, refer to https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ - name: Post comment - uses: py-cov-action/python-coverage-comment-action@2b84cfb222691291ba358a586c13606f0a9c172c # v3 + uses: py-cov-action/python-coverage-comment-action@a05be3d2e8a6272d3ef5fb2840ab20368bb2eb71 # v4.3 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8790adbd..31e447ef 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,6 +10,7 @@ on: - "!.github/ISSUE_TEMPLATE/**" - "!.github/workflows/**" - ".github/workflows/test.yaml" + - ".github/workflows/_pytest.yaml" pull_request: paths: *trigger-paths workflow_call: @@ -22,88 +23,127 @@ permissions: contents: read jobs: - build: - runs-on: ${{ matrix.os }} - permissions: - contents: write # Needed by the coverage-comment step below - pull-requests: write # Needed by the coverage-comment step below - strategy: - matrix: - os: [ubuntu-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] - include: - - os: macos-latest - python-version: "3.10" + # Check which language bindings are affected by the change + changes: + runs-on: ubuntu-latest + outputs: + cpp: ${{ steps.filter.outputs.cpp }} + golang: ${{ steps.filter.outputs.golang }} + jsonschema: ${{ steps.filter.outputs.jsonschema }} + python: ${{ steps.filter.outputs.python }} + rust: ${{ steps.filter.outputs.rust }} steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: ${{ matrix.python-version }} - cache: pip - - name: Setup Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: 24 - cache: npm - cache-dependency-path: scripts/package-lock.json - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - npm install -g jsonld-cli - npm --prefix scripts ci - - name: Install Linux packages - if: runner.os == 'Linux' - run: | - sudo apt update - sudo apt install -y build-essential cppcheck doxygen graphviz ccache - - name: Install macOS packages - if: runner.os == 'macOS' - run: | - brew update - brew install cppcheck doxygen gcc go graphviz ccache - - name: Install Go static analysis tools - run: | - echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" - go install honnef.co/go/tools/cmd/staticcheck@latest - - name: Install Rust static analysis tools - run: | - rustup component add clippy - - name: Configure ccache - uses: hendrikmuhs/ccache-action@v1.2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 + id: filter with: - key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.python-version }} - - name: Build package - run: | - python -m build - - name: Install package - run: | - pip install -e .[dev] - - name: Run tests - run: | - pytest -v - - name: Coverage comment - id: coverage_comment - if: >- - matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' && - (github.event_name == 'pull_request' || - (github.event_name == 'push' && github.ref == 'refs/heads/main')) - uses: py-cov-action/python-coverage-comment-action@2b84cfb222691291ba358a586c13606f0a9c172c # v3 - with: - GITHUB_TOKEN: ${{ github.token }} - - name: Store Pull Request comment to be posted - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 - if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' - with: - name: python-coverage-comment-action - path: python-coverage-comment-action.txt + filters: | + common: &common + - '.github/workflows/_pytest.yaml' + - '.github/workflows/test.yaml' + - 'src/shacl2code/*.py' + - 'src/shacl2code/lang/__init__.py' + - 'src/shacl2code/lang/common.py' + - 'src/shacl2code/lang/jinja.py' + - 'src/shacl2code/lang/lang.py' + - 'pyproject.toml' + - 'tests/conftest.py' + - 'tests/data/**' + - 'testfixtures/**' + cpp: + - *common + - 'src/shacl2code/lang/cpp.py' + - 'src/shacl2code/lang/sources/cpp/**' + - 'src/shacl2code/lang/templates/cpp/**' + - 'tests/test_cpp.py' + golang: + - *common + - 'src/shacl2code/lang/golang.py' + - 'src/shacl2code/lang/sources/golang/**' + - 'src/shacl2code/lang/templates/golang/**' + - 'tests/test_golang.py' + jsonschema: + - *common + - 'scripts/**' + - 'src/shacl2code/lang/jsonschema.py' + - 'src/shacl2code/lang/sources/jsonschema/**' + - 'src/shacl2code/lang/templates/jsonschema.j2' + - 'tests/test_jsonschema.py' + python: + - *common + - 'src/shacl2code/lang/python.py' + - 'src/shacl2code/lang/sources/python/**' + - 'src/shacl2code/lang/templates/python/**' + - 'tests/test_python.py' + rust: + - *common + - 'src/shacl2code/lang/rust.py' + - 'src/shacl2code/lang/templates/rust/**' + - 'tests/test_rust.py' + + # Always run core (ignores language-specific test suites) + core: + uses: ./.github/workflows/_pytest.yaml + with: + install-native-toolchains: false + test-files: >- + tests + --ignore=tests/test_cpp.py + --ignore=tests/test_golang.py + --ignore=tests/test_jsonschema.py + --ignore=tests/test_python.py + --ignore=tests/test_rust.py + + cpp: + needs: changes + if: needs.changes.outputs.cpp == 'true' + uses: ./.github/workflows/_pytest.yaml + with: + install-native-toolchains: true + test-files: tests/test_cpp.py + + golang: + needs: changes + if: needs.changes.outputs.golang == 'true' + uses: ./.github/workflows/_pytest.yaml + with: + install-native-toolchains: false + install-go: true + install-go-staticcheck: true + test-files: tests/test_golang.py + + jsonschema: + needs: changes + if: needs.changes.outputs.jsonschema == 'true' + uses: ./.github/workflows/_pytest.yaml + with: + install-native-toolchains: false + test-files: tests/test_jsonschema.py + + python: + needs: changes + if: needs.changes.outputs.python == 'true' + uses: ./.github/workflows/_pytest.yaml + with: + install-native-toolchains: false + test-files: tests/test_python.py + + rust: + needs: changes + if: needs.changes.outputs.rust == 'true' + uses: ./.github/workflows/_pytest.yaml + with: + # `rust`/`cargo` preinstalled on runners + install-native-toolchains: false + install-rust-clippy: true + test-files: tests/test_rust.py + lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: # Black in this Python version must support all target-versions listed in pyproject.toml python-version: "3.14" diff --git a/README.md b/README.md index de4833ea..d3b0b3ba 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ shacl2code generate \ --input model-draft.ttl \ --context-url context-draft.jsonld https://example.com/context.jsonld \ jsonschema \ - --output schema.json + --output schema.json ``` ## Developing