diff --git a/.github/workflows/ci-qodana.yml b/.github/workflows/ci-qodana.yml new file mode 100644 index 00000000..06922f8b --- /dev/null +++ b/.github/workflows/ci-qodana.yml @@ -0,0 +1,254 @@ +--- +# This action is centrally managed in https://github.com//.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in +# the above-mentioned repo. + +name: Qodana + +on: + pull_request: + branches: [master, nightly] + types: [opened, synchronize, reopened] + push: + branches: [master, nightly] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + qodana_initial_check: + name: Qodana Initial Check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Prepare + id: prepare + run: | + # check the branch variable + if [ "${{ github.event_name }}" == "push" ] + then + echo "This is a PUSH event" + # use the branch name + destination=${{ github.ref_name }} + target=${{ github.ref_name }} + else + echo "This is a PR event" + # use the PR number + destination=${{ github.event.pull_request.number }} + target=${{ github.event.pull_request.base.ref }} + fi + + echo "checkout_repo=$checkout_repo" >> $GITHUB_OUTPUT + echo "checkout_ref=$checkout_ref" >> $GITHUB_OUTPUT + echo "destination=$destination" >> $GITHUB_OUTPUT + echo "target=$target" >> $GITHUB_OUTPUT + + # prepare urls + base=https://${{ github.repository_owner }}.github.io + report_url=${base}/qodana-reports/${{ github.event.repository.name }}/${destination} + echo "report_url=$report_url" >> $GITHUB_OUTPUT + + # build matrix + files=$(find . -type f -iname "qodana*.yaml") + + echo "files: ${files}" + + # do not quote to keep this as a single line + echo files=${files} >> $GITHUB_OUTPUT + + MATRIX_COMBINATIONS="" + REPORTS_MARKDOWN="" + for FILE in ${files}; do + # extract the language from file name after `qodana-` and before `.yaml` + language=$(echo $FILE | sed -r -z -e 's/(\.\/)*.*\/(qodana.yaml)/default/gm') + if [[ $language != "default" ]]; then + language=$(echo $FILE | sed -r -z -e 's/(\.\/)*.*qodana-(.*).yaml/\2/gm') + fi + MATRIX_COMBINATIONS="$MATRIX_COMBINATIONS {\"file\": \"$FILE\", \"language\": \"$language\"}," + REPORTS_MARKDOWN="$REPORTS_MARKDOWN
- [${language}](${report_url}/${language})" + done + + # removes the last character (i.e. comma) + MATRIX_COMBINATIONS=${MATRIX_COMBINATIONS::-1} + + # setup matrix for later jobs + matrix=$(( + echo "{ \"include\": [$MATRIX_COMBINATIONS] }" + ) | jq -c .) + + echo $matrix + echo $matrix | jq . + echo "matrix=$matrix" >> $GITHUB_OUTPUT + + echo "reports_markdown=$REPORTS_MARKDOWN" >> $GITHUB_OUTPUT + + - name: Setup initial notification client payload + id: client_payload + if: >- + startsWith(github.event_name, 'pull_request') && + steps.prepare.outputs.files != '' + run: | + # workflow logs + workflow_url_a=https://github.com/${{ github.repository_owner }}/${{ github.event.repository.name }} + workflow_url=${workflow_url_a}/actions/runs/${{ github.run_id }} + + # multiline message + message=$(cat <<- EOF + :warning: **Qodana is checking this PR** :warning: + Live results available [here](${workflow_url}) + EOF + ) + + # escape json control characters + message=$(jq -n --arg message "$message" '$message' | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g') + + client_payload=$(echo '{ + "issue_message": "'"${message}"'", + "issue_message_id": "'"qodana"'", + "issue_number": "'"${{ github.event.number }}"'" + }' | jq -c .) + + echo $client_payload + echo $client_payload | jq . + echo "client_payload=$client_payload" >> $GITHUB_OUTPUT + + - name: Repository Dispatch + if: ${{ steps.prepare.outputs.files != '' }} + uses: peter-evans/repository-dispatch@v2.1.1 + with: + token: ${{ secrets.GH_BOT_TOKEN }} + repository: ${{ github.repository_owner }}/.github + event-type: issue-pr-comment + client-payload: ${{ steps.client_payload.outputs.client_payload }} + + outputs: + destination: ${{ steps.prepare.outputs.destination }} + target: ${{ steps.prepare.outputs.target }} + files: ${{ steps.prepare.outputs.files }} + reports_markdown: ${{ steps.prepare.outputs.reports_markdown }} + matrix: ${{ steps.prepare.outputs.matrix }} + + qodana: + if: ${{ needs.qodana_initial_check.outputs.files != '' }} + needs: [qodana_initial_check] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.qodana_initial_check.outputs.matrix) }} + name: Qodana-Scan-${{ matrix.language }} + continue-on-error: true + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Get baseline + id: baseline + run: | + # check if destination is not an integer + if ! [[ "${{ needs.qodana_initial_check.outputs.destination }}" =~ ^[0-9]+$ ]] + then + echo "Running for a branch update" + echo "baseline_args=" >> $GITHUB_OUTPUT + else + echo "Running for a PR" + + sarif_file=qodana.sarif.json + repo=${{ github.event.repository.name }} + target=${{ needs.qodana_initial_check.outputs.target }} + language=${{ matrix.language }} + + baseline_file="${repo}/${target}/${language}/results/${sarif_file}" + baseline_file_url="https://lizardbyte.github.io/qodana-reports/${baseline_file}" + + # don't fail if file does not exist + wget ${baseline_file_url} || true + + # check if file exists + if [ -f ${sarif_file} ] + then + echo "baseline exists" + echo "baseline_args=--baseline,${sarif_file}" >> $GITHUB_OUTPUT + else + echo "baseline does not exist" + echo "baseline_args=" >> $GITHUB_OUTPUT + fi + fi + + - name: Rename Qodana config file + id: rename + run: | + # rename the file + if [ "${{ matrix.file }}" != "./qodana.yaml" ] + then + mv -f ${{ matrix.file }} ./qodana.yaml + fi + + - name: Qodana + id: qodana + continue-on-error: true # ensure dispatch-qodana job is run + uses: JetBrains/qodana-action@v2022.3.4 + with: + additional-cache-hash: ${{ github.ref }}-${{ matrix.language }} + artifact-name: qodana-${{ matrix.language }} # yamllint disable-line rule:line-length + args: '--print-problems,${{ steps.baseline.outputs.baseline_args }}' + pr-mode: false + upload-result: true + use-caches: true + + - name: Set output status + id: status + run: | + # check if qodana failed + echo "qodana_status=${{ steps.qodana.outcome }}" >> $GITHUB_OUTPUT + + outputs: + qodana_status: ${{ steps.status.outputs.qodana_status }} + + dispatch-qodana: + # trigger qodana-reports to download artifacts from the matrix runs + needs: [qodana_initial_check, qodana] + runs-on: ubuntu-latest + name: Dispatch Qodana + if: ${{ needs.qodana_initial_check.outputs.files != '' }} + steps: + - name: Setup client payload + id: client_payload + run: | + # get the artifacts + artifacts=${{ toJson(steps.artifacts.outputs.result) }} + artifacts=$(echo $artifacts | jq -c .) + + # get the target branch + target=${{ needs.qodana_initial_check.outputs.target }} + + # get the destination branch + destination=${{ needs.qodana_initial_check.outputs.destination }} + + # client payload + client_payload=$(echo '{ + "destination": "'"${destination}"'", + "ref": "'"${{ github.ref }}"'", + "repo": "'"${{ github.repository }}"'", + "repo_name": "'"${{ github.event.repository.name }}"'", + "run_id": "'"${{ github.run_id }}"'", + "reports_markdown": "'"${{ needs.qodana_initial_check.outputs.reports_markdown }}"'", + "status": "'"${{ needs.qodana.outputs.qodana_status }}"'" + }' | jq -c .) + + echo $client_payload + echo $client_payload | jq . + echo "client_payload=$client_payload" >> $GITHUB_OUTPUT + + - name: Repository Dispatch + uses: peter-evans/repository-dispatch@v2.1.1 + with: + token: ${{ secrets.GH_BOT_TOKEN }} + repository: ${{ github.repository_owner }}/qodana-reports + event-type: qodana-publish + client-payload: ${{ steps.client_payload.outputs.client_payload }} diff --git a/qodana-js.yaml b/qodana-js.yaml new file mode 100644 index 00000000..d1309513 --- /dev/null +++ b/qodana-js.yaml @@ -0,0 +1,21 @@ +--- +version: "1.0" +linter: jetbrains/qodana-js:2023.1-eap + +bootstrap: | + # install npm dependencies + npm install + +exclude: + - name: All + paths: + - gh-pages + - third-party + +failThreshold: 100 + +include: + - name: CheckDependencyLicenses + +profile: + name: qodana.recommended diff --git a/qodana-python.yaml b/qodana-python.yaml new file mode 100644 index 00000000..efbf876e --- /dev/null +++ b/qodana-python.yaml @@ -0,0 +1,29 @@ +--- +version: "1.0" +linter: jetbrains/qodana-python:2023.1-eap + +bootstrap: | + # setup python + + python3 -m venv /data/cache/venv + source /data/cache/venv/bin/activate + python3 -m pip install -r /data/project/docs/requirements.txt + python3 -m pip install -r /data/project/scripts/requirements.txt + + # remove idea directory (No Python interpreter configured for the project) + # https://github.com/JetBrains/Qodana/discussions/134#discussioncomment-4329981 + rm -rf .idea + +exclude: + - name: All + paths: + - gh-pages + - third-party + +failThreshold: 100 + +include: + - name: CheckDependencyLicenses + +profile: + name: qodana.recommended