├── .github └── workflows │ ├── release-on-milestone-closed-triggering-release-event.yml │ └── test.yml ├── .gitignore ├── Dockerfile ├── README.md ├── composer.json ├── psalm-html-output.xsl ├── renovate.json └── test ├── generate-test-report.sh ├── psalm-report.html └── psalm-report.xml /.github/workflows/release-on-milestone-closed-triggering-release-event.yml: -------------------------------------------------------------------------------- 1 | # Alternate workflow example. 2 | # This one is identical to the one in release-on-milestone.yml, with one change: 3 | # the Release step uses the ORGANIZATION_ADMIN_TOKEN instead, to allow it to 4 | # trigger a release workflow event. This is useful if you have other actions 5 | # that intercept that event. 6 | 7 | name: "Automatic Releases" 8 | 9 | on: 10 | milestone: 11 | types: 12 | - "closed" 13 | 14 | jobs: 15 | release: 16 | name: "GIT tag, release & create merge-up PR" 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: "Checkout" 21 | uses: "actions/checkout@v3" 22 | 23 | - name: "Release" 24 | uses: "laminas/automatic-releases@v1" 25 | with: 26 | command-name: "laminas:automatic-releases:release" 27 | env: 28 | "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} 29 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} 30 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} 31 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} 32 | 33 | - name: "Create Merge-Up Pull Request" 34 | uses: "laminas/automatic-releases@v1" 35 | with: 36 | command-name: "laminas:automatic-releases:create-merge-up-pull-request" 37 | env: 38 | "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} 39 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} 40 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} 41 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} 42 | 43 | - name: "Create and/or Switch to new Release Branch" 44 | uses: "laminas/automatic-releases@v1" 45 | with: 46 | command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor" 47 | env: 48 | "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} 49 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} 50 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} 51 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} 52 | 53 | - name: "Create new milestones" 54 | uses: "laminas/automatic-releases@v1" 55 | with: 56 | command-name: "laminas:automatic-releases:create-milestones" 57 | env: 58 | "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} 59 | "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} 60 | "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} 61 | "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} 62 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | test: 9 | name: "Test Output Generation" 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: "Checkout" 15 | uses: "actions/checkout@v3" 16 | 17 | - name: "Generate test report" 18 | run: test/generate-test-report.sh 19 | 20 | - name: "Verify generated HTML matches expectations" 21 | run: diff -s test/psalm-report.html test/psalm-report-generated.html 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | test/psalm-report-generated.html 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN set -xeu && \ 4 | IFS=$'\n\t' && \ 5 | export DEBIAN_FRONTEND=noninteractive && \ 6 | export LANG="C.UTF-8" && \ 7 | apt update && \ 8 | apt upgrade -y && \ 9 | apt install -y xsltproc && \ 10 | apt autoremove -y && apt clean 11 | 12 | ADD psalm-html-output.xsl /psalm-html-output.xsl 13 | 14 | ENTRYPOINT ["xsltproc", "/psalm-html-output.xsl"] 15 | CMD ["-"] 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Psalm HTML Output 2 | 3 | Takes the XML output from [Psalm](https://psalm.dev/) and renders it as HTML. 4 | 5 | ![Build](https://github.com/Roave/psalm-html-output/workflows/Build/badge.svg) 6 | 7 | ## Installation 8 | 9 | First, install `xsltproc` on your machine (for example, `apt install xsltproc`). 10 | 11 | Then `composer require --dev roave/psalm-html-output` 12 | 13 | ## Usage 14 | 15 | ```bash 16 | vendor/bin/psalm --output-format=xml | xsltproc vendor/roave/psalm-html-output/psalm-html-output.xsl - > psalm-report.html 17 | ``` 18 | 19 | ## Run with Docker 20 | 21 | To avoid having to install `xsltproc` if you already have Docker, first build the image with: 22 | 23 | ```bash 24 | docker build . -t psalm-html-output:latest 25 | ``` 26 | 27 | Then to generate the HTML: 28 | 29 | ```bash 30 | vendor/bin/psalm --output-format=xml | docker run --rm -i psalm-html-output:latest > psalm-report.html 31 | ``` 32 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roave/psalm-html-output", 3 | "description": "Psalm HTML Output", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "James Titcumb", 9 | "email": "james@asgrim.com" 10 | } 11 | ], 12 | "scripts": { 13 | "post-install-cmd": [ 14 | "which xsltproc" 15 | ], 16 | "post-update-cmd": [ 17 | "which xsltproc" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /psalm-html-output.xsl: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | Psalm Report 15 | 16 | 17 | 58 | 59 | 60 |
61 |

Total violations:

62 |
63 |
64 | 72 |
73 | 76 | 159 | 160 | 161 |
162 | 163 |
  • 164 |

    165 | : 166 |

    167 |
      168 |
    • 169 |
    • 170 |
    171 |

    172 |
    173 |                 /**##(##**//**##)##**/
    174 |             
    175 |
  • 176 |
    177 |
    178 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/generate-test-report.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | cd "$(dirname "$0")/.." 6 | 7 | docker build . -t psalm-html-output:latest 8 | 9 | cat test/psalm-report.xml | docker run --rm -i psalm-html-output:latest > test/psalm-report-generated.html 10 | -------------------------------------------------------------------------------- /test/psalm-report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Psalm Report 5 | 6 | 7 | 48 | 49 | 50 |

    Total violations: 3

    51 | 84 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /test/psalm-report.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | error 5 | 94 6 | 94 7 | MixedArrayAccess 8 | Cannot access array value on mixed variable $transaction 9 | tests/src/SomeTest.php 10 | /full/path/to/project/tests/src/SomeTest.php 11 | $this->assertSame('http://example.com/EchoTest', (string) $transaction['request']->getUri()); 12 | $transaction['request'] 13 | 2307 14 | 2330 15 | 2237 16 | 2342 17 | 71 18 | 94 19 | 1 20 | 51 21 | https://psalm.dev/051 22 | 23 | 24 | 25 | error 26 | 18 27 | 18 28 | InvalidScalarArgument 29 | Argument 1 of __construct expects string, int provided 30 | src/Reflector/Exception/IdentifierNotFound.php 31 | /full/path/to/better-reflection/src/Reflector/Exception/IdentifierNotFound.php 32 | parent::__construct($message); 33 | $message 34 | 392 35 | 400 36 | 364 37 | 402 38 | 29 39 | 37 40 | 1 41 | 51 42 | https://psalm.dev/051 43 | 44 | 45 | 46 | error 47 | 30 48 | 34 49 | InvalidScalarArgument 50 | Argument 1 of Roave\BetterReflection\Reflector\Exception\IdentifierNotFound::__construct expects int, string provided 51 | src/Reflector/Exception/IdentifierNotFound.php 52 | /full/path/to/better-reflection/src/Reflector/Exception/IdentifierNotFound.php 53 | return new self(sprintf( 54 | '%s "%s" could not be found in the located source', 55 | $identifier->getType()->getName(), 56 | $identifier->getName() 57 | ), $identifier); 58 | sprintf( 59 | '%s "%s" could not be found in the located source', 60 | $identifier->getType()->getName(), 61 | $identifier->getName() 62 | ) 63 | 651 64 | 815 65 | 627 66 | 830 67 | 25 68 | 10 69 | 1 70 | 51 71 | https://psalm.dev/051 72 | 73 | 74 | 75 | --------------------------------------------------------------------------------