From 22109db320e66dcdfa4aff87adaab626b6cf9b15 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Wed, 13 Aug 2025 14:56:10 -0400 Subject: [PATCH] chore(ci): add global linter (#6111) Co-authored-by: matt korwel --- .github/workflows/ci.yml | 237 ++++++++++++++++++++++++++++++++++----- .yamllint.yml | 88 +++++++++++++++ 2 files changed, 300 insertions(+), 25 deletions(-) create mode 100644 .yamllint.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1945fa4f..2307b2f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,53 +13,240 @@ on: concurrency: group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}' - cancel-in-progress: ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }} + cancel-in-progress: |- + ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }} + +permissions: + checks: 'write' + contents: 'read' + statuses: 'write' + +defaults: + run: + shell: 'bash' + +env: + ACTIONLINT_VERSION: '1.7.7' + SHELLCHECK_VERSION: '0.11.0' + YAMLLINT_VERSION: '1.35.1' jobs: - lint: - name: Lint - runs-on: ubuntu-latest - permissions: - contents: read # For checkout + # + # Lint: GitHub Actions + # + lint_github_actions: + name: 'Lint (GitHub Actions)' + runs-on: 'ubuntu-latest' steps: - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - name: 'Checkout' + uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 + with: + fetch-depth: 1 - - name: Set up Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + - name: 'Install shellcheck' # Actionlint uses shellcheck + run: |- + mkdir -p "${RUNNER_TEMP}/shellcheck" + curl -sSLo "${RUNNER_TEMP}/.shellcheck.txz" "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" + tar -xf "${RUNNER_TEMP}/.shellcheck.txz" -C "${RUNNER_TEMP}/shellcheck" --strip-components=1 + echo "${RUNNER_TEMP}/shellcheck" >> "${GITHUB_PATH}" + + - name: 'Install actionlint' + run: |- + mkdir -p "${RUNNER_TEMP}/actionlint" + curl -sSLo "${RUNNER_TEMP}/.actionlint.tgz" "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" + tar -xzf "${RUNNER_TEMP}/.actionlint.tgz" -C "${RUNNER_TEMP}/actionlint" + echo "${RUNNER_TEMP}/actionlint" >> "${GITHUB_PATH}" + + # For actionlint, we specifically ignore shellcheck rules that are + # annoying or unhelpful. See the shellcheck action for a description. + - name: 'Run actionlint' + run: |- + actionlint \ + -color \ + -format '{{range $err := .}}::error file={{$err.Filepath}},line={{$err.Line}},col={{$err.Column}}::{{$err.Filepath}}@{{$err.Line}} {{$err.Message}}%0A```%0A{{replace $err.Snippet "\\n" "%0A"}}%0A```\n{{end}}' \ + -ignore 'SC2002:' \ + -ignore 'SC2016:' \ + -ignore 'SC2129:' \ + -ignore 'label ".+" is unknown' + + - name: 'Run ratchet' + uses: 'sethvargo/ratchet@8b4ca256dbed184350608a3023620f267f0a5253' # ratchet:sethvargo/ratchet@v0.11.4 + with: + files: |- + .github/workflows/*.yml + .github/actions/**/*.yml + + # + # Lint: Javascript + # + lint_javascript: + name: 'Lint (Javascript)' + runs-on: 'ubuntu-latest' + steps: + - name: 'Checkout' + uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 + with: + fetch-depth: 1 + + - name: 'Set up Node.js' + uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4.4.0 with: node-version-file: '.nvmrc' cache: 'npm' - - name: Install dependencies - run: npm ci + - name: 'Install dependencies' + run: |- + npm ci - - name: Run formatter check - run: | + - name: 'Run formatter check' + run: |- npm run format git diff --exit-code - - name: Run linter - run: npm run lint:ci + - name: 'Run linter' + run: |- + npm run lint:ci - - name: Run linter on integration tests - run: npx eslint integration-tests --max-warnings 0 + - name: 'Run linter on integration tests' + run: |- + npx eslint integration-tests --max-warnings 0 - - name: Run formatter on integration tests - run: | + - name: 'Run formatter on integration tests' + run: |- npx prettier --check integration-tests git diff --exit-code - - name: Build project - run: npm run build + - name: 'Build project' + run: |- + npm run build - - name: Run type check - run: npm run typecheck + - name: 'Run type check' + run: |- + npm run typecheck + # + # Lint: Shell + # + lint_shell: + name: 'Lint (Shell)' + runs-on: 'ubuntu-latest' + steps: + - name: 'Checkout' + uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 + with: + fetch-depth: 1 + + - name: 'Install shellcheck' + run: |- + mkdir -p "${RUNNER_TEMP}/shellcheck" + curl -sSLo "${RUNNER_TEMP}/.shellcheck.txz" "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" + tar -xf "${RUNNER_TEMP}/.shellcheck.txz" -C "${RUNNER_TEMP}/shellcheck" --strip-components=1 + echo "${RUNNER_TEMP}/shellcheck" >> "${GITHUB_PATH}" + + - name: 'Install shellcheck problem matcher' + run: |- + cat > "${RUNNER_TEMP}/shellcheck/problem-matcher-lint-shell.json" <<"EOF" + { + "problemMatcher": [ + { + "owner": "lint_shell", + "pattern": [ + { + "regexp": "^(.*):(\\\\d+):(\\\\d+):\\\\s+(?:fatal\\\\s+)?(warning|error):\\\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] + } + ] + } + EOF + echo "::add-matcher::${RUNNER_TEMP}/shellcheck/problem-matcher-lint-shell.json" + + # Note that only warning and error severity show up in the github files + # page. So we replace 'style' and 'note' with 'warning' to make it show + # up. + # + # We also try and find all bash scripts even if they don't have an + # explicit extension. + # + # We explicitly ignore the following rules: + # + # - SC2002: This rule suggests using "cmd < file" instead of "cat | cmd". + # While < is more efficient, pipes are much more readable and expected. + # + # - SC2129: This rule suggests grouping multiple writes to a file in + # braces like "{ cmd1; cmd2; } >> file". This is unexpected and less + # readable. + # + # - SC2310: This is an optional warning that only appears with "set -e" + # and when a command is used as a conditional. + - name: 'Run shellcheck' + run: |- + git ls-files | grep -E '^([^.]+|.*\.(sh|zsh|bash))$' | xargs file --mime-type \ + | grep "text/x-shellscript" | awk '{ print substr($1, 1, length($1)-1) }' \ + | xargs shellcheck \ + --check-sourced \ + --enable=all \ + --exclude=SC2002,SC2129,SC2310 \ + --severity=style \ + --format=gcc \ + --color=never | sed -e 's/note:/warning:/g' -e 's/style:/warning:/g' + + # + # Lint: YAML + # + lint_yaml: + name: 'Lint (YAML)' + runs-on: 'ubuntu-latest' + steps: + - name: 'Checkout' + uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 + with: + fetch-depth: 1 + + - name: 'Setup Python' + uses: 'actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065' # ratchet:actions/setup-python@v5 + with: + python-version: '3' + + - name: 'Install yamllint' + run: |- + pip install --user "yamllint==${YAMLLINT_VERSION}" + + - name: 'Run yamllint' + run: |- + git ls-files | grep -E '\.(yaml|yml)' | xargs yamllint --format github + + # + # Lint: All + # + # This is a virtual job that other jobs depend on to wait for all linters to + # finish. It's also used to ensure linting happens on CI via required + # workflows. + lint: + name: 'Lint' + needs: + - 'lint_github_actions' + - 'lint_javascript' + - 'lint_shell' + - 'lint_yaml' + runs-on: 'ubuntu-latest' + steps: + - run: |- + echo 'All linters finished!' + + # + # Test: Node + # test: name: 'Test' runs-on: '${{ matrix.os }}' - needs: 'lint' + needs: + - 'lint' permissions: contents: 'read' checks: 'write' diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 00000000..b4612e07 --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,88 @@ +rules: + anchors: + forbid-duplicated-anchors: true + forbid-undeclared-aliases: true + forbid-unused-anchors: true + + braces: + forbid: 'non-empty' + min-spaces-inside-empty: 0 + max-spaces-inside-empty: 0 + + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: 0 + max-spaces-inside-empty: 0 + + colons: + max-spaces-before: 0 + max-spaces-after: 1 + + commas: + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + + comments: + require-starting-space: true + ignore-shebangs: true + min-spaces-from-content: 1 + + comments-indentation: 'disable' + + document-end: + present: false + + document-start: + present: false + + empty-lines: + max: 2 + max-start: 0 + max-end: 1 + + empty-values: + forbid-in-block-mappings: false + forbid-in-flow-mappings: true + + float-values: + forbid-inf: false + forbid-nan: false + forbid-scientific-notation: false + require-numeral-before-decimal: false + + hyphens: + max-spaces-after: 1 + + indentation: + spaces: 2 + indent-sequences: true + check-multi-line-strings: false + + key-duplicates: {} + + new-line-at-end-of-file: {} + + new-lines: + type: 'unix' + + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: false + + quoted-strings: + quote-type: 'single' + required: true + allow-quoted-quotes: true + + trailing-spaces: {} + + truthy: + allowed-values: ['true', 'false', 'on'] # GitHub Actions uses "on" + check-keys: true + +ignore: + - 'thirdparty/' + - 'third_party/' + - 'vendor/'