Skip to content

Add global linters

Add global linters #3

Workflow file for this run

name: 'Lint'
# NOTE: This workflow is NOT intended to be a reusable workflow via
# workflow_call. Instead it will be "reused" by configuring via organization
# rulesets as a required workflow.
on:
# Note that for org required workflows:
# "Any filters you specify for the supported events are ignored
# - for example, branches, branches-ignore, paths, types and so on."
# https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#supported-event-triggers
pull_request:
merge_group:
concurrency:
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
cancel-in-progress: true
permissions:
contents: 'read'
statuses: 'write'
jobs:
init:
runs-on: 'ubuntu-latest'
if: |
${{ github.repository != 'google-github-actions/.github' }}
outputs:
lint-targets: '${{ steps.lint-targets.outputs.lint-targets }}'
gomod-dirs: '${{ steps.lint-targets.outputs.gomod-dirs }}'
packagejson-dirs: '${{ steps.lint-targets.outputs.packagejson-dirs }}'
steps:
- name: 'Checkout'
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
with:
fetch-depth: 1
ref: '${{ github.event.pull_request.head.sha }}'
- name: 'Identify Lint Targets'
id: 'lint-targets'
env:
REF: '${{ github.event.pull_request.head.sha }}'
LC_ALL: 'C'
shell: 'bash'
run: |-
set -euo pipefail
# match_files determines if the current git repository has any files
# matching the given pattern. This has been performance tested
# against a shallow checkout of chromium (future changes should be
# tested in the same manner).
match_files() {
local filepattern="${1}"
matches="$(git ls-tree -r --name-only "${REF}" | grep -m 1 -E "${filepattern}")"
code="$?"
if [[ -n "${matches}" ]]; then
# Ignore exit codes because we found a match.
# Exit code 141 and higher may occur because we exit early.
return 0
fi
return "${code}"
}
find_dirs() {
local filepattern="${1}"
git ls-tree -r --name-only "${REF}" | grep -E "${filepattern}" | xargs -I {} bash -c 'echo $(dirname {})' | sort || true
}
to_json() {
local filepaths="${1}"
echo "${filepaths}" | jq -R -s -c 'split("\n")[:-1]' || true
}
# Go linting handled separtely from the rest of linting in order to
# shard linting across each go.mod file.
GOMOD_DIRS="$(find_dirs '(go.mod$)')"
if [[ -n "${GOMOD_DIRS}" ]]; then
GOMOD_DIRS_JSON="$(to_json "${GOMOD_DIRS}")"
echo "::debug::Found go.mod directories: ${GOMOD_DIRS_JSON}"
echo "gomod-dirs=${GOMOD_DIRS_JSON}" >> "${GITHUB_OUTPUT}"
fi
# Typescript linting handled separtely from the rest of linting in order to
# shard linting across each package.json file.
PACKAGEJSON_DIRS="$(find_dirs '(package.json$)')"
if [[ -n "${PACKAGEJSON_DIRS}" ]]; then
PACKAGEJSON_DIRS_JSON="$(to_json "${PACKAGEJSON_DIRS}")"
echo "::debug::Found package.json directories: ${PACKAGEJSON_DIRS_JSON}"
echo "packagejson-dirs=${PACKAGEJSON_DIRS_JSON}" >> "${GITHUB_OUTPUT}"
fi
declare -a TARGETS=()
if match_files '.*(\.dockerfile|Dockerfile)$'; then
TARGETS+=("docker")
fi
if match_files '.github/(actions|workflows)/.*\.(yaml|yml)$'; then
TARGETS+=("github" "ratchet")
fi
if match_files '.*\.(java)$'; then
TARGETS+=("java")
fi
if match_files '.*\.(sh)$'; then
TARGETS+=("shell")
fi
if match_files '.*\.(tf)$'; then
TARGETS+=("terraform")
fi
if match_files '.*\.(yaml|yml)$'; then
TARGETS+=("yaml")
fi
LINT_TARGETS="$(jq --compact-output --null-input '$ARGS.positional' --args -- "${TARGETS[@]}")"
echo "::debug::Found lint targets: ${LINT_TARGETS}"
echo "lint-targets=${LINT_TARGETS}" >> "${GITHUB_OUTPUT}"
lint:
runs-on: 'ubuntu-latest'
needs:
- 'init'
if: |-
${{ needs.init.outputs.lint-targets != '[]' && github.repository != 'google-github-actions/.github' }}
strategy:
fail-fast: false
max-parallel: 100
matrix:
lint-target: '${{ fromJSON(needs.init.outputs.lint-targets) }}'
env:
# Docker
DOCKER_LINT_DIRECTORY: |-
${{ vars.DOCKER_LINT_DIRECTORY || '.' }}
DOCKER_LINT_HADOLINT_CONFIG_URL: |-
${{ vars.DOCKER_LINT_HADOLINT_CONFIG_URL || 'https://raw.githubusercontent.com/abcxyz/actions/main/.hadolint.yml' }}
DOCKER_LINT_HADOLINT_VERSION: |-
${{ vars.DOCKER_LINT_HADOLINT_VERSION || '2.12.0' }}
# GitHub Actions
#
# Note, must not start with GITHUB_
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#naming-conventions-for-configuration-variables
ACTIONS_LINT_ACTIONLINT_VERSION: |-
${{ vars.ACTIONS_LINT_ACTIONLINT_VERSION || '1.7.7' }}
# Java
JAVA_LINT_GOOGLE_JAVA_FORMAT_VERSION: |-
${{ vars.JAVA_LINT_GOOGLE_JAVA_FORMAT_VERSION || '1.25.2' }}
JAVA_LINT_DIRECTORY: |-
${{ vars.JAVA_LINT_DIRECTORY || '.' }}
# Shell
LINT_SHELL_TARGET: |-
${{ vars.LINT_SHELL_TARGET || '.' }}
# Terraform
TERRAFORM_LINT_TERRAFORM_VERSION: |-
${{ vars.TERRAFORM_LINT_TERRAFORM_VERSION }}
TERRAFORM_LINT_DIRECTORY: |-
${{ vars.TERRAFORM_LINT_DIRECTORY || '' }}
# YAML
#
# The URL to a yamllint config file. This is only used if no file is found in the local directory.
YAML_LINT_YAMLLINT_URL: |-
${{ vars.YAML_LINT_YAMLLINT_URL || 'https://raw.githubusercontent.com/google-github-actions/.github/refs/heads/main/.yamllint.yml' }}
YAML_LINT_YAMLLINT_VERSION: |-
${{ vars.YAML_LINT_YAMLLINT_VERSION || '1.37.1' }}
YAML_LINT_TARGET: |-
${{ vars.YAML_LINT_TARGET || '.' }}
steps:
- name: 'Checkout'
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
with:
fetch-depth: 1
- name: 'Lint (Java)'
if: |-
${{ matrix.lint-target == 'java' }}
uses: 'abcxyz/actions/.github/actions/lint-java@main' # ratchet:exclude
with:
google_java_format_version: '${{ env.JAVA_LINT_GOOGLE_JAVA_FORMAT_VERSION }}'
directory: '${{ env.JAVA_LINT_DIRECTORY }}'
github_token: '${{ secrets.GITHUB_TOKEN }}'
- name: 'Lint (Shell)'
if: |-
${{ matrix.lint-target == 'shell' }}
uses: 'abcxyz/actions/.github/actions/lint-shell@main' # ratchet:exclude
with:
target: '${{ env.LINT_SHELL_TARGET }}'
- name: 'Lint (Terraform)'
if: |-
${{ matrix.lint-target == 'terraform' }}
uses: 'abcxyz/actions/.github/actions/lint-terraform@main' # ratchet:exclude
with:
terraform_version: '${{ env.TERRAFORM_LINT_TERRAFORM_VERSION }}'
directory: '${{ env.TERRAFORM_LINT_DIRECTORY }}'
- name: 'Lint (YAML)'
if: |-
${{ matrix.lint-target == 'yaml' }}
uses: 'abcxyz/actions/.github/actions/lint-yaml@main' # ratchet:exclude
with:
yamllint_url: '${{ env.YAML_LINT_YAMLLINT_URL }}'
yamllint_version: '${{ env.YAML_LINT_YAMLLINT_VERSION }}'
target: '${{ env.YAML_LINT_TARGET }}'
- name: 'Lint (Ratchet)'
if: |-
${{ matrix.lint-target == 'ratchet' }}
uses: 'sethvargo/ratchet@main' # ratchet:exclude
with:
files: './.github/actions/**/*.yml ./.github/workflows/*.yml'
- name: 'Lint (GitHub Actions)'
if: |-
${{ matrix.lint-target == 'github' }}
uses: 'abcxyz/actions/.github/actions/lint-github-actions@main' # ratchet:exclude
with:
actionlint_version: '${{ env.ACTIONS_LINT_ACTIONLINT_VERSION }}'
- name: 'Lint (Docker)'
if: |-
${{ matrix.lint-target == 'docker' }}
uses: 'abcxyz/actions/.github/actions/lint-docker@main' # ratchet:exclude
with:
target: '${{ env.DOCKER_LINT_DIRECTORY }}'
hadolint_config_url: '${{ env.DOCKER_LINT_HADOLINT_CONFIG_URL }}'
hadolint_version: '${{ env.DOCKER_LINT_HADOLINT_VERSION }}'
lint-go:
runs-on: 'ubuntu-latest'
needs:
- 'init'
if: |-
${{ needs.init.outputs.gomod-dirs != '' && needs.init.outputs.gomod-dirs != '[]' && github.repository != 'google-github-actions/.github' }}
strategy:
fail-fast: false
max-parallel: 100
matrix:
gomod-dir: '${{ fromJSON(needs.init.outputs.gomod-dirs) }}'
env:
# Go
#
# The version of Go to install and use.
GO_LINT_GO_VERSION: |-
${{ vars.GO_LINT_GO_VERSION }}
GO_LINT_GOLANGCI_URL: |-
${{ vars.GO_LINT_GOLANGCI_URL || 'https://raw.githubusercontent.com/abcxyz/actions/main/default.golangci.yml' }}
GO_LINT_GOLANGCI_LINT_VERSION: |-
${{ vars.GO_LINT_GOLANGCI_LINT_VERSION || 'v1.64' }}
steps:
- name: 'Checkout'
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
with:
fetch-depth: 1
- name: 'Lint (Go Modules)'
if: |-
${{ always() && !cancelled() }}
uses: 'abcxyz/actions/.github/actions/lint-go-modules@main' # ratchet:exclude
with:
go_version: '${{ env.GO_LINT_GO_VERSION }}'
go_version_file: '${{ matrix.gomod-dir }}/go.mod'
golangci_url: '${{ env.GO_LINT_GOLANGCI_URL }}'
directory: '${{ matrix.gomod-dir }}'
golangci_lint_version: '${{ env.GO_LINT_GOLANGCI_LINT_VERSION }}'
- name: 'Lint (Go)'
if: |-
${{ always() && !cancelled() }}
uses: 'abcxyz/actions/.github/actions/lint-go@main' # ratchet:exclude
with:
go_version: '${{ env.GO_LINT_GO_VERSION }}'
go_version_file: '${{ matrix.gomod-dir }}/go.mod'
golangci_url: '${{ env.GO_LINT_GOLANGCI_URL }}'
directory: '${{ matrix.gomod-dir }}'
golangci_lint_version: '${{ env.GO_LINT_GOLANGCI_LINT_VERSION }}'
lint-javascript:
runs-on: 'ubuntu-latest'
needs:
- 'init'
if: |-
${{ needs.init.outputs.packagejson-dirs != '' && needs.init.outputs.packagejson-dirs != '[]' && github.repository != 'google-github-actions/.github' }}
strategy:
fail-fast: false
max-parallel: 100
matrix:
packagejson-dir: '${{ fromJSON(needs.init.outputs.packagejson-dirs) }}'
steps:
- name: 'Checkout'
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
with:
fetch-depth: 1
- name: 'Lint (JavaScript)'
uses: 'abcxyz/actions/.github/actions/lint-javascript@main' # ratchet:exclude
with:
directory: '${{ matrix.packagejson-dir }}'