Skip to content

Commit fff4234

Browse files
committed
Add global linters
1 parent a3df4ec commit fff4234

1 file changed

Lines changed: 300 additions & 0 deletions

File tree

.github/workflows/lint.yml

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

0 commit comments

Comments
 (0)