diff --git a/cargo-annotation/action.yml b/cargo-annotation/action.yml index 861a1a4..d7a8a1f 100644 --- a/cargo-annotation/action.yml +++ b/cargo-annotation/action.yml @@ -12,18 +12,8 @@ runs: using: "composite" steps: - name: cargo - run: | - CMD="cargo" - - if [[ "${{ inputs.cargo-command }}" != "" ]]; then - CMD="$CMD ${{ inputs.cargo-command }}" - fi - - if [[ "${{ inputs.with-annotation }}" == "true" ]]; then - CMD="$CMD --message-format=json" - fi - - eval "$CMD | ${{ github.action_path }}/annotation.sh" + run: python $GITHUB_ACTION_PATH/annotation.py shell: bash env: + INPUT_CARGO_COMMAND: ${{ inputs.cargo-command }} WITH_ANNOTATION: ${{ inputs.with-annotation }} diff --git a/cargo-annotation/annotation.py b/cargo-annotation/annotation.py new file mode 100755 index 0000000..c4416f0 --- /dev/null +++ b/cargo-annotation/annotation.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import json +import sys +import shlex +from typing import Any + + +def emit_annotation_for_clippy(results: list[dict[str, Any]], with_annotation: bool): + total_count = 0 + limit = 10 + + severenty_map = { + "help": "notice", + "note": "notice", + "warning": "warning", + "error": "error", + } + for item in results: + if total_count >= limit: + break + + message = item.get("message") + if not message: + continue + + spans = message.get("spans") or [] + primary_span = next((span for span in spans if span.get("is_primary")), None) + if not primary_span: + continue + + level = severenty_map.get(message.get("level"), "error") + title = message.get("message", "") + rendered_message = message.get("rendered", "") + + file_name = (primary_span["file_name"],) + line_start = (primary_span["line_start"],) + line_end = (primary_span["line_end"],) + column_start = (primary_span["column_start"],) + column_end = (primary_span["column_end"],) + + line_info = f"line={line_start},endLine={line_end},title={title}" + + column_info = "" + if ( + line_start == line_end + and column_end is not None + and column_start is not None + ): + column_info = f"col={column_start},endColumn={column_end}," + if with_annotation: + print( + f"::{level} file={file_name},{column_info}{line_info}::{rendered_message}" + ) + total_count += 1 + + return 1 if total_count > 0 else 0 + + +def main(): + cmd = ["cargo"] + + cargo_command = os.getenv("INPUT_CARGO_COMMAND", "").strip() + with_annotation = ( + os.getenv("INPUT_WITH_ANNOTATION", "true").strip().lower() == "true" + ) + + if cargo_command: + args = shlex.split(cargo_command) + cmd.extend(args) + + if "--message-format=json" not in cmd: + cmd.append("--message-format=json") + + proc = subprocess.run(cmd, capture_output=with_annotation) + clippy_results = [ + json.loads(line) for line in proc.stdout.splitlines() if line.strip() + ] + + result = emit_annotation_for_clippy(clippy_results, with_annotation) + sys.exit(result) + + +if __name__ == "__main__": + main() diff --git a/cargo-annotation/annotation.sh b/cargo-annotation/annotation.sh deleted file mode 100755 index 8952cc2..0000000 --- a/cargo-annotation/annotation.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit -set -o nounset -set -o pipefail - -annotations=$(jq -r ' - select(.reason == "compiler-message") - | .message as $msg - | select($msg.level == "error" or $msg.level == "warning") - | $msg.spans[] - | select(.is_primary) - | "::\($msg.level) file=\(.file_name),line=\(.line_start),col=\(.column_start)\(if .column_end != .column_start then ",endColumn=\(.column_end)" else "" end)::\($msg.message)" -') - -if [[ -n "$annotations" ]]; then - if [[ "${WITH_ANNOTATION}" == "true" ]]; then - echo "$annotations" - fi - exit 1 -fi