Skip to content

Commit c788b21

Browse files
committed
Add global linters
1 parent a3df4ec commit c788b21

1 file changed

Lines changed: 302 additions & 0 deletions

File tree

.github/workflows/lint.yml

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
name: 'Lint'
2+
3+
# NOTE: This workflow is NOT intended to be a reusable workflow via
4+
# workflow_call. Instead it will be "reused" by configuring via organization
5+
# rulesets as a required workflow.
6+
on:
7+
# Note that for org required workflows:
8+
# "Any filters you specify for the supported events are ignored
9+
# - for example, branches, branches-ignore, paths, types and so on."
10+
# 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
11+
pull_request:
12+
merge_group:
13+
14+
concurrency:
15+
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
16+
cancel-in-progress: true
17+
18+
jobs:
19+
init:
20+
runs-on: 'ubuntu-latest'
21+
if: |
22+
${{ github.repository != 'google-github-actions/.github' }}
23+
outputs:
24+
lint-targets: '${{ steps.lint-targets.outputs.lint-targets }}'
25+
gomod-dirs: '${{ steps.lint-targets.outputs.gomod-dirs }}'
26+
packagejson-dirs: '${{ steps.lint-targets.outputs.packagejson-dirs }}'
27+
steps:
28+
- name: 'Checkout'
29+
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
30+
with:
31+
fetch-depth: 1
32+
ref: '${{ github.event.pull_request.head.sha }}'
33+
34+
- name: 'Identify Lint Targets'
35+
id: 'lint-targets'
36+
env:
37+
REF: '${{ github.event.pull_request.head.sha }}'
38+
LC_ALL: 'C'
39+
shell: 'bash'
40+
run: |-
41+
set -euo pipefail
42+
43+
# match_files determines if the current git repository has any files
44+
# matching the given pattern. This has been performance tested
45+
# against a shallow checkout of chromium (future changes should be
46+
# tested in the same manner).
47+
match_files() {
48+
local filepattern="${1}"
49+
matches="$(git ls-tree -r --name-only "${REF}" | grep -m 1 -E "${filepattern}")"
50+
code="$?"
51+
if [[ -n "${matches}" ]]; then
52+
# Ignore exit codes because we found a match.
53+
# Exit code 141 and higher may occur because we exit early.
54+
return 0
55+
fi
56+
return "${code}"
57+
}
58+
59+
find_dirs() {
60+
local filepattern="${1}"
61+
git ls-tree -r --name-only "${REF}" | grep -E "${filepattern}" | xargs -I {} bash -c 'echo $(dirname {})' | sort || true
62+
}
63+
64+
to_json() {
65+
local filepaths="${1}"
66+
echo "${filepaths}" | jq -R -s -c 'split("\n")[:-1]' || true
67+
}
68+
69+
# Go linting handled separtely from the rest of linting in order to
70+
# shard linting across each go.mod file.
71+
GOMOD_DIRS="$(find_dirs '(go.mod$)')"
72+
if [[ -n "${GOMOD_DIRS}" ]]; then
73+
GOMOD_DIRS_JSON="$(to_json "${GOMOD_DIRS}")"
74+
echo "::debug::Found go.mod directories: ${GOMOD_DIRS_JSON}"
75+
echo "gomod-dirs=${GOMOD_DIRS_JSON}" >> "${GITHUB_OUTPUT}"
76+
fi
77+
78+
# Typescript linting handled separtely from the rest of linting in order to
79+
# shard linting across each package.json file.
80+
PACKAGEJSON_DIRS="$(find_dirs '(package.json$)')"
81+
if [[ -n "${PACKAGEJSON_DIRS}" ]]; then
82+
PACKAGEJSON_DIRS_JSON="$(to_json "${PACKAGEJSON_DIRS}")"
83+
echo "::debug::Found package.json directories: ${PACKAGEJSON_DIRS_JSON}"
84+
echo "packagejson-dirs=${PACKAGEJSON_DIRS_JSON}" >> "${GITHUB_OUTPUT}"
85+
fi
86+
87+
declare -a TARGETS=()
88+
if match_files '.*(\.dockerfile|Dockerfile)$'; then
89+
TARGETS+=("docker")
90+
fi
91+
if match_files '.github/(actions|workflows)/.*\.(yaml|yml)$'; then
92+
TARGETS+=("github" "ratchet")
93+
fi
94+
if match_files '.*\.(java)$'; then
95+
TARGETS+=("java")
96+
fi
97+
if match_files '.*\.(sh)$'; then
98+
TARGETS+=("shell")
99+
fi
100+
if match_files '.*\.(tf)$'; then
101+
TARGETS+=("terraform")
102+
fi
103+
if match_files '.*\.(yaml|yml)$'; then
104+
TARGETS+=("yaml")
105+
fi
106+
107+
LINT_TARGETS="$(jq --compact-output --null-input '$ARGS.positional' --args -- "${TARGETS[@]}")"
108+
echo "::debug::Found lint targets: ${LINT_TARGETS}"
109+
echo "lint-targets=${LINT_TARGETS}" >> "${GITHUB_OUTPUT}"
110+
111+
lint:
112+
runs-on: 'ubuntu-latest'
113+
needs:
114+
- 'init'
115+
if: |-
116+
${{ needs.init.outputs.lint-targets != '[]' && github.repository != 'google-github-actions/.github' }}
117+
permissions:
118+
contents: 'read'
119+
strategy:
120+
fail-fast: false
121+
max-parallel: 100
122+
matrix:
123+
lint-target: '${{ fromJSON(needs.init.outputs.lint-targets) }}'
124+
env:
125+
# Docker
126+
DOCKER_LINT_DIRECTORY: |-
127+
${{ vars.DOCKER_LINT_DIRECTORY || '.' }}
128+
DOCKER_LINT_HADOLINT_CONFIG_URL: |-
129+
${{ vars.DOCKER_LINT_HADOLINT_CONFIG_URL || 'https://raw.githubusercontent.com/abcxyz/actions/main/.hadolint.yml' }}
130+
DOCKER_LINT_HADOLINT_VERSION: |-
131+
${{ vars.DOCKER_LINT_HADOLINT_VERSION || '2.12.0' }}
132+
133+
# GitHub Actions
134+
#
135+
# Note, must not start with GITHUB_
136+
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#naming-conventions-for-configuration-variables
137+
ACTIONS_LINT_ACTIONLINT_VERSION: |-
138+
${{ vars.ACTIONS_LINT_ACTIONLINT_VERSION || '1.7.7' }}
139+
140+
# Java
141+
JAVA_LINT_GOOGLE_JAVA_FORMAT_VERSION: |-
142+
${{ vars.JAVA_LINT_GOOGLE_JAVA_FORMAT_VERSION || '1.25.2' }}
143+
JAVA_LINT_DIRECTORY: |-
144+
${{ vars.JAVA_LINT_DIRECTORY || '.' }}
145+
146+
# Shell
147+
LINT_SHELL_TARGET: |-
148+
${{ vars.LINT_SHELL_TARGET || '.' }}
149+
150+
# Terraform
151+
TERRAFORM_LINT_TERRAFORM_VERSION: |-
152+
${{ vars.TERRAFORM_LINT_TERRAFORM_VERSION }}
153+
TERRAFORM_LINT_DIRECTORY: |-
154+
${{ vars.TERRAFORM_LINT_DIRECTORY || '' }}
155+
156+
# YAML
157+
#
158+
# The URL to a yamllint config file. This is only used if no file is found in the local directory.
159+
YAML_LINT_YAMLLINT_URL: |-
160+
${{ vars.YAML_LINT_YAMLLINT_URL || 'https://raw.githubusercontent.com/google-github-actions/.github/refs/heads/main/.yamllint.yml' }}
161+
YAML_LINT_YAMLLINT_VERSION: |-
162+
${{ vars.YAML_LINT_YAMLLINT_VERSION || '1.37.1' }}
163+
YAML_LINT_TARGET: |-
164+
${{ vars.YAML_LINT_TARGET || '.' }}
165+
166+
steps:
167+
- name: 'Checkout'
168+
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
169+
with:
170+
fetch-depth: 1
171+
172+
- name: 'Lint (Java)'
173+
if: |-
174+
${{ matrix.lint-target == 'java' }}
175+
uses: 'abcxyz/actions/.github/actions/lint-java@main' # ratchet:exclude
176+
with:
177+
google_java_format_version: '${{ env.JAVA_LINT_GOOGLE_JAVA_FORMAT_VERSION }}'
178+
directory: '${{ env.JAVA_LINT_DIRECTORY }}'
179+
github_token: '${{ secrets.GITHUB_TOKEN }}'
180+
181+
- name: 'Lint (Shell)'
182+
if: |-
183+
${{ matrix.lint-target == 'shell' }}
184+
uses: 'abcxyz/actions/.github/actions/lint-shell@main' # ratchet:exclude
185+
with:
186+
target: '${{ env.LINT_SHELL_TARGET }}'
187+
188+
- name: 'Lint (Terraform)'
189+
if: |-
190+
${{ matrix.lint-target == 'terraform' }}
191+
uses: 'abcxyz/actions/.github/actions/lint-terraform@main' # ratchet:exclude
192+
with:
193+
terraform_version: '${{ env.TERRAFORM_LINT_TERRAFORM_VERSION }}'
194+
directory: '${{ env.TERRAFORM_LINT_DIRECTORY }}'
195+
196+
- name: 'Lint (YAML)'
197+
if: |-
198+
${{ matrix.lint-target == 'yaml' }}
199+
uses: 'abcxyz/actions/.github/actions/lint-yaml@main' # ratchet:exclude
200+
with:
201+
yamllint_url: '${{ env.YAML_LINT_YAMLLINT_URL }}'
202+
yamllint_version: '${{ env.YAML_LINT_YAMLLINT_VERSION }}'
203+
target: '${{ env.YAML_LINT_TARGET }}'
204+
205+
- name: 'Lint (Ratchet)'
206+
if: |-
207+
${{ matrix.lint-target == 'ratchet' }}
208+
uses: 'sethvargo/ratchet@main' # ratchet:exclude
209+
with:
210+
files: './.github/actions/**/*.yml ./.github/workflows/*.yml'
211+
212+
- name: 'Lint (GitHub Actions)'
213+
if: |-
214+
${{ matrix.lint-target == 'github' }}
215+
uses: 'abcxyz/actions/.github/actions/lint-github-actions@main' # ratchet:exclude
216+
with:
217+
actionlint_version: '${{ env.ACTIONS_LINT_ACTIONLINT_VERSION }}'
218+
219+
- name: 'Lint (Docker)'
220+
if: |-
221+
${{ matrix.lint-target == 'docker' }}
222+
uses: 'abcxyz/actions/.github/actions/lint-docker@main' # ratchet:exclude
223+
with:
224+
target: '${{ env.DOCKER_LINT_DIRECTORY }}'
225+
hadolint_config_url: '${{ env.DOCKER_LINT_HADOLINT_CONFIG_URL }}'
226+
hadolint_version: '${{ env.DOCKER_LINT_HADOLINT_VERSION }}'
227+
228+
lint-go:
229+
runs-on: 'ubuntu-latest'
230+
needs:
231+
- 'init'
232+
if: |-
233+
${{ needs.init.outputs.gomod-dirs != '' && needs.init.outputs.gomod-dirs != '[]' && github.repository != 'google-github-actions/.github' }}
234+
permissions:
235+
contents: 'read'
236+
strategy:
237+
fail-fast: false
238+
max-parallel: 100
239+
matrix:
240+
gomod-dir: '${{ fromJSON(needs.init.outputs.gomod-dirs) }}'
241+
env:
242+
# Go
243+
#
244+
# The version of Go to install and use.
245+
GO_LINT_GO_VERSION: |-
246+
${{ vars.GO_LINT_GO_VERSION }}
247+
GO_LINT_GOLANGCI_URL: |-
248+
${{ vars.GO_LINT_GOLANGCI_URL || 'https://raw.githubusercontent.com/abcxyz/actions/main/default.golangci.yml' }}
249+
GO_LINT_GOLANGCI_LINT_VERSION: |-
250+
${{ vars.GO_LINT_GOLANGCI_LINT_VERSION || 'v1.64' }}
251+
252+
steps:
253+
- name: 'Checkout'
254+
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
255+
with:
256+
fetch-depth: 1
257+
258+
- name: 'Lint (Go Modules)'
259+
if: |-
260+
${{ always() && !cancelled() }}
261+
uses: 'abcxyz/actions/.github/actions/lint-go-modules@main' # ratchet:exclude
262+
with:
263+
go_version: '${{ env.GO_LINT_GO_VERSION }}'
264+
go_version_file: '${{ matrix.gomod-dir }}/go.mod'
265+
golangci_url: '${{ env.GO_LINT_GOLANGCI_URL }}'
266+
directory: '${{ matrix.gomod-dir }}'
267+
golangci_lint_version: '${{ env.GO_LINT_GOLANGCI_LINT_VERSION }}'
268+
269+
- name: 'Lint (Go)'
270+
if: |-
271+
${{ always() && !cancelled() }}
272+
uses: 'abcxyz/actions/.github/actions/lint-go@main' # ratchet:exclude
273+
with:
274+
go_version: '${{ env.GO_LINT_GO_VERSION }}'
275+
go_version_file: '${{ matrix.gomod-dir }}/go.mod'
276+
golangci_url: '${{ env.GO_LINT_GOLANGCI_URL }}'
277+
directory: '${{ matrix.gomod-dir }}'
278+
golangci_lint_version: '${{ env.GO_LINT_GOLANGCI_LINT_VERSION }}'
279+
280+
lint-javascript:
281+
runs-on: 'ubuntu-latest'
282+
needs:
283+
- 'init'
284+
if: |-
285+
${{ needs.init.outputs.packagejson-dirs != '' && needs.init.outputs.packagejson-dirs != '[]' && github.repository != 'google-github-actions/.github' }}
286+
permissions:
287+
contents: 'read'
288+
strategy:
289+
fail-fast: false
290+
max-parallel: 100
291+
matrix:
292+
packagejson-dir: '${{ fromJSON(needs.init.outputs.packagejson-dirs) }}'
293+
steps:
294+
- name: 'Checkout'
295+
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
296+
with:
297+
fetch-depth: 1
298+
299+
- name: 'Lint (JavaScript)'
300+
uses: 'abcxyz/actions/.github/actions/lint-javascript@main' # ratchet:exclude
301+
with:
302+
directory: '${{ matrix.packagejson-dir }}'

0 commit comments

Comments
 (0)