├── .devcontainer └── devcontainer.json ├── .github ├── dependabot.yml └── workflows │ ├── deps.yml │ ├── lint.yml │ ├── release-action.yml │ ├── release-crate.yml │ └── toolchain.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── deny.toml ├── justfile ├── rust-toolchain ├── setup └── action.yml └── src └── main.rs /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cargo-action-fmt.dev", 3 | "image": "ghcr.io/linkerd/dev:v36", 4 | "extensions": [ 5 | "DavidAnson.vscode-markdownlint", 6 | "KokaKiwi.vscode-just", 7 | "NathanRidley.autotrim", 8 | "rust-lang.rust-analyzer", 9 | "samverschueren.final-newline", 10 | "tamasfe.even-better-toml" 11 | ], 12 | "runArgs": [ 13 | "--init" 14 | ], 15 | "remoteUser": "code", 16 | "mounts": [] 17 | } 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | time: "03:00" 8 | timezone: UTC 9 | 10 | - package-ecosystem: cargo 11 | directory: / 12 | schedule: 13 | interval: weekly 14 | time: "03:30" 15 | timezone: UTC 16 | allow: 17 | - dependency-type: all 18 | ignore: 19 | - dependency-name: "clap_derive" 20 | -------------------------------------------------------------------------------- /.github/workflows/deps.yml: -------------------------------------------------------------------------------- 1 | # Audits dependencies with cargo-deny 2 | name: deps 3 | 4 | permissions: 5 | contents: read 6 | 7 | on: 8 | pull_request: 9 | paths: 10 | - Cargo.lock 11 | - deny.toml 12 | - .github/workflows/deps.yml 13 | 14 | env: 15 | CARGO_INCREMENTAL: 0 16 | CARGO_NET_RETRY: 10 17 | RUST_BACKTRACE: short 18 | RUSTUP_MAX_RETRIES: 10 19 | 20 | jobs: 21 | # Check for security advisories. 22 | # 23 | # Failures are not fatal, since issues are opened in the linkerd2 repo via rustsecbot. 24 | advisories: 25 | timeout-minutes: 10 26 | runs-on: ubuntu-latest 27 | continue-on-error: true 28 | steps: 29 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 30 | - uses: EmbarkStudios/cargo-deny-action@8a8607bd8e2b3a514d5a40174cc7c55b229d9ba7 31 | with: 32 | command: check advisories 33 | 34 | # Audit licenses, unreleased crates, and unexpected duplicate versions. 35 | bans: 36 | timeout-minutes: 10 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 40 | - uses: EmbarkStudios/cargo-deny-action@8a8607bd8e2b3a514d5a40174cc7c55b229d9ba7 41 | with: 42 | command: check bans licenses sources 43 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | paths: 9 | - "**/*.rs" 10 | - .github/workflows/lint.yml 11 | 12 | jobs: 13 | clippy: 14 | timeout-minutes: 10 15 | runs-on: ubuntu-latest 16 | container: docker://ghcr.io/linkerd/dev:v36-rust 17 | steps: 18 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 19 | - run: just-cargo fetch --locked 20 | - run: just-cargo clippy 21 | 22 | fmt: 23 | timeout-minutes: 10 24 | runs-on: ubuntu-latest 25 | container: docker://ghcr.io/linkerd/dev:v36-rust 26 | steps: 27 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 28 | - run: just-cargo fmt --check 29 | 30 | docs: 31 | timeout-minutes: 10 32 | runs-on: ubuntu-latest 33 | container: docker://ghcr.io/linkerd/dev:v36-rust 34 | steps: 35 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 36 | - run: just-cargo fetch 37 | - run: just-cargo doc --no-deps 38 | -------------------------------------------------------------------------------- /.github/workflows/release-action.yml: -------------------------------------------------------------------------------- 1 | name: Action Release 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - .github/workflows/release-actionml 7 | - setup/action.yml 8 | push: 9 | tags: 10 | - 'v[0-9]+.[0-9]+.[0-9]+' 11 | 12 | permissions: 13 | contents: write 14 | 15 | jobs: 16 | publish: 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 10 19 | container: docker://ghcr.io/linkerd/dev:v36-rust 20 | steps: 21 | - name: Parse tag 22 | id: parse-tag 23 | shell: bash 24 | run: | 25 | ref="${{ github.ref }}" 26 | if [[ "$ref" == refs/tags/v* ]]; then 27 | v="${ref#refs/tags/}" 28 | echo "Action version: $v" 29 | echo version="$v" >> "$GITHUB_OUTPUT" 30 | else 31 | echo "Not a release version: $ref" 32 | fi 33 | 34 | # XXX dev:v36-rust container doesn't include libjq 35 | - run: apt update && apt install -y --no-install-recommends jq 36 | 37 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 38 | - run: just fetch --locked 39 | 40 | - name: Check that the action references the current crate version 41 | run: | 42 | action=$(yq .inputs.version.default setup/action.yml) 43 | echo "setup/action.yml $action" 44 | crate=$(just crate-version) 45 | echo "Cargo.toml $crate" 46 | if [ "${crate}" != "${action}" ]; then 47 | echo "::error ::Action version ${action} does not match the current crate version ${crate}" 48 | exit 1 49 | fi 50 | 51 | # Exercise the action to ensure it works as expected 52 | - uses: ./setup 53 | id: setup 54 | - name: Check that setup-provided binary is first in the PATH 55 | run: | 56 | bin=$(command -v cargo-action-fmt) 57 | if [ "$bin" != '${{ steps.setup.outputs.bin }}' ]; then 58 | echo "::error ::cargo-action-fmt is not installed in the expected location" 59 | exit 1 60 | fi 61 | - name: Check that it's usable 62 | run: cargo check -q --message-format=json | cargo-action-fmt 63 | 64 | - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 65 | if: steps.parse-tag.outputs.version 66 | with: 67 | name: setup@${{ steps.parse-tag.outputs.version }} 68 | generate_release_notes: true 69 | -------------------------------------------------------------------------------- /.github/workflows/release-crate.yml: -------------------------------------------------------------------------------- 1 | name: Crate Release 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - .github/workflows/release-crate.yml 7 | - Cargo.toml 8 | - justfile 9 | push: 10 | tags: 11 | - 'release/v[0-9]+.[0-9]+.[0-9]+' 12 | 13 | permissions: 14 | contents: write 15 | 16 | jobs: 17 | publish: 18 | runs-on: ubuntu-latest 19 | timeout-minutes: 10 20 | container: docker://ghcr.io/linkerd/dev:v36-rust-musl 21 | steps: 22 | - name: Parse tag 23 | id: parse-tag 24 | shell: bash 25 | run: | 26 | ref="${{ github.ref }}" 27 | if [[ "$ref" == refs/tags/release/* ]]; then 28 | v="${ref#refs/tags/release/}" 29 | echo "Crate version: $v" 30 | echo version="$v" >> "$GITHUB_OUTPUT" 31 | else 32 | echo "Not a release version: $ref" 33 | fi 34 | 35 | # XXX dev:v36-rust container doesn't include libjq 36 | - run: apt update && apt install -y --no-install-recommends jq 37 | 38 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 39 | - run: just fetch --locked 40 | 41 | - name: Check crate version 42 | if: steps.parse-tag.outputs.version 43 | run: | 44 | v=$(just crate-version) 45 | echo "cargo.toml $v" 46 | if [ "${v}" != '${{ steps.parse-tag.outputs.version }}' ]; then 47 | echo "::error ::Crate version '${v} does' not match tag version '${{ steps.parse-tag.outputs.version }}'" 48 | exit 1 49 | fi 50 | 51 | # These are fairly quick so we don't bother running them in parallel. The 52 | # `cargo fetch` time above far outweighs the build time. 53 | - run: echo PKG_DIR="$(mktemp -d "$RUNNER_TEMP/pkg.XXX")" >> "$GITHUB_ENV" 54 | - run: just profile=release target=x86_64-unknown-linux-musl package "$PKG_DIR" 55 | - run: just profile=release target=aarch64-unknown-linux-musl package "$PKG_DIR" 56 | - run: just profile=release target=armv7-unknown-linux-musleabihf package "$PKG_DIR" 57 | - run: find "$PKG_DIR"/release -ls 58 | 59 | - if: steps.parse-tag.outputs.version == '' 60 | run: cargo publish --dry-run 61 | - if: steps.parse-tag.outputs.version 62 | run: cargo publish --token=${{ secrets.CRATESIO_TOKEN }} 63 | 64 | - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 65 | if: steps.parse-tag.outputs.version 66 | with: 67 | name: ${{ steps.parse-tag.outputs.version }} 68 | files: ${{ env.PKG_DIR }}/release/* 69 | generate_release_notes: true 70 | -------------------------------------------------------------------------------- /.github/workflows/toolchain.yml: -------------------------------------------------------------------------------- 1 | name: rust-toolchain 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - rust-toolchain 7 | - "**Dockerfile" 8 | - ".github/workflows/*" 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | dev-action-images: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: linkerd/dev/actions/setup-tools@v36 18 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 19 | - run: just-dev check-action-images 20 | 21 | lint-actions: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: linkerd/dev/actions/setup-tools@v36 25 | - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 26 | - run: just-dev lint-actions 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.66" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "cargo-action-fmt" 19 | version = "1.0.4" 20 | dependencies = [ 21 | "anyhow", 22 | "clap", 23 | "serde", 24 | "serde_json", 25 | ] 26 | 27 | [[package]] 28 | name = "clap" 29 | version = "4.0.29" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" 32 | dependencies = [ 33 | "bitflags", 34 | "clap_derive", 35 | "clap_lex", 36 | "once_cell", 37 | ] 38 | 39 | [[package]] 40 | name = "clap_derive" 41 | version = "4.0.21" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" 44 | dependencies = [ 45 | "heck", 46 | "proc-macro-error", 47 | "proc-macro2", 48 | "quote", 49 | "syn", 50 | ] 51 | 52 | [[package]] 53 | name = "clap_lex" 54 | version = "0.3.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 57 | dependencies = [ 58 | "os_str_bytes", 59 | ] 60 | 61 | [[package]] 62 | name = "heck" 63 | version = "0.4.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 66 | 67 | [[package]] 68 | name = "itoa" 69 | version = "1.0.4" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 72 | 73 | [[package]] 74 | name = "once_cell" 75 | version = "1.16.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 78 | 79 | [[package]] 80 | name = "os_str_bytes" 81 | version = "6.4.1" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 84 | 85 | [[package]] 86 | name = "proc-macro-error" 87 | version = "1.0.4" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 90 | dependencies = [ 91 | "proc-macro-error-attr", 92 | "proc-macro2", 93 | "quote", 94 | "syn", 95 | "version_check", 96 | ] 97 | 98 | [[package]] 99 | name = "proc-macro-error-attr" 100 | version = "1.0.4" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 103 | dependencies = [ 104 | "proc-macro2", 105 | "quote", 106 | "version_check", 107 | ] 108 | 109 | [[package]] 110 | name = "proc-macro2" 111 | version = "1.0.47" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 114 | dependencies = [ 115 | "unicode-ident", 116 | ] 117 | 118 | [[package]] 119 | name = "quote" 120 | version = "1.0.21" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 123 | dependencies = [ 124 | "proc-macro2", 125 | ] 126 | 127 | [[package]] 128 | name = "ryu" 129 | version = "1.0.11" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 132 | 133 | [[package]] 134 | name = "serde" 135 | version = "1.0.148" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" 138 | dependencies = [ 139 | "serde_derive", 140 | ] 141 | 142 | [[package]] 143 | name = "serde_derive" 144 | version = "1.0.148" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" 147 | dependencies = [ 148 | "proc-macro2", 149 | "quote", 150 | "syn", 151 | ] 152 | 153 | [[package]] 154 | name = "serde_json" 155 | version = "1.0.89" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" 158 | dependencies = [ 159 | "itoa", 160 | "ryu", 161 | "serde", 162 | ] 163 | 164 | [[package]] 165 | name = "syn" 166 | version = "1.0.105" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" 169 | dependencies = [ 170 | "proc-macro2", 171 | "quote", 172 | "unicode-ident", 173 | ] 174 | 175 | [[package]] 176 | name = "unicode-ident" 177 | version = "1.0.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" 180 | 181 | [[package]] 182 | name = "version_check" 183 | version = "0.9.4" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 186 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-action-fmt" 3 | version = "1.0.4" 4 | edition = "2021" 5 | license = "MIT" 6 | repository = "https://github.com/olix0r/cargo-action-fmt" 7 | description = """ 8 | Converts cargo check (and clippy) JSON output to the GitHub Action error format 9 | """ 10 | 11 | [dependencies] 12 | anyhow = "1" 13 | serde = { version = "1", features = ["derive"] } 14 | serde_json = "1" 15 | 16 | [dependencies.clap] 17 | version = "4" 18 | default-features = false 19 | features = ["derive", "std"] 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2022 Oliver Gould @olix0r 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `cargo-action-fmt` 2 | 3 | Takes JSON-formatted `cargo check` (and `cargo clippy`) output and formats it 4 | for GitHub actions. 5 | 6 | ## Examples 7 | 8 | This tool can be used with a variety of `cargo` commands: 9 | 10 | ```shell 11 | :; cargo check -q --message-format=json | cargo-action-fmt 12 | ``` 13 | 14 | ```shell 15 | :; cargo clippy -q --message-format=json | cargo-action-fmt 16 | ``` 17 | 18 | ```shell 19 | :; cargo doc --message-format=json | cargo-action-fmt 20 | ``` 21 | 22 | Note that this tool does **not** currently support `cargo fmt` or `cargo test` 23 | output. However, you can invoke `cargo test` so that test compilation errors are 24 | annotated properly: 25 | 26 | ```shell 27 | :; cargo test --no-run --message-format=json | cargo-action-fmt 28 | ``` 29 | 30 | ### GitHub Action 31 | 32 | It's primarily intended to be used in a GitHub Actions workflow: 33 | 34 | ```yaml 35 | docs: 36 | runs-on: ubuntu-latest 37 | container: rust:slim 38 | steps: 39 | - uses: olix0r/cargo-action-fmt/setup@v2 40 | - uses: actions/checkout@v2 41 | - run: cargo doc --no-deps --message-format=json | cargo-action-fmt 42 | ``` 43 | 44 | ![Example annotation](https://user-images.githubusercontent.com/240738/153767390-66f859d4-da3f-4e1e-846b-02605e8be628.png) 45 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [advisories] 2 | db-path = "~/.cargo/advisory-db" 3 | db-urls = ["https://github.com/rustsec/advisory-db"] 4 | vulnerability = "deny" 5 | unmaintained = "warn" 6 | yanked = "deny" 7 | notice = "warn" 8 | # ignore = [ 9 | # # jsonwebtoken v7 pulls in bad versions of `time`/`chrono`. Waiting on jsonwebtoken v8. 10 | # "RUSTSEC-2020-0071", 11 | # "RUSTSEC-2020-0159", 12 | # ] 13 | 14 | [licenses] 15 | unlicensed = "deny" 16 | allow = [ 17 | "Apache-2.0", 18 | "MIT", 19 | ] 20 | deny = [] 21 | copyleft = "deny" 22 | allow-osi-fsf-free = "neither" 23 | default = "deny" 24 | confidence-threshold = 0.8 25 | exceptions = [] 26 | 27 | [[licenses.clarify]] 28 | name = "ring" 29 | version = "*" 30 | expression = "MIT AND ISC AND OpenSSL" 31 | license-files = [ 32 | { path = "LICENSE", hash = 0xbd0eed23 }, 33 | ] 34 | 35 | [[licenses.clarify]] 36 | name = "encoding_rs" 37 | version = "*" 38 | expression = "(Apache-2.0 OR MIT) AND BSD-3-Clause" 39 | license-files = [ 40 | { path = "COPYRIGHT", hash = 0x39f8ad31 }, 41 | ] 42 | 43 | [bans] 44 | multiple-versions = "deny" 45 | wildcards = "deny" 46 | highlight = "all" 47 | deny = [] 48 | skip-tree = [] 49 | 50 | [sources] 51 | unknown-registry = "deny" 52 | unknown-git = "deny" 53 | allow-registry = ["https://github.com/rust-lang/crates.io-index"] 54 | allow-git = [] 55 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # Defaults for development. 2 | profile := 'debug' 3 | target := 'x86_64-unknown-linux-gnu' 4 | 5 | fetch *args: 6 | @just-cargo fetch {{ args }} 7 | 8 | build *args: 9 | @just-cargo profile={{ profile }} target={{ target }} build --frozen {{ args }} 10 | 11 | package dir: build 12 | @mkdir -p '{{ dir }}/{{ profile }}' 13 | tar czf "{{ dir }}/{{ profile }}/cargo-action-fmt-$(just crate-version)-{{ target }}.tar.gz" \ 14 | -C 'target/{{ target }}/{{ profile }}' \ 15 | cargo-action-fmt 16 | 17 | crate-version: 18 | @just-cargo crate-version 'cargo-action-fmt' 19 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | 1.64.0 2 | -------------------------------------------------------------------------------- /setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup cargo-action-fmt 2 | description: Formats cargo's JSON output as GitHub Action annotations 3 | author: olix0r 4 | 5 | inputs: 6 | version: 7 | description: The version of cargo-action-fmt to use 8 | required: false 9 | default: v1.0.4 10 | 11 | outputs: 12 | bin: 13 | description: The path to the cargo-action-fmt binary 14 | value: ${{ steps.install.outputs.bin }} 15 | 16 | runs: 17 | using: composite 18 | steps: 19 | - name: Install cargo-action-fmt 20 | id: install 21 | shell: bash 22 | run: | 23 | set -euo pipefail 24 | d=$(mktemp -d "${RUNNER_TEMP}/caf.XXXXX") 25 | url="https://github.com/olix0r/cargo-action-fmt/releases/download/release%2F${{ inputs.version }}/cargo-action-fmt-${{ inputs.version }}-$(uname -m)-unknown-linux-musl.tar.gz" 26 | curl --proto '=https' --tlsv1.3 -fsSL "$url" | tar zvxf - -C "$d" cargo-action-fmt 27 | echo bin="$d/cargo-action-fmt" >> "$GITHUB_OUTPUT" 28 | echo PATH="$d:$PATH" >> "$GITHUB_ENV" 29 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings, rust_2018_idioms)] 2 | 3 | use anyhow::Result; 4 | use clap::Parser; 5 | use serde_json as json; 6 | use std::path::PathBuf; 7 | 8 | /// Converts cargo check (and clippy) JSON output to the GitHub Action error format 9 | #[derive(Debug, Parser)] 10 | #[clap(version)] 11 | struct Args { 12 | #[clap(default_value = "-")] 13 | path: PathBuf, 14 | } 15 | 16 | #[derive(Clone, Debug, serde::Deserialize)] 17 | #[serde(tag = "reason", rename_all = "kebab-case")] 18 | enum CargoMessage { 19 | CompilerArtifact(json::Value), 20 | BuildScriptExecuted(json::Value), 21 | CompilerMessage { message: CompilerMessage }, 22 | BuildFinished { success: bool }, 23 | } 24 | 25 | #[derive(Clone, Debug, serde::Deserialize)] 26 | #[serde(rename_all = "snake_case")] 27 | struct CompilerMessage { 28 | rendered: String, 29 | code: Option, 30 | level: String, 31 | spans: Vec, 32 | } 33 | 34 | #[derive(Clone, Debug, serde::Deserialize)] 35 | #[serde(rename_all = "snake_case")] 36 | struct CompilerMessageSpan { 37 | column_start: usize, 38 | column_end: usize, 39 | file_name: String, 40 | line_start: usize, 41 | line_end: usize, 42 | } 43 | 44 | fn main() -> Result<()> { 45 | let Args { path } = Args::parse(); 46 | 47 | for msg in read_messages(path)? { 48 | match msg { 49 | CargoMessage::CompilerArtifact(_) => {} 50 | CargoMessage::BuildScriptExecuted(_) => {} 51 | CargoMessage::CompilerMessage { message } => { 52 | if message.code.is_some() { 53 | let msg = encode_newlines(message.rendered); 54 | for span in message.spans.into_iter() { 55 | println!( 56 | "::{} file={},line={},endLine={},col={},endColumn={}::{}", 57 | message.level, 58 | span.file_name, 59 | span.line_start, 60 | span.line_end, 61 | span.column_start, 62 | span.column_end, 63 | msg, 64 | ); 65 | } 66 | } 67 | } 68 | CargoMessage::BuildFinished { success } => { 69 | if !success { 70 | anyhow::bail!("build failed") 71 | } 72 | } 73 | } 74 | } 75 | 76 | Ok(()) 77 | } 78 | 79 | fn read_messages(path: PathBuf) -> Result> { 80 | if path.to_str() == Some("-") { 81 | let msgs = json::Deserializer::from_reader(std::io::stdin()) 82 | .into_iter::() 83 | .collect::, _>>()?; 84 | return Ok(msgs); 85 | } 86 | 87 | let f = std::fs::File::open(path)?; 88 | let msgs = json::Deserializer::from_reader(f) 89 | .into_iter::() 90 | .collect::, _>>()?; 91 | Ok(msgs) 92 | } 93 | 94 | fn encode_newlines(orig: String) -> String { 95 | orig.replace('\n', "%0A") 96 | } 97 | --------------------------------------------------------------------------------