From cef669a52fc566756f06e595cb8446335024d010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Irving=20Mondrag=C3=B3n?= Date: Tue, 14 Apr 2026 14:46:10 +0200 Subject: [PATCH] chore(lint): run all CI linters locally via make targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Irving Mondragón --- .github/linters/.cspell.json | 12 ++--- .github/linters/Dockerfile | 20 ++++++++ .github/linters/VERSION | 1 + .github/linters/entrypoint.sh | 36 +++++++++++++ .github/linters/urunc-dict.txt | 5 +- .github/workflows/linters-npm-image.yml | 67 +++++++++++++++++++++++++ Makefile | 41 +++++++++++++-- 7 files changed, 171 insertions(+), 11 deletions(-) create mode 100644 .github/linters/Dockerfile create mode 100644 .github/linters/VERSION create mode 100755 .github/linters/entrypoint.sh create mode 100644 .github/workflows/linters-npm-image.yml diff --git a/.github/linters/.cspell.json b/.github/linters/.cspell.json index eeb63a41..8df21c5e 100644 --- a/.github/linters/.cspell.json +++ b/.github/linters/.cspell.json @@ -9,12 +9,12 @@ "dictionaries": ["project-dict"], "words": [ ], "ignorePaths": [ - "node_modules", - "vendor", - "dist", - "build", - "out", - "coverage", + "**/node_modules/**", + "**/vendor/**", + "**/dist/**", + "**/build/**", + "**/out/**", + "**/coverage/**", "**/*.lock", "**/*.pb.go", "**/*.pb.cc", diff --git a/.github/linters/Dockerfile b/.github/linters/Dockerfile new file mode 100644 index 00000000..03ecbe5d --- /dev/null +++ b/.github/linters/Dockerfile @@ -0,0 +1,20 @@ +FROM node:22-bookworm-slim@sha256:f3a68cf41a855d227d1b0ab832bed9749469ef38cf4f58182fb8c893bc462383 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends git ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /linters +COPY .github/linters/package.json /linters/package.json +RUN npm install --legacy-peer-deps + +ENV PATH="/linters/node_modules/.bin:${PATH}" +ENV NODE_PATH="/linters/node_modules" + +COPY .github/linters/entrypoint.sh /usr/local/bin/lint-entrypoint.sh +RUN chmod +x /usr/local/bin/lint-entrypoint.sh + +WORKDIR /app + +ENTRYPOINT ["/usr/local/bin/lint-entrypoint.sh"] +CMD ["all"] diff --git a/.github/linters/VERSION b/.github/linters/VERSION new file mode 100644 index 00000000..6e8bf73a --- /dev/null +++ b/.github/linters/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/.github/linters/entrypoint.sh b/.github/linters/entrypoint.sh new file mode 100755 index 00000000..2635c667 --- /dev/null +++ b/.github/linters/entrypoint.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +CSPELL_CONFIG="${CSPELL_CONFIG:-.github/linters/.cspell.json}" +COMMITLINT_CONFIG="${COMMITLINT_CONFIG:-.github/linters/commitlint.config.js}" +COMMITLINT_FROM="${COMMITLINT_FROM:-origin/main}" + +run_cspell() { + cspell --config "${CSPELL_CONFIG}" +} + +run_commitlint() { + # Bind-mounted repo is owned by a different UID than the container user; + # git refuses to operate on it unless the path is marked safe. + git config --global --add safe.directory /app + git fetch origin main --depth=1 >/dev/null 2>&1 \ + || echo "warning: git fetch failed; using cached origin/main if available" >&2 + commitlint --from="${COMMITLINT_FROM}" --config "${COMMITLINT_CONFIG}" +} + +case "${1:-all}" in + cspell) + run_cspell + ;; + commitlint) + run_commitlint + ;; + all|"") + run_cspell + run_commitlint + ;; + *) + echo "Usage: $0 {cspell|commitlint|all}" >&2 + exit 2 + ;; +esac diff --git a/.github/linters/urunc-dict.txt b/.github/linters/urunc-dict.txt index 538649e0..51548211 100644 --- a/.github/linters/urunc-dict.txt +++ b/.github/linters/urunc-dict.txt @@ -405,4 +405,7 @@ Logr onsi ESRCH Prafful -praffq \ No newline at end of file +praffq +commitlint +licenserc +skywalking diff --git a/.github/workflows/linters-npm-image.yml b/.github/workflows/linters-npm-image.yml new file mode 100644 index 00000000..4862017a --- /dev/null +++ b/.github/workflows/linters-npm-image.yml @@ -0,0 +1,67 @@ +name: Build/publish npm linters image + +on: + push: + branches: ["main"] + paths: + - '.github/linters/Dockerfile' + - '.github/linters/entrypoint.sh' + - '.github/linters/package.json' + - '.github/linters/VERSION' + - '.github/workflows/linters-npm-image.yml' + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + build: + runs-on: ubuntu-22.04 + env: + IMAGE: ghcr.io/${{ github.repository }}/linters-npm + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Checkout the repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Resolve image tag from VERSION file + id: version + run: | + version="$(sed 's/^v//' .github/linters/VERSION | tr -d '[:space:]')" + if [ -z "${version}" ]; then + echo "::error::.github/linters/VERSION is empty" + exit 1 + fi + echo "tag=v${version}" >> "$GITHUB_OUTPUT" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: Log in to ghcr.io + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Fail if the tag already exists + run: | + tag="${{ steps.version.outputs.tag }}" + if docker buildx imagetools inspect "${IMAGE}:${tag}" >/dev/null 2>&1; then + echo "::error::${IMAGE}:${tag} already exists. Bump .github/linters/VERSION before merging." + exit 1 + fi + + - name: Build and push + uses: docker/build-push-action@9e436ba9f2d7bcd1d038c8e55d039d37896ddc5d # master + with: + context: . + file: .github/linters/Dockerfile + push: true + tags: ${{ env.IMAGE }}:${{ steps.version.outputs.tag }} + provenance: false diff --git a/Makefile b/Makefile index 19ea6958..db7a1db9 100644 --- a/Makefile +++ b/Makefile @@ -78,11 +78,19 @@ CNTR_TOOL ?= docker CNTR_OPTS ?= run --rm -it # Linking variables -LINT_CNTR_OPTS ?= $(CNTR_OPTS) -v $(CURDIR):/app -w /app +# Don't inherit $(CNTR_OPTS): linters don't need a TTY and -it breaks scripts. +LINT_CNTR_OPTS ?= run --rm -v $(CURDIR):/app -w /app #? LINT_CNTR_IMG The linter image to use (default: golangci/golangci-lint:v1.53.3) LINT_CNTR_IMG ?= golangci/golangci-lint:v2.7 LINT_CNTR_CMD ?= golangci-lint run -v --timeout=5m +LINT_NPM_VERSION ?= $(shell sed 's/^v//' $(CURDIR)/.github/linters/VERSION) +#? LINT_NPM_CNTR_IMG npm linters image (cspell + commitlint) +LINT_NPM_CNTR_IMG ?= ghcr.io/urunc-dev/urunc/linters-npm:v$(LINT_NPM_VERSION) +#? LICENSE_CNTR_IMG license-eye image (Apache-header check) +# Keep in sync with the action SHA in CI. +LICENSE_CNTR_IMG ?= ghcr.io/apache/skywalking-eyes/license-eye:07a607ff5b0759f5ed47306c865aac50fe9b3985 + #? DOCS_CNTR_IMG The mkdocs image to use (default: harbor.nbfc.io/nubificus/urunc/mkdocs:test) DOCS_CNTR_IMG ?= harbor.nbfc.io/nubificus/urunc/mkdocs:latest @@ -208,11 +216,36 @@ clean: rm -fr $(BUILD_DIR) # Linting targets -## lint Run the lint test using a golang container -.PHONY: lint -lint: +## lint_golangci Run golangci-lint in a container +.PHONY: lint_golangci +lint_golangci: $(CNTR_TOOL) $(LINT_CNTR_OPTS) $(LINT_CNTR_IMG) $(LINT_CNTR_CMD) +## lint_cspell Run cspell in the npm linters container +.PHONY: lint_cspell +lint_cspell: + $(CNTR_TOOL) $(LINT_CNTR_OPTS) $(LINT_NPM_CNTR_IMG) cspell + +## lint_commitlint Run commitlint in the npm linters container +.PHONY: lint_commitlint +lint_commitlint: + $(CNTR_TOOL) $(LINT_CNTR_OPTS) $(LINT_NPM_CNTR_IMG) commitlint + +## lint_license Run license-eye (Apache header check) in a container +.PHONY: lint_license +lint_license: + $(CNTR_TOOL) $(LINT_CNTR_OPTS) $(LICENSE_CNTR_IMG) \ + -v info -c .github/linters/licenserc.yml header check + +## lint Run all CI linters (golangci, cspell, commitlint, license-eye) +.PHONY: lint +lint: lint_golangci lint_cspell lint_commitlint lint_license + +## lint_npm_build Build the npm linters image locally from .github/linters/Dockerfile +.PHONY: lint_npm_build +lint_npm_build: + $(CNTR_TOOL) build -t $(LINT_NPM_CNTR_IMG) -f .github/linters/Dockerfile . + # Dcos targets ## docs Build and serve urunc's docs locally .PHONY: docs