diff --git a/.github/actions/codeql-scan/README.md b/.github/actions/codeql-scan/README.md index d880aab..e531a5b 100644 --- a/.github/actions/codeql-scan/README.md +++ b/.github/actions/codeql-scan/README.md @@ -412,7 +412,7 @@ jobs: codeql-go: steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 - uses: NVIDIA/dsx-github-actions/.github/actions/codeql-scan@main with: languages: "go" @@ -558,7 +558,7 @@ jobs: build-and-scan: steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 # CodeQL will trace this build - uses: NVIDIA/dsx-github-actions/.github/actions/codeql-scan@main diff --git a/.github/actions/commitlint/README.md b/.github/actions/commitlint/README.md new file mode 100644 index 0000000..e819c8a --- /dev/null +++ b/.github/actions/commitlint/README.md @@ -0,0 +1,52 @@ +# Commitlint Action + +A GitHub Composite Action that validates commit messages against [Conventional Commits](https://www.conventionalcommits.org/) using `commitlint`. + +## Usage + +```yaml +steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required to access commit history + - name: Lint Commits + uses: NVIDIA/dsx-github-actions/.github/actions/commitlint@main +``` + +### With custom config + +```yaml +steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Lint Commits + uses: NVIDIA/dsx-github-actions/.github/actions/commitlint@main + with: + config-file: '.commitlintrc.js' +``` + +## Inputs + +| Input | Description | Required | Default | +| :--- | :--- | :--- | :--- | +| `config-file` | Path to commitlint config file. If empty, uses default discovery. | `false` | `''` | +| `from` | Lint commits starting from this ref (exclusive). | `false` | `''` | +| `to` | Lint commits up to this ref (inclusive). | `false` | `HEAD` | +| `node-version` | Node.js version to use. | `false` | `20` | + +## Behavior + +1. **Set up Node.js** using `actions/setup-node`. +2. **Install commitlint** packages (`@commitlint/cli` and `@commitlint/config-conventional`). +3. **Determine commit range** automatically: + - If `from` input is provided, uses that ref. + - In PR context: lints commits from the base branch (`origin/$GITHUB_BASE_REF`). + - In push context: lints only the latest commit (`HEAD~1..HEAD`). + - On initial commits or shallow clones: lints `HEAD` only. +4. **Run commitlint** with verbose output. + +## Notes + +- The calling workflow must use `actions/checkout` with `fetch-depth: 0` (or at least enough depth to cover all commits in the PR) for commitlint to access the full commit range. +- npm packages are installed with `--ignore-scripts` for supply chain safety. diff --git a/.github/actions/commitlint/action.yml b/.github/actions/commitlint/action.yml new file mode 100644 index 0000000..3c5e64c --- /dev/null +++ b/.github/actions/commitlint/action.yml @@ -0,0 +1,88 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: 'Commit Lint' +description: 'Validate commit messages against conventional commits using commitlint' + +inputs: + config-file: + description: 'Path to commitlint config file. If empty, uses default discovery (commitlint.config.js, .commitlintrc.js, etc.)' + required: false + default: '' + from: + description: 'Lint commits starting from this ref (exclusive). Defaults to the base of the PR or the parent of HEAD.' + required: false + default: '' + to: + description: 'Lint commits up to this ref (inclusive). Defaults to HEAD.' + required: false + default: 'HEAD' + node-version: + description: 'Node.js version to use' + required: false + default: '20' + +runs: + using: 'composite' + steps: + - name: Set up Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: ${{ inputs.node-version }} + + - name: Install commitlint + shell: bash + run: npm install --no-package-lock --no-save --ignore-scripts @commitlint/cli @commitlint/config-conventional + + - name: Determine commit range + id: range + shell: bash + env: + INPUT_FROM: ${{ inputs.from }} + run: | + if [ -n "$INPUT_FROM" ]; then + echo "from=$INPUT_FROM" >> "$GITHUB_OUTPUT" + elif [ -n "$GITHUB_BASE_REF" ]; then + # PR context: lint commits from base branch + echo "from=origin/$GITHUB_BASE_REF" >> "$GITHUB_OUTPUT" + elif git rev-parse HEAD~1 >/dev/null 2>&1; then + # Push context: lint only the latest commit + echo "from=HEAD~1" >> "$GITHUB_OUTPUT" + else + # Initial commit or shallow clone: skip range, lint HEAD only + echo "from=" >> "$GITHUB_OUTPUT" + fi + + - name: Run commitlint + shell: bash + env: + COMMITLINT_FROM: ${{ steps.range.outputs.from }} + COMMITLINT_TO: ${{ inputs.to }} + COMMITLINT_CONFIG: ${{ inputs.config-file }} + run: | + CONFIG_FLAG="" + if [ -n "$COMMITLINT_CONFIG" ]; then + CONFIG_FLAG="--config $COMMITLINT_CONFIG" + fi + + FROM_FLAG="" + if [ -n "$COMMITLINT_FROM" ]; then + FROM_FLAG="--from $COMMITLINT_FROM" + echo "Linting commits from $COMMITLINT_FROM to $COMMITLINT_TO" + else + echo "Linting commit $COMMITLINT_TO" + fi + + # shellcheck disable=SC2086 + npx commitlint $FROM_FLAG --to "$COMMITLINT_TO" $CONFIG_FLAG --verbose diff --git a/.github/actions/go-lint/README.md b/.github/actions/go-lint/README.md new file mode 100644 index 0000000..871160e --- /dev/null +++ b/.github/actions/go-lint/README.md @@ -0,0 +1,46 @@ +# Go Lint Action + +A GitHub Composite Action that runs a Go linting suite: `golangci-lint`, `go fmt` check, and `go vet`. + +## Usage + +```yaml +steps: + - uses: actions/checkout@v4 + - name: Go Lint + uses: NVIDIA/dsx-github-actions/.github/actions/go-lint@main + with: + working-directory: '.' +``` + +### With vendor mode and custom config + +```yaml +steps: + - uses: actions/checkout@v4 + - name: Go Lint + uses: NVIDIA/dsx-github-actions/.github/actions/go-lint@main + with: + go-flags: '-mod=vendor' + config-path: '.golangci.yml' + golangci-lint-args: '--timeout=5m' +``` + +## Inputs + +| Input | Description | Required | Default | +| :--- | :--- | :--- | :--- | +| `go-version` | Go version to use (e.g., `1.25.5`). If empty, uses `go.mod`. | `false` | `''` | +| `go-version-file` | Path to `go.mod` for version detection. | `false` | `go.mod` | +| `working-directory` | Working directory for lint commands. | `false` | `.` | +| `golangci-lint-version` | golangci-lint version. | `false` | `v2.11` | +| `golangci-lint-args` | Additional arguments for `golangci-lint run`. | `false` | `''` | +| `config-path` | Path to `.golangci.yml` config file. | `false` | `''` | +| `go-flags` | `GOFLAGS` environment variable (e.g., `-mod=vendor`). | `false` | `''` | + +## Behavior + +1. **Set up Go** using `actions/setup-go` with caching enabled. +2. **Check go fmt** by running `gofmt -l` on all `.go` files (excluding `vendor/`). Fails if any files are unformatted. +3. **Run go vet** on all packages. +4. **Run golangci-lint** via `golangci/golangci-lint-action`. diff --git a/.github/actions/go-lint/action.yml b/.github/actions/go-lint/action.yml new file mode 100644 index 0000000..d574df4 --- /dev/null +++ b/.github/actions/go-lint/action.yml @@ -0,0 +1,99 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: 'Go Lint' +description: 'Run Go linting suite: golangci-lint, go fmt check, and go vet' + +inputs: + go-version: + description: 'Go version to use (e.g., 1.25.5). If empty, uses go.mod version.' + required: false + default: '' + go-version-file: + description: 'Path to go.mod for version detection. Used when go-version is empty.' + required: false + default: 'go.mod' + working-directory: + description: 'Working directory for running lint commands' + required: false + default: '.' + golangci-lint-version: + description: 'golangci-lint version to use' + required: false + default: 'v2.11' + golangci-lint-args: + description: 'Additional arguments for golangci-lint run' + required: false + default: '' + config-path: + description: 'Path to .golangci.yml config file. If empty, uses default discovery.' + required: false + default: '' + go-flags: + description: 'GOFLAGS environment variable (e.g., -mod=vendor)' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ inputs.go-version }} + go-version-file: ${{ inputs.go-version == '' && format('{0}/{1}', inputs.working-directory, inputs.go-version-file) || '' }} + cache: true + cache-dependency-path: ${{ inputs.working-directory }}/go.sum + + - name: Check go fmt + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + GOFLAGS: ${{ inputs.go-flags }} + run: | + echo "::group::go fmt" + UNFMT=$(find . -name '*.go' -not -path './vendor/*' -exec gofmt -l {} + 2>&1) + FIND_EXIT=$? + if [ "$FIND_EXIT" -ne 0 ] && [ -z "$UNFMT" ]; then + echo "::error::gofmt check failed unexpectedly (exit code $FIND_EXIT)" + echo "::endgroup::" + exit 1 + fi + if [ -n "$UNFMT" ]; then + echo "::error::The following files are not formatted:" + echo "$UNFMT" + echo "::endgroup::" + echo "Run 'gofmt -w .' to fix formatting." + exit 1 + fi + echo "All files formatted correctly." + echo "::endgroup::" + + - name: Run go vet + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + GOFLAGS: ${{ inputs.go-flags }} + run: | + echo "::group::go vet" + go vet ./... + echo "::endgroup::" + # Note: go vet ./... already skips vendor/ by default + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 + with: + version: ${{ inputs.golangci-lint-version }} + working-directory: ${{ inputs.working-directory }} + args: ${{ inputs.config-path != '' && format('--config {0} {1}', inputs.config-path, inputs.golangci-lint-args) || inputs.golangci-lint-args }} diff --git a/.github/actions/go-test/README.md b/.github/actions/go-test/README.md new file mode 100644 index 0000000..392238a --- /dev/null +++ b/.github/actions/go-test/README.md @@ -0,0 +1,65 @@ +# Go Test Action + +A GitHub Composite Action that runs Go tests with race detection, coverage reporting, and JUnit XML output via `gotestsum`. + +## Usage + +```yaml +steps: + - uses: actions/checkout@v4 + - name: Go Test + uses: NVIDIA/dsx-github-actions/.github/actions/go-test@main +``` + +### With custom packages and flags + +```yaml +steps: + - uses: actions/checkout@v4 + - name: Go Test + uses: NVIDIA/dsx-github-actions/.github/actions/go-test@main + with: + packages: './pkg/...' + test-flags: '-v -count=1 -timeout=10m' + go-flags: '-mod=vendor' + artifact-name: 'test-results-${{ matrix.go-version }}' +``` + +## Inputs + +| Input | Description | Required | Default | +| :--- | :--- | :--- | :--- | +| `go-version` | Go version to use (e.g., `1.25.5`). If empty, uses `go.mod`. | `false` | `''` | +| `go-version-file` | Path to `go.mod` for version detection. | `false` | `go.mod` | +| `working-directory` | Working directory for running tests. | `false` | `.` | +| `packages` | Go packages to test. | `false` | `./...` | +| `race` | Enable race detector. | `false` | `true` | +| `coverage` | Enable coverage reporting. | `false` | `true` | +| `coverage-file` | Coverage output file name. | `false` | `coverage.out` | +| `junit` | Generate JUnit XML report via `gotestsum`. | `false` | `true` | +| `junit-file` | JUnit XML output file name. | `false` | `junit-report.xml` | +| `test-flags` | Additional flags passed to `go test`. | `false` | `-v -count=1` | +| `go-flags` | `GOFLAGS` environment variable (e.g., `-mod=vendor`). | `false` | `''` | +| `gotestsum-version` | `gotestsum` version to install. | `false` | `v1.12.0` | +| `upload-artifacts` | Upload coverage and JUnit reports as workflow artifacts. | `false` | `true` | +| `artifact-name` | Name for uploaded artifact (override to avoid collisions in matrix builds). | `false` | `go-test-results` | + +## Outputs + +| Output | Description | +| :--- | :--- | +| `coverage-file` | Path to coverage output file. | +| `junit-file` | Path to JUnit XML report. | + +## Behavior + +1. **Set up Go** using `actions/setup-go` with caching enabled. +2. **Install gotestsum** (pinned version) if JUnit output is enabled. +3. **Run tests** with configurable race detection, coverage, and JUnit output. +4. **Display coverage summary** showing the total coverage percentage. +5. **Upload artifacts** (coverage and JUnit reports) with 7-day retention. + +## Notes + +- When using matrix builds, set `artifact-name` to a unique value per matrix cell to avoid upload collisions (e.g., `artifact-name: 'test-results-${{ matrix.go-version }}'`). +- All shell inputs are routed through environment variables to prevent expression injection. diff --git a/.github/actions/go-test/action.yml b/.github/actions/go-test/action.yml new file mode 100644 index 0000000..3aaa63e --- /dev/null +++ b/.github/actions/go-test/action.yml @@ -0,0 +1,160 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: 'Go Test' +description: 'Run Go tests with race detection, coverage reporting, and JUnit output' + +inputs: + go-version: + description: 'Go version to use (e.g., 1.25.5). If empty, uses go.mod version.' + required: false + default: '' + go-version-file: + description: 'Path to go.mod for version detection. Used when go-version is empty.' + required: false + default: 'go.mod' + working-directory: + description: 'Working directory for running tests' + required: false + default: '.' + packages: + description: 'Go packages to test (e.g., ./... or ./src/...)' + required: false + default: './...' + race: + description: 'Enable race detector' + required: false + default: 'true' + coverage: + description: 'Enable coverage reporting' + required: false + default: 'true' + coverage-file: + description: 'Coverage output file name' + required: false + default: 'coverage.out' + junit: + description: 'Generate JUnit XML report via gotestsum' + required: false + default: 'true' + junit-file: + description: 'JUnit XML output file name' + required: false + default: 'junit-report.xml' + test-flags: + description: 'Additional flags passed to go test (e.g., -v -count=1 -timeout=10m)' + required: false + default: '-v -count=1' + go-flags: + description: 'GOFLAGS environment variable (e.g., -mod=vendor)' + required: false + default: '' + gotestsum-version: + description: 'gotestsum version to install for JUnit output' + required: false + default: 'c4a0df2e75a225d979a444342dd3db752b53619f' # v1.13.0 + upload-artifacts: + description: 'Upload coverage and JUnit reports as workflow artifacts' + required: false + default: 'true' + artifact-name: + description: 'Name for uploaded artifact (override to avoid collisions in matrix builds)' + required: false + default: 'go-test-results' + +outputs: + coverage-file: + description: 'Path to coverage output file' + value: ${{ inputs.working-directory }}/${{ inputs.coverage-file }} + junit-file: + description: 'Path to JUnit XML report' + value: ${{ inputs.working-directory }}/${{ inputs.junit-file }} + +runs: + using: 'composite' + steps: + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ inputs.go-version }} + go-version-file: ${{ inputs.go-version == '' && format('{0}/{1}', inputs.working-directory, inputs.go-version-file) || '' }} + cache: true + cache-dependency-path: ${{ inputs.working-directory }}/go.sum + + - name: Install gotestsum + if: inputs.junit == 'true' + shell: bash + env: + GOTESTSUM_VERSION: ${{ inputs.gotestsum-version }} + run: go install "gotest.tools/gotestsum@${GOTESTSUM_VERSION}" + + - name: Run tests + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + GOFLAGS: ${{ inputs.go-flags }} + INPUT_TEST_FLAGS: ${{ inputs.test-flags }} + INPUT_RACE: ${{ inputs.race }} + INPUT_COVERAGE: ${{ inputs.coverage }} + INPUT_COVERAGE_FILE: ${{ inputs.coverage-file }} + INPUT_PACKAGES: ${{ inputs.packages }} + INPUT_JUNIT: ${{ inputs.junit }} + INPUT_JUNIT_FILE: ${{ inputs.junit-file }} + run: | + # Build test flags (env vars prevent shell injection from ${{ }} interpolation) + TEST_FLAGS="$INPUT_TEST_FLAGS" + if [ "$INPUT_RACE" = "true" ]; then + TEST_FLAGS="-race ${TEST_FLAGS}" + fi + if [ "$INPUT_COVERAGE" = "true" ]; then + TEST_FLAGS="-coverprofile=${INPUT_COVERAGE_FILE} -covermode=atomic ${TEST_FLAGS}" + fi + + # shellcheck disable=SC2086 + echo "::group::go test ${TEST_FLAGS} ${INPUT_PACKAGES}" + if [ "$INPUT_JUNIT" = "true" ]; then + # shellcheck disable=SC2086 + gotestsum \ + --junitfile "$INPUT_JUNIT_FILE" \ + --format testdox \ + -- ${TEST_FLAGS} ${INPUT_PACKAGES} + else + # shellcheck disable=SC2086 + go test ${TEST_FLAGS} ${INPUT_PACKAGES} + fi + echo "::endgroup::" + + - name: Display coverage summary + if: inputs.coverage == 'true' + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + INPUT_COVERAGE_FILE: ${{ inputs.coverage-file }} + run: | + if [ -f "$INPUT_COVERAGE_FILE" ]; then + echo "::group::Coverage Summary" + go tool cover -func="$INPUT_COVERAGE_FILE" | tail -1 + echo "::endgroup::" + fi + + - name: Upload test artifacts + if: inputs.upload-artifacts == 'true' && always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: ${{ inputs.artifact-name }} + path: | + ${{ inputs.working-directory }}/${{ inputs.coverage-file }} + ${{ inputs.working-directory }}/${{ inputs.junit-file }} + if-no-files-found: ignore + retention-days: 7 diff --git a/.github/actions/license-headers/README.md b/.github/actions/license-headers/README.md new file mode 100644 index 0000000..d2cc8ca --- /dev/null +++ b/.github/actions/license-headers/README.md @@ -0,0 +1,46 @@ +# License Headers Action + +A GitHub Composite Action that checks (and optionally adds) SPDX license headers using `addlicense`. + +## Usage + +```yaml +steps: + - uses: actions/checkout@v4 + - name: Check License Headers + uses: NVIDIA/dsx-github-actions/.github/actions/license-headers@main +``` + +### Auto-add missing headers + +```yaml +steps: + - uses: actions/checkout@v4 + - name: Add License Headers + uses: NVIDIA/dsx-github-actions/.github/actions/license-headers@main + with: + check-only: 'false' + paths: 'src/ pkg/ cmd/' + ignore: 'vendor/**,testdata/**' +``` + +## Inputs + +| Input | Description | Required | Default | +| :--- | :--- | :--- | :--- | +| `license` | License type (`apache`, `mit`, `bsd`). | `false` | `apache` | +| `copyright-holder` | Copyright holder string. | `false` | `NVIDIA CORPORATION & AFFILIATES. All rights reserved.` | +| `year` | Copyright year. | `false` | `2026` | +| `paths` | Space-separated paths to check. | `false` | `.` | +| `ignore` | Comma-separated glob patterns to ignore. | `false` | `vendor/**` | +| `check-only` | Only check headers (fail if missing). Set to `false` to auto-add. | `false` | `true` | +| `addlicense-version` | `addlicense` tool version. | `false` | `v1.2.0` | +| `go-version` | Go version for installing `addlicense`. If empty, uses existing Go. | `false` | `''` | + +## Behavior + +1. **Set up Go** (optional, only if `go-version` is specified). +2. **Install addlicense** at the pinned version. +3. **Check or add** license headers depending on the `check-only` flag. + - Check mode: fails if any files are missing headers. + - Add mode: automatically inserts missing headers. diff --git a/.github/actions/license-headers/action.yml b/.github/actions/license-headers/action.yml new file mode 100644 index 0000000..e7135ec --- /dev/null +++ b/.github/actions/license-headers/action.yml @@ -0,0 +1,109 @@ +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: 'License Headers Check' +description: 'Check (and optionally add) SPDX license headers using addlicense' + +inputs: + license: + description: 'License type (apache, mit, bsd)' + required: false + default: 'apache' + copyright-holder: + description: 'Copyright holder string' + required: false + default: 'NVIDIA CORPORATION & AFFILIATES. All rights reserved.' + year: + description: 'Copyright year' + required: false + default: '2026' + paths: + description: 'Space-separated paths to check (e.g., "src/ pkg/ cmd/")' + required: false + default: '.' + ignore: + description: 'Comma-separated glob patterns to ignore (e.g., "vendor/**,tests/**")' + required: false + default: 'vendor/**' + check-only: + description: 'Only check headers (fail if missing). Set to false to auto-add missing headers.' + required: false + default: 'true' + addlicense-version: + description: 'addlicense tool version' + required: false + default: 'b289835c0741ba2fb92dd5f61af070e79c69a55e' # v1.2.0 + go-version: + description: 'Go version for installing addlicense. If empty, uses existing Go installation.' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Set up Go + if: inputs.go-version != '' + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ inputs.go-version }} + cache: false + + - name: Install addlicense + shell: bash + env: + ADDLICENSE_VERSION: ${{ inputs.addlicense-version }} + run: go install "github.com/google/addlicense@${ADDLICENSE_VERSION}" + + - name: Check license headers + shell: bash + env: + INPUT_LICENSE: ${{ inputs.license }} + INPUT_COPYRIGHT: ${{ inputs.copyright-holder }} + INPUT_YEAR: ${{ inputs.year }} + INPUT_IGNORE: ${{ inputs.ignore }} + INPUT_CHECK_ONLY: ${{ inputs.check-only }} + INPUT_PATHS: ${{ inputs.paths }} + run: | + # Build command as an array to preserve flag boundaries + CMD=(addlicense -l "$INPUT_LICENSE" -c "$INPUT_COPYRIGHT" -s=only -y "$INPUT_YEAR") + + # Add ignore flags + IFS=',' read -ra PATTERNS <<< "$INPUT_IGNORE" + for pattern in "${PATTERNS[@]}"; do + pattern=$(echo "$pattern" | xargs) # trim whitespace + if [ -n "$pattern" ]; then + CMD+=(-ignore "$pattern") + fi + done + + # Add check flag + if [ "$INPUT_CHECK_ONLY" = "true" ]; then + CMD+=(-check) + echo "Checking license headers (check-only mode)..." + else + echo "Adding missing license headers..." + fi + + # Add paths last (positional args must come after flags) + # shellcheck disable=SC2206 + CMD+=($INPUT_PATHS) + + # Run + "${CMD[@]}" + + if [ "$INPUT_CHECK_ONLY" = "true" ]; then + echo "License headers check passed." + else + echo "Missing license headers added successfully." + fi diff --git a/.github/actions/semantic-release/README.md b/.github/actions/semantic-release/README.md index 3cca1ee..a8f9868 100644 --- a/.github/actions/semantic-release/README.md +++ b/.github/actions/semantic-release/README.md @@ -291,7 +291,7 @@ steps: ### Example 7: NPM Package Publishing ```yaml -- uses: actions/setup-node@v4 +- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: "20" diff --git a/README.md b/README.md index d259ace..96a4417 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ A collection of reusable GitHub Actions for standardizing CI/CD workflows across | [docker-build](.github/actions/docker-build/) | Docker Buildx build/push wrapper | Build/push multi-arch OCI images | | [git-tag](.github/actions/git-tag/) | Create and push git tag | Tagging releases | | [slack-notify](.github/actions/slack-notify/) | Send notifications to Slack | CI/CD status notifications | +| [go-lint](.github/actions/go-lint/) | Go linting (golangci-lint, fmt, vet) | Go code quality checks | +| [go-test](.github/actions/go-test/) | Go tests with coverage and JUnit | Go test execution and reporting | +| [license-headers](.github/actions/license-headers/) | SPDX license header checks | License compliance | +| [commitlint](.github/actions/commitlint/) | Conventional commit validation | Commit message enforcement | ## ♻️ Available Workflows @@ -107,6 +111,10 @@ This reusable workflow wraps `skopeo copy`, so it copies the entire manifest lis - [Resource Push NGC Action](.github/actions/resource-push-ngc/README.md) - [Docker Build Action](.github/actions/docker-build/README.md) - [Slack Notify Action](.github/actions/slack-notify/README.md) +- [Go Lint Action](.github/actions/go-lint/README.md) +- [Go Test Action](.github/actions/go-test/README.md) +- [License Headers Action](.github/actions/license-headers/README.md) +- [Commitlint Action](.github/actions/commitlint/README.md) - [Workflows Guide](.github/workflows/README.md) ## 🎯 Features @@ -309,7 +317,11 @@ If CI still fails, execute `pre-commit run actionlint --all-files` or `pre-commi │ ├── semantic-release/ # Automated versioning and releases │ ├── resource-push-ngc/ # NGC resources publishing │ ├── git-tag/ # Create and push git tag -│ └── slack-notify/ # Send Slack notifications +│ ├── slack-notify/ # Send Slack notifications +│ ├── go-lint/ # Go linting (golangci-lint, fmt, vet) +│ ├── go-test/ # Go tests with coverage and JUnit +│ ├── license-headers/ # SPDX license header checks +│ └── commitlint/ # Conventional commit validation └── workflows/ ├── release.yml # Automatic semantic versioning ├── promote-image.yml # Promote image across registries