chore(ci-governance): adopt Go profile #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: 2026 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.) | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: CodeQL | |
| on: | |
| push: | |
| branches: [main, main-*, develop, develop-*] | |
| pull_request: | |
| branches: [main, main-*, develop, develop-*] | |
| merge_group: | |
| types: [checks_requested] | |
| schedule: | |
| - cron: '17 4 * * 1' | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: codeql-${{ github.repository }}-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| WORKING_DIRECTORY: ${{ vars.WORKING_DIRECTORY }} | |
| ASDF_BRANCH_VERSION: '0.18.0' | |
| jobs: | |
| # Resolves code-scanning availability at runtime so the analysis skips cleanly (green) where no Advanced Security seat is licensed, and starts automatically once a repository becomes public or gains a seat. | |
| scanning-availability: | |
| name: Resolve code scanning availability | |
| if: >- | |
| github.event_name == 'schedule' || | |
| ( | |
| vars.DEVELOPMENT_FLOW == 'trunk-based-development' && | |
| ( | |
| (github.event_name == 'push' && (github.ref_name == 'main' || startsWith(github.ref_name, 'main-'))) || | |
| (github.event_name == 'pull_request' && (github.event.pull_request.base.ref == 'main' || startsWith(github.event.pull_request.base.ref, 'main-'))) || | |
| (github.event_name == 'merge_group' && (github.event.merge_group.base_ref == 'main' || startsWith(github.event.merge_group.base_ref, 'main-'))) | |
| ) | |
| ) || | |
| ( | |
| vars.DEVELOPMENT_FLOW == 'git-flow' && | |
| ( | |
| (github.event_name == 'push' && (github.ref_name == 'develop' || startsWith(github.ref_name, 'develop-'))) || | |
| (github.event_name == 'pull_request' && (github.event.pull_request.base.ref == 'develop' || startsWith(github.event.pull_request.base.ref, 'develop-'))) || | |
| (github.event_name == 'merge_group' && (github.event.merge_group.base_ref == 'develop' || startsWith(github.event.merge_group.base_ref, 'develop-'))) | |
| ) | |
| ) | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read # Reads repository metadata to resolve availability. | |
| outputs: | |
| enabled: ${{ steps.resolve.outputs.enabled }} | |
| steps: | |
| - name: Resolve availability | |
| id: resolve | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| # Manual opt-in (CODE_SCANNING_ENABLED='true') for a seated private/internal repository whose seat status the workflow token cannot read. | |
| FORCE_ENABLED: ${{ vars.CODE_SCANNING_ENABLED }} | |
| run: | | |
| set -euo pipefail | |
| # Visibility is always readable; the security-and-analysis block is admin-token-only, so treat a missing value as unknown. | |
| visibility="$(gh api "/repos/${GITHUB_REPOSITORY}" --jq '.visibility' 2>/dev/null || echo unknown)" | |
| seat="$(gh api "/repos/${GITHUB_REPOSITORY}" \ | |
| --jq '.security_and_analysis.advanced_security.status // "unknown"' 2>/dev/null || echo unknown)" | |
| enabled=false | |
| if [ "${visibility}" = "public" ]; then | |
| enabled=true # Code scanning is always free and available on public repositories. | |
| elif [ "${seat}" = "enabled" ]; then | |
| enabled=true # A Code Security seat is attached, so analysis is licensed. | |
| elif [ "${FORCE_ENABLED:-}" = "true" ]; then | |
| enabled=true # Operator opted this repository in explicitly. | |
| fi | |
| echo "Code scanning availability: visibility=${visibility} seat=${seat} enabled=${enabled}" | |
| echo "enabled=${enabled}" >> "${GITHUB_OUTPUT}" | |
| analyze: | |
| name: Analyze (${{ matrix.language }}) | |
| needs: scanning-availability | |
| if: needs.scanning-availability.outputs.enabled == 'true' | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| actions: read # Lets CodeQL inspect workflow metadata. | |
| contents: read | |
| packages: read # Lets CodeQL resolve package metadata during analysis. | |
| security-events: write # Uploads CodeQL results to Code Scanning. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - language: actions | |
| build-mode: none | |
| # Go has no buildless extractor: CodeQL supports build-mode 'none' only for | |
| # C/C++, C#, Java, and Rust. Go is analyzed with build-mode 'autobuild', which | |
| # compiles the module during init and therefore needs the governed toolchain on | |
| # PATH (set up conditionally below before Initialize CodeQL). | |
| - language: go | |
| build-mode: autobuild | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Read governed tool versions | |
| id: tool-versions | |
| if: matrix.language == 'go' | |
| working-directory: ${{ env.WORKING_DIRECTORY }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| resolved="" | |
| if [[ -f .tool-versions ]]; then | |
| # Validate only real "<plugin> <version>" lines; asdf allows # comments (SPDX/REUSE) and blank lines. | |
| if grep -vE '^[[:space:]]*(#|$)' .tool-versions | grep -qvE '^[a-zA-Z0-9_-]+ [a-zA-Z0-9._+-]+$'; then | |
| echo "::error title=Invalid tool-versions::${WORKING_DIRECTORY}/.tool-versions is malformed." | |
| exit 1 | |
| fi | |
| # Emit only the governed golang pin so asdf ignores project extra pins (e.g. kind). | |
| resolved="$(grep -E '^golang ' .tool-versions || true)" | |
| fi | |
| if [[ -z "$resolved" ]]; then | |
| # No committed golang pin: derive it from the module's own go directive so the | |
| # governed lane follows the product's declared toolchain instead of failing or | |
| # clamping it. A committed pin always wins over the derived one. | |
| if [[ ! -f go.mod ]]; then | |
| echo "::error title=Missing go toolchain::${WORKING_DIRECTORY} has no golang pin in .tool-versions and no go.mod to derive one from." | |
| exit 1 | |
| fi | |
| version="$(sed -nE 's/^toolchain[[:space:]]+go([0-9]+\.[0-9]+(\.[0-9]+)?)[[:space:]]*$/\1/p' go.mod | head -n 1)" | |
| if [[ -z "$version" ]]; then | |
| version="$(sed -nE 's/^go[[:space:]]+([0-9]+\.[0-9]+(\.[0-9]+)?)[[:space:]]*$/\1/p' go.mod | head -n 1)" | |
| fi | |
| if [[ -z "$version" ]]; then | |
| echo "::error title=Missing go toolchain::${WORKING_DIRECTORY}/go.mod declares no usable go version." | |
| exit 1 | |
| fi | |
| # asdf installs an exact patch release; a two-part directive (go 1.21) means .0. | |
| if [[ "$version" != *.*.* ]]; then | |
| version="${version}.0" | |
| fi | |
| resolved="golang ${version}" | |
| echo "::notice title=Derived go toolchain::Using ${resolved} from ${WORKING_DIRECTORY}/go.mod." | |
| fi | |
| { | |
| echo "tool_versions<<EOF" | |
| echo "$resolved" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Set up asdf-managed Go | |
| # autobuild compiles the module during Initialize CodeQL, so the governed Go | |
| # toolchain must already be on PATH; the actions language needs no toolchain. | |
| if: matrix.language == 'go' | |
| uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 # v4.0.1 | |
| with: | |
| tool_versions: ${{ steps.tool-versions.outputs.tool_versions }} | |
| asdf_version: ${{ env.ASDF_BRANCH_VERSION }} | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 | |
| with: | |
| build-mode: ${{ matrix.build-mode }} | |
| languages: ${{ matrix.language }} | |
| - name: Perform CodeQL analysis | |
| uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 | |
| with: | |
| category: /language:${{ matrix.language }} |