diff --git a/.github/actions/linter_tests/action.yaml b/.github/actions/linter_tests/action.yaml index f16025012..bb976fb92 100644 --- a/.github/actions/linter_tests/action.yaml +++ b/.github/actions/linter_tests/action.yaml @@ -66,7 +66,7 @@ runs: - name: Run plugin tests if: runner.os == 'Windows' - run: npm test --ci ${{ inputs.append-args }} ${{ env.PLATFORM_APPEND_ARGS }} + run: npm test ${{ inputs.append-args }} ${{ env.PLATFORM_APPEND_ARGS }} --ci shell: bash working-directory: ${{ inputs.path }} env: @@ -87,10 +87,10 @@ runs: working-directory: ${{ inputs.path }} trunk-token: ${{ inputs.trunk-token }} org: trunk-staging-org - run: npm test --ci ${{ inputs.append-args }} ${{ env.PLATFORM_APPEND_ARGS }} + run: npm test ${{ inputs.append-args }} ${{ env.PLATFORM_APPEND_ARGS }} --ci env: PLUGINS_TEST_LINTER_VERSION: ${{ inputs.linter-version }} PLUGINS_TEST_CLI_VERSION: ${{ inputs.cli-version }} PLUGINS_TEST_CLI_PATH: ${{ env.CLI_PATH }} SOURCERY_TOKEN: ${{ inputs.sourcery-token }} - DEBUG: Driver:nixpkgs-fmt:* + DEBUG: Driver:nixpkgs-fmt:*, Driver:eslint:* diff --git a/.github/actions/tool_tests/action.yaml b/.github/actions/tool_tests/action.yaml index 98506628a..938952395 100644 --- a/.github/actions/tool_tests/action.yaml +++ b/.github/actions/tool_tests/action.yaml @@ -50,7 +50,7 @@ runs: - name: Run plugin tests if: runner.os == 'Windows' - run: npm test --ci ${{ inputs.append-args }} ${{ env.PLATFORM_APPEND_ARGS }} + run: npm test ${{ inputs.append-args }} ${{ env.PLATFORM_APPEND_ARGS }} --ci shell: bash working-directory: ${{ inputs.path }} env: @@ -67,7 +67,7 @@ runs: working-directory: ${{ inputs.path }} trunk-token: ${{ inputs.trunk-token }} org: trunk-staging-org - run: npm test --ci ${{ inputs.append-args }} ${{ env.PLATFORM_APPEND_ARGS }} + run: npm test ${{ inputs.append-args }} ${{ env.PLATFORM_APPEND_ARGS }} --ci env: PLUGINS_TEST_CLI_VERSION: ${{ inputs.cli-version }} PLUGINS_TEST_CLI_PATH: ${{ env.CLI_PATH }} diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index ec088427d..d78540004 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -190,184 +190,22 @@ jobs: name: ${{ matrix.results-file }}-test-results path: ${{ matrix.results-file }}-res.json - upload_test_results: - name: Upload Test Results + upload_linter_tests: + name: Upload Linter Test Results needs: linter_tests_release - runs-on: ubuntu-x64 # Still run on test failure if: always() - timeout-minutes: 10 - env: - SLACK_CHANNEL_ID: plugins-notifications - steps: - - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: Retrieve Test Outputs ubuntu - id: download-ubuntu - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 - continue-on-error: true - with: - name: ubuntu-latest-test-results - - name: Retrieve Test Outputs macOS - id: download-macos - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 - continue-on-error: true - with: - name: macos-latest-test-results - - - name: Retrieve Test Outputs Windows - id: download-windows - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 - continue-on-error: true - with: - name: windows-latest-test-results - - - name: Print Test Outputs - continue-on-error: true - shell: bash - run: | - echo "::group::Ubuntu results" - cat "ubuntu-latest-res.json" || echo "missing" - echo "::endgroup::" - - echo "::group::Macos results" - cat "macos-latest-res.json" || echo "missing" - echo "::endgroup::" - - echo "::group::Windows results" - cat "windows-latest-res.json" || echo "missing" - echo "::endgroup::" - - - name: Slack Notification For Missing Artifacts - uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 - if: - steps.download-ubuntu.outcome == 'failure' || steps.download-macos.outcome == 'failure' || - steps.download-windows.outcome == 'failure' - with: - channel-id: ${{ env.SLACK_CHANNEL_ID }} - payload: | - { - "text": "Artifact Download Failure", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "Failure: " - } - } - ] - } - env: - SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} - - - name: Setup Node - uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 - with: - node-version: 18 - - - name: Install Dependencies - shell: bash - run: npm ci - - - name: Add npm bin to path - shell: bash - run: echo "$PWD/node_modules/.bin" >> "$GITHUB_PATH" - - - name: Parse Test Results - shell: bash - id: parse - run: | - npm run parse - echo "failures=$([[ -f failures.json ]] && echo "true" || echo "false")" >> "$GITHUB_OUTPUT" - echo "failures-payload=$(cat failures.json)" >> "$GITHUB_OUTPUT" - env: - PLUGIN_VERSION: ${{needs.linter_tests_release.outputs.plugin-version}} - # Used to format Slack notification for failures - RUN_ID: ${{ github.run_id }} - TEST_REF: Release - - - name: Upload Test Results Staging - continue-on-error: true - id: upload-staging - env: - TRUNK_API_TOKEN: ${{ secrets.TRUNK_STAGING_API_TOKEN }} - TRUNK_API_ADDRESS: api.trunk-staging.io:8443 - # upload-linter-versions is a hidden command reserved exclusively for uploading - # validated results from the plugins repo. - # daemon must be restarted in order to propagate environment variable - shell: bash - run: | - trunk daemon shutdown - trunk upload-linter-versions --token="$TRUNK_API_TOKEN" results.json - - - name: Upload Test Results Prod - continue-on-error: true - id: upload-prod - env: - TRUNK_API_TOKEN: ${{ secrets.TRUNK_API_TOKEN }} - TRUNK_API_ADDRESS: api.trunk.io:8443 - # upload-linter-versions is a hidden command reserved exclusively for uploading - # validated results from the plugins repo. - # daemon must be restarted in order to propagate environment variable - shell: bash - run: | - trunk daemon shutdown - trunk upload-linter-versions --token="$TRUNK_API_TOKEN" results.json - - # Slack notifications - - name: Slack Notification For Failures - uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 - if: always() && steps.parse.outputs.failures == 'true' - with: - channel-id: ${{ env.SLACK_CHANNEL_ID }} - payload: ${{ steps.parse.outputs.failures-payload }} - env: - SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} - - - name: Slack Notification For Staging Upload Failure - uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 - if: steps.upload-staging.outcome == 'failure' - with: - channel-id: ${{ env.SLACK_CHANNEL_ID }} - payload: | - { - "text": "Upload Failure", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "Failure: " - } - } - ] - } - env: - SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} - - - name: Slack Notification For Prod Upload Failure - uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 - if: steps.upload-prod.outcome == 'failure' - with: - channel-id: ${{ env.SLACK_CHANNEL_ID }} - payload: | - { - "text": "Upload Failure", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "Failure: " - } - } - ] - } - env: - SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} + uses: ./.github/workflows/upload_results.reusable.yaml + secrets: + TRUNKBOT_SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} + TRUNK_STAGING_API_TOKEN: ${{ secrets.TRUNK_STAGING_API_TOKEN }} + TRUNK_API_TOKEN: ${{ secrets.TRUNK_API_TOKEN }} + with: + plugin-version: ${{ needs.linter_tests_release.outputs.plugin-version }} + upload-validated-versions: true + test-type: linter + test-ref: latest release # Run tool tests only on main tool_tests_main: @@ -379,6 +217,14 @@ jobs: fail-fast: false matrix: os: [ubuntu-x64, macOS, windows-latest] + include: + # Normalize the filenames as inputs for ease of parsing + - os: ubuntu-x64 + results-file: ubuntu-latest + - os: macOS + results-file: macos-latest + - os: windows-latest + results-file: windows-latest steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -388,8 +234,32 @@ jobs: - name: Tool Tests ${{ matrix.os }} uses: ./.github/actions/tool_tests with: + append-args: tools -- --json --outputFile=${{ matrix.results-file }}-res.json trunk-token: ${{ secrets.TRUNK_DEBUGGER_TOKEN }} + - name: Upload Test Outputs for Notification Job + # Always run, except when cancelled. + if: (failure() || success()) + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + with: + name: tools-${{ matrix.results-file }}-test-results + path: ${{ matrix.results-file }}-res.json + + upload_tool_tests: + name: Upload Tool Test Results + needs: tool_tests_main + # Still run on test failure + if: always() + uses: ./.github/workflows/upload_results.reusable.yaml + secrets: + TRUNKBOT_SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} + with: + plugin-version: main + results-prefix: tools- + upload-validated-versions: false + test-type: tool + test-ref: main + # Run repo healthcheck tests repo_tests: name: Repo Tests diff --git a/.github/workflows/repo_tests.reusable.yaml b/.github/workflows/repo_tests.reusable.yaml index 9ff8a101f..d289d1def 100644 --- a/.github/workflows/repo_tests.reusable.yaml +++ b/.github/workflows/repo_tests.reusable.yaml @@ -34,7 +34,7 @@ jobs: run: npm ci - name: Run repo tests - run: npm test --ci tests/repo_tests + run: npm test tests/repo_tests --ci env: PLUGINS_TEST_CLI_VERSION: ${{ inputs.cli-version }} PLUGINS_TEST_CLI_PATH: ${{ inputs.cli-path }} diff --git a/.github/workflows/upload_results.reusable.yaml b/.github/workflows/upload_results.reusable.yaml new file mode 100644 index 000000000..27d93925b --- /dev/null +++ b/.github/workflows/upload_results.reusable.yaml @@ -0,0 +1,212 @@ +# Parse test results and +# Send upload notifications +name: Parse Test Results +on: + workflow_call: + inputs: + plugin-version: + required: false + type: string + results-prefix: + required: false + type: string + upload-validated-versions: + required: false + type: boolean + default: false + test-type: + required: false + type: string + default: linter + test-ref: + required: false + type: string + default: latest release + secrets: + TRUNKBOT_SLACK_BOT_TOKEN: + required: true + TRUNK_STAGING_API_TOKEN: + required: false + TRUNK_API_TOKEN: + required: false +permissions: read-all + +jobs: + upload_test_results: + name: Upload Test Results + runs-on: ubuntu-x64 + timeout-minutes: 10 + env: + SLACK_CHANNEL_ID: plugins-notifications + steps: + - name: Checkout + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + + - name: Retrieve Test Outputs ubuntu + id: download-ubuntu + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + continue-on-error: true + with: + name: ${{ inputs.results-prefix }}ubuntu-latest-test-results + + - name: Retrieve Test Outputs macOS + id: download-macos + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + continue-on-error: true + with: + name: ${{ inputs.results-prefix }}macos-latest-test-results + + - name: Retrieve Test Outputs Windows + id: download-windows + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + continue-on-error: true + with: + name: ${{ inputs.results-prefix }}windows-latest-test-results + + - name: Print Test Outputs + continue-on-error: true + shell: bash + run: | + echo "::group::Ubuntu results" + cat "ubuntu-latest-res.json" || echo "missing" + echo "::endgroup::" + + echo "::group::Macos results" + cat "macos-latest-res.json" || echo "missing" + echo "::endgroup::" + + echo "::group::Windows results" + cat "windows-latest-res.json" || echo "missing" + echo "::endgroup::" + + - name: Slack Notification For Missing Artifacts + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + if: + steps.download-ubuntu.outcome == 'failure' || steps.download-macos.outcome == 'failure' || + steps.download-windows.outcome == 'failure' + with: + channel-id: ${{ env.SLACK_CHANNEL_ID }} + payload: | + { + "text": "Artifact Download Failure", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Failure: " + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} + + - name: Setup Node + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 + with: + node-version: 18 + + - name: Install Dependencies + shell: bash + run: npm ci + + - name: Add npm bin to path + shell: bash + run: echo "$PWD/node_modules/.bin" >> "$GITHUB_PATH" + + - name: Parse Test Results + shell: bash + id: parse + run: | + npm run parse + echo "failures=$([[ -f failures.json ]] && echo "true" || echo "false")" >> "$GITHUB_OUTPUT" + echo "failures-payload=$(cat failures.json)" >> "$GITHUB_OUTPUT" + env: + PLUGIN_VERSION: ${{ inputs.plugin-version }} + # Used to format Slack notification for failures + RUN_ID: ${{ github.run_id }} + TEST_REF: ${{ inputs.test-ref }} + TEST_TYPE: ${{ inputs.test-type }} + + - name: Upload Test Results Staging + if: inputs.upload-validated-versions == 'true' + continue-on-error: true + id: upload-staging + env: + TRUNK_API_TOKEN: ${{ secrets.TRUNK_STAGING_API_TOKEN }} + TRUNK_API_ADDRESS: api.trunk-staging.io:8443 + # upload-linter-versions is a hidden command reserved exclusively for uploading + # validated results from the plugins repo. + # daemon must be restarted in order to propagate environment variable + shell: bash + run: | + trunk daemon shutdown + trunk upload-linter-versions --token="$TRUNK_API_TOKEN" results.json + + - name: Upload Test Results Prod + if: inputs.upload-validated-versions == 'true' + continue-on-error: true + id: upload-prod + env: + TRUNK_API_TOKEN: ${{ secrets.TRUNK_API_TOKEN }} + TRUNK_API_ADDRESS: api.trunk.io:8443 + # upload-linter-versions is a hidden command reserved exclusively for uploading + # validated results from the plugins repo. + # daemon must be restarted in order to propagate environment variable + shell: bash + run: | + trunk daemon shutdown + trunk upload-linter-versions --token="$TRUNK_API_TOKEN" results.json + + # Slack notifications + - name: Slack Notification For Failures + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + if: always() && steps.parse.outputs.failures == 'true' + with: + channel-id: ${{ env.SLACK_CHANNEL_ID }} + payload: ${{ steps.parse.outputs.failures-payload }} + env: + SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} + + - name: Slack Notification For Staging Upload Failure + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + if: inputs.upload-validated-versions == 'true' && steps.upload-staging.outcome == 'failure' + with: + channel-id: ${{ env.SLACK_CHANNEL_ID }} + payload: | + { + "text": "Upload Failure", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Failure: " + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} + + - name: Slack Notification For Prod Upload Failure + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + if: inputs.upload-validated-versions == 'true' && steps.upload-prod.outcome == 'failure' + with: + channel-id: ${{ env.SLACK_CHANNEL_ID }} + payload: | + { + "text": "Upload Failure", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Failure: " + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.TRUNKBOT_SLACK_BOT_TOKEN }} diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index dbcd8b45b..e2f4ce930 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -2,7 +2,7 @@ version: 0.1 # version used for local trunk runs and testing cli: - version: 1.17.2 + version: 1.18.0 api: address: api.trunk-staging.io:8443 @@ -79,3 +79,6 @@ tools: enabled: - clangd-indexing-tools@16.0.2 - clangd@16.0.2 + runtimes: + # expose shims in .trunk/tools + - node diff --git a/linters/ansible-lint/test_data/ansible_lint_v6.22.1_FQCN.check.shot b/linters/ansible-lint/test_data/ansible_lint_v6.22.1_FQCN.check.shot new file mode 100644 index 000000000..712d010ce --- /dev/null +++ b/linters/ansible-lint/test_data/ansible_lint_v6.22.1_FQCN.check.shot @@ -0,0 +1,584 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP +// trunk-upgrade-validation:RELEASE + +exports[`Testing linter ansible-lint test FQCN 1`] = ` +{ + "issues": [ + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/demo-aws-launch.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "28", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.wait_for\` or \`ansible.legacy.wait_for\` instead.", + "targetType": "custom", + }, + { + "code": "yaml[truthy]", + "column": "1", + "file": "jboss-standalone/demo-aws-launch.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#yaml[truthy]", + "level": "LEVEL_HIGH", + "line": "5", + "linter": "ansible-lint", + "message": "Truthy value should be one of [false, true]", + "targetType": "custom", + }, + { + "code": "name[play]", + "column": "1", + "file": "jboss-standalone/deploy-application.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#name[play]", + "level": "LEVEL_HIGH", + "line": "4", + "linter": "ansible-lint", + "message": "All plays should be named.", + "targetType": "custom", + }, + { + "code": "role-name", + "column": "1", + "file": "jboss-standalone/roles/java-app", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#role-name", + "level": "LEVEL_HIGH", + "line": "1", + "linter": "ansible-lint", + "message": "Role name java-app does not match \`\`^[a-z][a-z0-9_]*$\`\` pattern.", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/java-app/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "14", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.copy\` or \`ansible.legacy.copy\` instead.", + "targetType": "custom", + }, + { + "code": "risky-file-permissions", + "column": "1", + "file": "jboss-standalone/roles/java-app/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#risky-file-permissions", + "level": "LEVEL_HIGH", + "line": "14", + "linter": "ansible-lint", + "message": "Task/Handler: Copy application WAR file to host", + "targetType": "custom", + }, + { + "code": "fqcn[action]", + "column": "1", + "file": "jboss-standalone/roles/java-app/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action]", + "level": "LEVEL_HIGH", + "line": "19", + "linter": "ansible-lint", + "message": "Action \`jboss\` is not FQCN.", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/java-app/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "2", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.copy\` or \`ansible.legacy.copy\` instead.", + "targetType": "custom", + }, + { + "code": "risky-file-permissions", + "column": "1", + "file": "jboss-standalone/roles/java-app/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#risky-file-permissions", + "level": "LEVEL_HIGH", + "line": "2", + "linter": "ansible-lint", + "message": "Task/Handler: Copy application WAR file to host", + "targetType": "custom", + }, + { + "code": "fqcn[action]", + "column": "1", + "file": "jboss-standalone/roles/java-app/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action]", + "level": "LEVEL_HIGH", + "line": "7", + "linter": "ansible-lint", + "message": "Action \`jboss\` is not FQCN.", + "targetType": "custom", + }, + { + "code": "role-name", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#role-name", + "level": "LEVEL_HIGH", + "line": "1", + "linter": "ansible-lint", + "message": "Role name jboss-standalone does not match \`\`^[a-z][a-z0-9_]*$\`\` pattern.", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/handlers/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "2", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.service\` or \`ansible.legacy.service\` instead.", + "targetType": "custom", + }, + { + "code": "name[casing]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/handlers/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#name[casing]", + "level": "LEVEL_HIGH", + "line": "2", + "linter": "ansible-lint", + "message": "Task/Handler: restart jboss", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/handlers/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "7", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.service\` or \`ansible.legacy.service\` instead.", + "targetType": "custom", + }, + { + "code": "name[casing]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/handlers/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#name[casing]", + "level": "LEVEL_HIGH", + "line": "7", + "linter": "ansible-lint", + "message": "Task/Handler: restart iptables", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "12", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.get_url\` or \`ansible.legacy.get_url\` instead.", + "targetType": "custom", + }, + { + "code": "risky-file-permissions", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#risky-file-permissions", + "level": "LEVEL_HIGH", + "line": "12", + "linter": "ansible-lint", + "message": "Task/Handler: Download JBoss from jboss.org", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "17", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.unarchive\` or \`ansible.legacy.unarchive\` instead.", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "2", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.yum\` or \`ansible.legacy.yum\` instead.", + "targetType": "custom", + }, + { + "code": "yaml[truthy]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#yaml[truthy]", + "level": "LEVEL_HIGH", + "line": "22", + "linter": "ansible-lint", + "message": "Truthy value should be one of [false, true]", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "25", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.command\` or \`ansible.legacy.command\` instead.", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "31", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.template\` or \`ansible.legacy.template\` instead.", + "targetType": "custom", + }, + { + "code": "risky-file-permissions", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#risky-file-permissions", + "level": "LEVEL_HIGH", + "line": "31", + "linter": "ansible-lint", + "message": "Task/Handler: Copying standalone.xml configuration file", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "37", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.group\` or \`ansible.legacy.group\` instead.", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "41", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.user\` or \`ansible.legacy.user\` instead.", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "47", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.file\` or \`ansible.legacy.file\` instead.", + "targetType": "custom", + }, + { + "code": "yaml[truthy]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#yaml[truthy]", + "level": "LEVEL_HIGH", + "line": "53", + "linter": "ansible-lint", + "message": "Truthy value should be one of [false, true]", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "55", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.copy\` or \`ansible.legacy.copy\` instead.", + "targetType": "custom", + }, + { + "code": "yaml[octal-values]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#yaml[octal-values]", + "level": "LEVEL_HIGH", + "line": "59", + "linter": "ansible-lint", + "message": "Forbidden implicit octal value "0755"", + "targetType": "custom", + }, + { + "code": "command-instead-of-module", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#command-instead-of-module", + "level": "LEVEL_HIGH", + "line": "61", + "linter": "ansible-lint", + "message": "Task/Handler: Workaround for systemd bug", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "61", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.shell\` or \`ansible.legacy.shell\` instead.", + "targetType": "custom", + }, + { + "code": "ignore-errors", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#ignore-errors", + "level": "LEVEL_HIGH", + "line": "61", + "linter": "ansible-lint", + "message": "Task/Handler: Workaround for systemd bug", + "targetType": "custom", + }, + { + "code": "no-changed-when", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#no-changed-when", + "level": "LEVEL_HIGH", + "line": "61", + "linter": "ansible-lint", + "message": "Task/Handler: Workaround for systemd bug", + "targetType": "custom", + }, + { + "code": "yaml[truthy]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#yaml[truthy]", + "level": "LEVEL_HIGH", + "line": "63", + "linter": "ansible-lint", + "message": "Truthy value should be one of [false, true]", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "65", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.service\` or \`ansible.legacy.service\` instead.", + "targetType": "custom", + }, + { + "code": "yaml[truthy]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#yaml[truthy]", + "level": "LEVEL_HIGH", + "line": "68", + "linter": "ansible-lint", + "message": "Truthy value should be one of [false, true]", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "71", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.template\` or \`ansible.legacy.template\` instead.", + "targetType": "custom", + }, + { + "code": "name[casing]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#name[casing]", + "level": "LEVEL_HIGH", + "line": "71", + "linter": "ansible-lint", + "message": "Task/Handler: deploy iptables rules", + "targetType": "custom", + }, + { + "code": "risky-file-permissions", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#risky-file-permissions", + "level": "LEVEL_HIGH", + "line": "71", + "linter": "ansible-lint", + "message": "Task/Handler: deploy iptables rules", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "78", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.yum\` or \`ansible.legacy.yum\` instead.", + "targetType": "custom", + }, + { + "code": "fqcn[action-core]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action-core]", + "level": "LEVEL_HIGH", + "line": "84", + "linter": "ansible-lint", + "message": "Use \`ansible.builtin.service\` or \`ansible.legacy.service\` instead.", + "targetType": "custom", + }, + { + "code": "fqcn[action]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#fqcn[action]", + "level": "LEVEL_HIGH", + "line": "90", + "linter": "ansible-lint", + "message": "Action \`firewalld\` is not FQCN.", + "targetType": "custom", + }, + { + "code": "name[casing]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#name[casing]", + "level": "LEVEL_HIGH", + "line": "90", + "linter": "ansible-lint", + "message": "Task/Handler: deploy firewalld rules", + "targetType": "custom", + }, + { + "code": "yaml[truthy]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#yaml[truthy]", + "level": "LEVEL_HIGH", + "line": "92", + "linter": "ansible-lint", + "message": "Truthy value should be one of [false, true]", + "targetType": "custom", + }, + { + "code": "yaml[truthy]", + "column": "1", + "file": "jboss-standalone/roles/jboss-standalone/tasks/main.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#yaml[truthy]", + "level": "LEVEL_HIGH", + "line": "95", + "linter": "ansible-lint", + "message": "Truthy value should be one of [false, true]", + "targetType": "custom", + }, + { + "code": "name[play]", + "column": "1", + "file": "jboss-standalone/site.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#name[play]", + "level": "LEVEL_HIGH", + "line": "4", + "linter": "ansible-lint", + "message": "All plays should be named.", + "targetType": "custom", + }, + ], + "lintActions": [ + { + "command": "lint", + "fileGroupName": "custom", + "linter": "ansible-lint", + "paths": [ + "jboss-standalone", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "lint", + "fileGroupName": "custom", + "linter": "ansible-lint", + "paths": [ + "jboss-standalone", + ], + "upstream": true, + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/ansible-lint/test_data/ansible_lint_v6.22.1_non_FQCN.check.shot b/linters/ansible-lint/test_data/ansible_lint_v6.22.1_non_FQCN.check.shot new file mode 100644 index 000000000..14521e7aa --- /dev/null +++ b/linters/ansible-lint/test_data/ansible_lint_v6.22.1_non_FQCN.check.shot @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP +// trunk-upgrade-validation:RELEASE + +exports[`Testing linter ansible-lint test non_FQCN 1`] = ` +{ + "issues": [ + { + "code": "syntax-check[unknown-module]", + "column": "7", + "file": "jboss-standalone/demo-aws-launch.yml", + "issueClass": "ISSUE_CLASS_NEW", + "issueUrl": "https://ansible-lint.readthedocs.io/en/latest/default_rules.html#syntax-check[unknown-module]", + "level": "LEVEL_HIGH", + "line": "12", + "linter": "ansible-lint", + "message": "couldn't resolve module/action 'ec2'. This often indicates a misspelling, missing collection, or incorrect module path.", + "targetType": "custom", + }, + ], + "lintActions": [ + { + "command": "lint", + "fileGroupName": "custom", + "linter": "ansible-lint", + "paths": [ + "jboss-standalone", + ], + "verb": "TRUNK_VERB_CHECK", + }, + { + "command": "lint", + "fileGroupName": "custom", + "linter": "ansible-lint", + "paths": [ + "jboss-standalone", + ], + "upstream": true, + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/black/plugin.yaml b/linters/black/plugin.yaml index 932910863..698e4b163 100644 --- a/linters/black/plugin.yaml +++ b/linters/black/plugin.yaml @@ -9,7 +9,7 @@ tools: lint: definitions: - name: black - files: [python, jupyter] + files: [python, jupyter, python-interface] commands: - name: format output: rewrite diff --git a/linters/checkov/plugin.yaml b/linters/checkov/plugin.yaml index 74143b43a..935cf569e 100644 --- a/linters/checkov/plugin.yaml +++ b/linters/checkov/plugin.yaml @@ -16,13 +16,13 @@ lint: # on Windows, we need to make sure 'checkov' resolves to 'checkov.cmd' platforms: [windows] run: checkov.cmd -f ${target} -o sarif --output-file-path ${tmpfile}, - output: sarif + output: sarif_uri success_codes: [0, 1] read_output_from: tmp_file is_security: true - name: lint run: checkov -f ${target} -o sarif --output-file-path ${tmpfile}, - output: sarif + output: sarif_uri success_codes: [0, 1] read_output_from: tmp_file is_security: true diff --git a/linters/eslint/eslint.test.ts b/linters/eslint/eslint.test.ts index fb8d0f001..afd88f330 100644 --- a/linters/eslint/eslint.test.ts +++ b/linters/eslint/eslint.test.ts @@ -18,6 +18,14 @@ const preCheck = (driver: TrunkLintDriver) => { moveConfig(driver); // TODO(Tyler): Cache node_modules between runs try { + const trunkYamlPath = ".trunk/trunk.yaml"; + const currentContents = driver.readFile(trunkYamlPath); + const newContents = currentContents.concat(`tools: + runtimes: + - node + `); + driver.writeFile(trunkYamlPath, newContents); + // NOTE(Tyler): It is slower to use the hermetic Trunk installation of the npm shim, but it is safer for more platforms // and avoids unhelpful circular JSON error messages. driver.debug("About to install shims"); @@ -49,6 +57,10 @@ const preCheck = (driver: TrunkLintDriver) => { }, ); driver.debug(install); + if (install.status !== 0) { + driver.debug(install.stdout.toString()); + driver.debug(install.stderr.toString()); + } } catch (err: any) { console.warn("Error installing eslint deps"); console.warn(err); diff --git a/linters/mypy/plugin.yaml b/linters/mypy/plugin.yaml index 150bd8011..db319f832 100644 --- a/linters/mypy/plugin.yaml +++ b/linters/mypy/plugin.yaml @@ -17,6 +17,7 @@ lint: run: mypy --ignore-missing-imports --follow-imports=silent --show-error-codes --show-column-numbers ${target} + run_from: ${root_or_parent_with_any_config} success_codes: [0, 1] stdin: false tools: [mypy] diff --git a/linters/nancy/nancy.test.ts b/linters/nancy/nancy.test.ts index 1328432f4..9289c51a8 100644 --- a/linters/nancy/nancy.test.ts +++ b/linters/nancy/nancy.test.ts @@ -23,5 +23,5 @@ fuzzyLinterCheckTest({ linterName: "nancy", args: "-a -y", preCheck, - fileIssueAssertionCallback: createFuzzyMatcher(() => expectedFileIssues as FileIssue[], 24), + fileIssueAssertionCallback: createFuzzyMatcher(() => expectedFileIssues as FileIssue[], 18), }); diff --git a/linters/nixpkgs-fmt/nixpkgs_fmt.test.ts b/linters/nixpkgs-fmt/nixpkgs_fmt.test.ts index 44607ce8c..fb68b786d 100644 --- a/linters/nixpkgs-fmt/nixpkgs_fmt.test.ts +++ b/linters/nixpkgs-fmt/nixpkgs_fmt.test.ts @@ -10,7 +10,7 @@ const preCheck: TestCallback = (driver) => { const currentContents = driver.readFile(trunkYamlPath); const newContents = currentContents.concat(`runtimes: enabled: - - rust@1.65.0 + - rust@1.71.1 `); driver.writeFile(trunkYamlPath, newContents); }; diff --git a/linters/sourcery/test_data/_plugin.yaml b/linters/sourcery/test_data/_plugin.yaml index c1dbb0c0c..2f459decd 100644 --- a/linters/sourcery/test_data/_plugin.yaml +++ b/linters/sourcery/test_data/_plugin.yaml @@ -16,4 +16,4 @@ lint: - name: SOURCERY_TOKEN value: ${env.SOURCERY_TOKEN} - name: REPO_DIR - value: "${workspace}" \ No newline at end of file + value: "${workspace}" diff --git a/linters/tflint/README.md b/linters/tflint/README.md new file mode 100644 index 000000000..7a0a2972a --- /dev/null +++ b/linters/tflint/README.md @@ -0,0 +1,23 @@ +# Tflint + +`tflint` uses +[config files](https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/config.md) +local to the +[directory](https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/working-directory.md) +being scanned. This means that `tflint` does not have a built-in conception of a global +configuration file. If you want to use a top-level config file or a hidden config in +`.trunk/configs`, please add the following to your `.trunk/trunk.yaml`: + +```yaml +lint: + definitions: + - name: tflint + environment: + - name: TFLINT_CONFIG_FILE + value: ${workspace}/.tflint.hcl # or ${workspace}/.trunk/configs/.tflint.hcl +``` + +## Tflint --init + +`tflint --init` is required to load plugins before tflint can run. At high parallelism with a cold +cache, you may run into some initialization errors. These issues should go away after rerunning. diff --git a/linters/tflint/plugin.yaml b/linters/tflint/plugin.yaml index 0e1bde250..e6a163d20 100644 --- a/linters/tflint/plugin.yaml +++ b/linters/tflint/plugin.yaml @@ -1,7 +1,7 @@ version: 0.1 downloads: - name: tflint - version: 0.35.0 + version: 0.47.0 downloads: # macos arm64 was introduced after this version. - os: macos @@ -20,7 +20,7 @@ tools: - name: tflint download: tflint shims: [tflint] - known_good_version: 0.35.0 + known_good_version: 0.47.0 lint: definitions: - name: tflint @@ -29,12 +29,14 @@ lint: - name: lint output: sarif prepare_run: tflint --init - run: tflint --format=sarif --chdir=${target} --force + run: tflint --format=sarif --force success_codes: [0, 1, 2] read_output_from: stdout + # tflint can only run on the current directory unless --recursive is passed target: ${parent} - run_from: ${root_or_parent_with(.tflint.hcl)} + run_from: ${target_directory} version: ">=0.47.0" + sandbox_type: expanded - name: lint output: sarif prepare_run: tflint --init @@ -53,7 +55,7 @@ lint: - name: GITHUB_TOKEN value: ${env.GITHUB_TOKEN} optional: true - known_good_version: 0.35.0 + known_good_version: 0.47.0 version_command: parse_regex: ${semver} run: tflint --version diff --git a/linters/tflint/test_data/.tflint.hcl b/linters/tflint/test_data/.tflint.hcl deleted file mode 100644 index 16a28d8c9..000000000 --- a/linters/tflint/test_data/.tflint.hcl +++ /dev/null @@ -1,5 +0,0 @@ -plugin "aws" { - enabled = true - version = "0.21.2" - source = "github.com/terraform-linters/tflint-ruleset-aws" -} diff --git a/linters/tflint/test_data/aws.tf b/linters/tflint/test_data/aws.tf index 9218d1984..236eecfd0 100644 --- a/linters/tflint/test_data/aws.tf +++ b/linters/tflint/test_data/aws.tf @@ -1,3 +1,12 @@ -resource "aws_instance" "foo" { - instance_type = "t1.2xlarge" +variable "region" { + type = map(any) + default = { + "a" = { + "region" = "uswest", + } + } +} + +variable "foo" { + default = "bar" } diff --git a/linters/tflint/test_data/tflint_v0.35.0_CUSTOM.check.shot b/linters/tflint/test_data/tflint_v0.35.0_CUSTOM.check.shot deleted file mode 100644 index 02bcf723d..000000000 --- a/linters/tflint/test_data/tflint_v0.35.0_CUSTOM.check.shot +++ /dev/null @@ -1,43 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Testing linter tflint test CUSTOM 1`] = ` -{ - "issues": [ - { - "code": "aws_instance_invalid_type", - "column": "19", - "file": "aws.tf", - "issueClass": "ISSUE_CLASS_NEW", - "level": "LEVEL_HIGH", - "line": "2", - "linter": "tflint", - "message": ""t1.2xlarge" is an invalid value as instance_type", - "targetType": "terraform", - }, - { - "code": "aws_instance_previous_type", - "column": "19", - "file": "aws.tf", - "issueClass": "ISSUE_CLASS_NEW", - "level": "LEVEL_MEDIUM", - "line": "2", - "linter": "tflint", - "message": ""t1.2xlarge" is previous generation instance type.", - "targetType": "terraform", - }, - ], - "lintActions": [ - { - "command": "lint", - "fileGroupName": "terraform", - "linter": "tflint", - "paths": [ - "aws.tf", - ], - "verb": "TRUNK_VERB_CHECK", - }, - ], - "taskFailures": [], - "unformattedFiles": [], -} -`; diff --git a/linters/tflint/test_data/tflint_v0.47.0_CUSTOM.check.shot b/linters/tflint/test_data/tflint_v0.47.0_CUSTOM.check.shot new file mode 100644 index 000000000..f926d928b --- /dev/null +++ b/linters/tflint/test_data/tflint_v0.47.0_CUSTOM.check.shot @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Testing linter tflint test CUSTOM 1`] = ` +{ + "issues": [ + { + "code": "terraform_unused_declarations", + "column": "1", + "file": "test_data/aws.tf", + "level": "LEVEL_MEDIUM", + "line": "1", + "linter": "tflint", + "message": "variable "region" is declared but not used", + "ranges": [ + { + "filePath": "test_data/aws.tf", + "length": "17", + }, + ], + "targetType": "terraform", + }, + { + "code": "terraform_typed_variables", + "column": "1", + "file": "test_data/aws.tf", + "level": "LEVEL_MEDIUM", + "line": "10", + "linter": "tflint", + "message": "\`foo\` variable has no type", + "ranges": [ + { + "filePath": "test_data/aws.tf", + "length": "14", + "offset": "104", + }, + ], + "targetType": "terraform", + }, + { + "code": "terraform_unused_declarations", + "column": "1", + "file": "test_data/aws.tf", + "level": "LEVEL_MEDIUM", + "line": "10", + "linter": "tflint", + "message": "variable "foo" is declared but not used", + "ranges": [ + { + "filePath": "test_data/aws.tf", + "length": "14", + "offset": "104", + }, + ], + "targetType": "terraform", + }, + ], + "lintActions": [ + { + "command": "lint", + "fileGroupName": "terraform", + "linter": "tflint", + "paths": [ + "test_data", + ], + "verb": "TRUNK_VERB_CHECK", + }, + ], + "taskFailures": [], + "unformattedFiles": [], +} +`; diff --git a/linters/tflint/test_data/tflint_v0.35.0_bad_config.check.shot b/linters/tflint/test_data/tflint_v0.47.0_bad_config.check.shot similarity index 100% rename from linters/tflint/test_data/tflint_v0.35.0_bad_config.check.shot rename to linters/tflint/test_data/tflint_v0.47.0_bad_config.check.shot diff --git a/linters/tflint/tflint.test.ts b/linters/tflint/tflint.test.ts index 404c4d3a9..38ddea826 100644 --- a/linters/tflint/tflint.test.ts +++ b/linters/tflint/tflint.test.ts @@ -3,12 +3,6 @@ import { customLinterCheckTest } from "tests"; import { TrunkLintDriver } from "tests/driver"; import { TEST_DATA } from "tests/utils"; -// NOTE: because of copying to root of repo, paths will not be in test_data/ in the test repo. -// const preCheck = (driver: TrunkLintDriver) => { -// driver.moveFile(path.join(TEST_DATA, ".tflint.hcl"), path.join(".tflint.hcl")); -// driver.moveFile(path.join(TEST_DATA, "aws.tf"), path.join("aws.tf")); -// }; - const preCheckBadConfig = (driver: TrunkLintDriver) => { driver.moveFile(path.join(TEST_DATA, "bad.tflint.hcl"), path.join(".tflint.hcl")); driver.moveFile(path.join(TEST_DATA, "aws.tf"), path.join("aws.tf")); @@ -16,7 +10,7 @@ const preCheckBadConfig = (driver: TrunkLintDriver) => { // Running tflint uses calls to GitHub's APIs. If you are concerned about rate limits, disable this test locally. // Because of these rate limits, this test is frequently flaky, so it is disabled for now. -// customLinterCheckTest({ linterName: "tflint", preCheck }); +// customLinterCheckTest({ linterName: "tflint", args: "-a" }); customLinterCheckTest({ linterName: "tflint", testName: "bad_config", diff --git a/plugin.yaml b/plugin.yaml index cb82de53b..4da7e4678 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -1,6 +1,6 @@ version: 0.1 # IfChange -required_trunk_version: ">=1.17.0" +required_trunk_version: ">=1.18.0" # ThenChange tests/repo_tests/config_check.test.ts environments: diff --git a/tests/driver/driver.ts b/tests/driver/driver.ts index 7998823e7..e70ab856a 100644 --- a/tests/driver/driver.ts +++ b/tests/driver/driver.ts @@ -26,6 +26,8 @@ export const executionEnv = (sandbox: string) => { TRUNK_DOWNLOAD_CACHE: DOWNLOAD_CACHE, // This is necessary to prevent launcher collision of non-atomic operations TMPDIR: path.resolve(sandbox, TEMP_SUBDIR), + TRUNK_SEGMENT: "off", + TRUNK_MIXPANEL: "off", }; }; diff --git a/tests/index.ts b/tests/index.ts index ae6fab846..0d0e1462d 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -38,14 +38,27 @@ declare global { } } -const registerVersion = (linterVersion?: string) => { +/** + * Add the version to the test reporter. This only applies when there are multiple workers, + * because the console buffer gets forwarded into the test reporter output. + * @param testType linter or tool, whichever type of test. Used for defensive filtering + * @param linterVersion the linter or tool version that was enabled + */ +const registerVersion = (testType: string, linterVersion?: string) => { // @ts-expect-error: `_buffer` is `private`, see `tests/reporter/reporters.ts` for rationale - // trunk-ignore(eslint): Manual patch is quired here for most reliable implementation - console._buffer?.push({ - message: linterVersion, - origin: expect.getState().currentTestName, - type: "linter-version", - }); + // trunk-ignore(eslint): Manual patch is required here for most reliable implementation + console._buffer?.push( + { + message: linterVersion, + origin: expect.getState().currentTestName, + type: "linter-version", + }, + { + message: testType, + origin: expect.getState().currentTestName, + type: "test-type", + }, + ); }; const baseDebug = Debug("Tests"); @@ -127,7 +140,7 @@ export const setupDriver = ( }); afterEach(() => { - registerVersion(driver.enabledVersion); + registerVersion("linter", driver.enabledVersion); }); return driver; }; @@ -158,6 +171,10 @@ export const setupTrunkToolDriver = ( afterAll(() => { driver.tearDown(); }); + + afterEach(() => { + registerVersion("tool", driver.enabledVersion); + }); return driver; }; @@ -187,6 +204,10 @@ export const setUpTrunkToolDriverForHealthCheck = ( afterAll(() => { driver.tearDown(); }); + + afterEach(() => { + registerVersion("tool", driver.enabledVersion); + }); return driver; }; @@ -225,14 +246,16 @@ export const toolInstallTest = ({ skipTestIf?: (version?: string) => boolean; preCheck?: ToolTestCallback; }) => { - const driver = setUpTrunkToolDriverForHealthCheck(dirName, {}, toolName, toolVersion, preCheck); - conditionalTest(skipTestIf(toolVersion), "tool ", async () => { - const { exitCode, stdout, stderr } = await runInstall(driver, toolName); - expect(exitCode).toEqual(0); - expect(stdout).toContain(toolName); - expect(stdout).toContain(toolVersion); - expect(stderr).toEqual(""); - expect(stdout).not.toContain("Failures:"); + describe(`Testing tool ${toolName}`, () => { + const driver = setUpTrunkToolDriverForHealthCheck(dirName, {}, toolName, toolVersion, preCheck); + conditionalTest(skipTestIf(toolVersion), "tool ", async () => { + const { exitCode, stdout, stderr } = await runInstall(driver, toolName); + expect(exitCode).toEqual(0); + expect(stdout).toContain(toolName); + expect(stdout).toContain(toolVersion); + expect(stderr).toEqual(""); + expect(stdout).not.toContain("Failures:"); + }); }); }; @@ -270,7 +293,7 @@ export const toolTest = ({ skipTestIf?: (version?: string) => boolean; preCheck?: ToolTestCallback; }) => { - describe(toolName, () => { + describe(`Testing tool ${toolName}`, () => { const driver = setupTrunkToolDriver(dirName, {}, toolName, toolVersion, preCheck); testConfigs.forEach(({ command, expectedOut, expectedErr, expectedExitCode }) => { conditionalTest(skipTestIf(toolVersion), command.join(" "), async () => { diff --git a/tests/parse/index.ts b/tests/parse/index.ts index 9250f6ce0..2bc95bfdd 100644 --- a/tests/parse/index.ts +++ b/tests/parse/index.ts @@ -23,6 +23,10 @@ const PLUGIN_VERSION = process.env.PLUGIN_VERSION ?? "v0.0.10"; if (!process.env.PLUGIN_VERSION) { console.log("Environment var `PLUGIN_VERSION` is not set, using fallback `v0.0.10`"); } +const TEST_TYPE = process.env.TEST_TYPE ?? "linter"; +if (!process.env.TEST_TYPE) { + console.log("Environment var `TEST_TYPE` is not set, using fallback `linter`"); +} /** * Convert an OS into the expected file path. @@ -63,7 +67,7 @@ const mergeTestStatuses = ( }; /** - * Combine maps of versions run on each OS. + * Combine maps of versions run on each OS. This consolidates in order to detail mismatches. */ const mergeTestVersions = (original: TestResult, incoming: TestResult) => { Array.from(incoming.allVersions).reduce((accumulator, [os, versions]) => { @@ -108,10 +112,11 @@ const parseResultsJson = (os: TestOS): TestResultSummary => { try { jsonResult = JSON.parse(fs.readFileSync(resultsJsonPath, { encoding: "utf-8" })); } catch (error) { + // The caller is primarily responsible for reporting if a file is missing. console.warn(`Failed to parse ${resultsJsonPath}. Skipping`); return { os, - linters: linterResults, + testResults: linterResults, }; } @@ -119,15 +124,21 @@ const parseResultsJson = (os: TestOS): TestResultSummary => { jsonResult.testResults.forEach((testResult: any) => { testResult.assertionResults.forEach((assertionResult: any) => { const testName: string = assertionResult.ancestorTitles[0]; - const foundLinterName = testName.match(/Testing (linter|formatter) (?.+)/); + const foundLinterName = testName.match(/Testing (linter|formatter|tool) (?.+)/); const linterName = foundLinterName?.groups?.linter; if (!linterName) { console.warn(`Failed to parse test name from ${testName}`); return; } - const version: string = assertionResult.version; const fullTestName: string = assertionResult.fullName; + const testType = assertionResult.testType; + if (testType && testType !== TEST_TYPE) { + console.warn(`Skipping test '${fullTestName}' because is type '${testType}'`); + return; + } + + const version: string = assertionResult.version; const status = parseTestStatus(assertionResult.status); const failedPlatforms = new Set(); if (status == "failed") { @@ -160,7 +171,7 @@ const parseResultsJson = (os: TestOS): TestResultSummary => { console.log(`Parsed results for ${os}`); return { os, - linters: linterResults, + testResults: linterResults, }; }; @@ -169,11 +180,11 @@ const parseResultsJson = (os: TestOS): TestResultSummary => { * same version and all tests must be skipped or pass to be considered passed. */ const mergeTestResultSummaries = (testResults: TestResultSummary[]): TestResultSummary => { - const compositeLinterResults = testResults[0].linters; + const compositeLinterResults = testResults[0].testResults; // Merge all other OS results into the first OS's results testResults.slice(1).forEach((testResult) => { - testResult.linters.forEach((linterResult, linterName) => { + testResult.testResults.forEach((linterResult, linterName) => { const compositeLinterResult = compositeLinterResults.get(linterName); if (compositeLinterResult) { // Merge existing composite record @@ -187,7 +198,7 @@ const mergeTestResultSummaries = (testResults: TestResultSummary[]): TestResultS return { os: "composite", - linters: compositeLinterResults, + testResults: compositeLinterResults, }; }; @@ -211,7 +222,7 @@ const writeFailuresForNotification = (failures: FailedVersion[]) => { type: "section", text: { type: "mrkdwn", - text: `${TEST_REF} Test Failure: _STATUS: ${status}_ ${details}`, + text: `${TEST_REF} Test Failure: _STATUS: ${status}_ ${details}`, }, }; }); @@ -245,7 +256,7 @@ const writeFailuresForNotification = (failures: FailedVersion[]) => { const writeTestResults = (testResults: TestResultSummary) => { const cliVersion = getTrunkVersion(); const pluginVersion = PLUGIN_VERSION; - const validatedVersions = Array.from(testResults.linters).reduce( + const validatedVersions = Array.from(testResults.testResults).reduce( (accumulator: ValidatedVersion[], [linter, { version, testResultStatus }]) => { if (testResultStatus === "passed" && version) { const additionalValidatedVersion: ValidatedVersion = { linter, version }; @@ -255,7 +266,7 @@ const writeTestResults = (testResults: TestResultSummary) => { }, [], ); - const failures = Array.from(testResults.linters).reduce( + const failures = Array.from(testResults.testResults).reduce( ( accumulator: FailedVersion[], [linter, { version, testResultStatus: status, allVersions, failedPlatforms }], @@ -294,7 +305,7 @@ const parseTestResultsAndWrite = () => { // Step 1: Parse each OS's results json file. If one of the expected files does not exist, throw and error out. const testResults = Object.values(TestOS).map((os) => parseResultsJson(os)); const totalParsedTests = testResults.reduce( - (total, testResultsSummary) => total + testResultsSummary.linters.size, + (total, testResultsSummary) => total + testResultsSummary.testResults.size, 0, ); if (totalParsedTests === 0) { diff --git a/tests/repo_tests/config_check.test.ts b/tests/repo_tests/config_check.test.ts index dc8046a87..48ebf11cd 100644 --- a/tests/repo_tests/config_check.test.ts +++ b/tests/repo_tests/config_check.test.ts @@ -25,7 +25,7 @@ describe("Global config health check", () => { setupTrunk: true, // NOTE: This version should be kept compatible in lockstep with the `required_trunk_version` in plugin.yaml // IfChange - trunkVersion: "1.17.0", + trunkVersion: "1.18.0", // ThenChange plugin.yaml }); diff --git a/tests/reporter/reporter.ts b/tests/reporter/reporter.ts index 0a92f2872..9f6b83983 100644 --- a/tests/reporter/reporter.ts +++ b/tests/reporter/reporter.ts @@ -26,13 +26,21 @@ export default class TestReporter implements CustomReporter { onTestResult(_test: Test, testResult: TestResult, _aggregatedResult: AggregatedResult) { // Step 1: Strip console of linter version messages, and populate map const linterVersionMap = new Map(); + const testTypeMap = new Map(); + const filteredConsole = testResult.console?.filter(({ message, origin, type }) => { const isLinterVersionMessage = type === ("linter-version" as LogType); if (isLinterVersionMessage) { // full test name is stored in origin, linter version is stored in message linterVersionMap.set(origin, message); } - return !isLinterVersionMessage; + + const hasTestType = type === ("test-type" as LogType); + if (hasTestType) { + // full test name is stored in origin, test type label is stored in message + testTypeMap.set(origin, message); + } + return !isLinterVersionMessage && !hasTestType; }); testResult.console = filteredConsole?.length ? filteredConsole : undefined; @@ -40,6 +48,7 @@ export default class TestReporter implements CustomReporter { // trunk-ignore-begin(eslint): Unsafe assignment here is expected testResult.testResults = testResult.testResults.map((individualResult: any) => { individualResult.version = linterVersionMap.get(individualResult.fullName); + individualResult.testType = testTypeMap.get(individualResult.fullName); return individualResult; }); // trunk-ignore-end(eslint) diff --git a/tests/types/index.ts b/tests/types/index.ts index cf1a42324..8260d0af7 100644 --- a/tests/types/index.ts +++ b/tests/types/index.ts @@ -159,11 +159,11 @@ export interface TestResult { /** * A summary of all tests run with an individual OS (or the merged result of all OSs). - * Includes a map of linter name to linter test results. + * Includes a map of linter/tool name to linter/tool test results. */ export interface TestResultSummary { os: TestOS | "composite"; - linters: Map; + testResults: Map; } /**