diff --git a/.github/inditextech-ci-sync-manifest.json b/.github/inditextech-ci-sync-manifest.json index 695af03..f1dd6b9 100644 --- a/.github/inditextech-ci-sync-manifest.json +++ b/.github/inditextech-ci-sync-manifest.json @@ -12,7 +12,7 @@ "creation_year": 2026, "integrity": { "algorithm": "hmac-sha256", - "signature": "b9e6b75852c23cbbed7265826d4808864092fa5f3c232af9e14f617ae71bf96e" + "signature": "35238a1879c3c8acc3d16b5d08af75a17d6814ad0550258924994c155e176b69" }, "managed_by": "InditexTech CI governance", "managed_paths": { @@ -22,11 +22,12 @@ ".github/ISSUE_TEMPLATE/config.yml": "10e5dee4d49aa9e7792ceb4c986160c61f95da1ba00a64bf06e5f1f636cd17ec", ".github/PULL_REQUEST_TEMPLATE.md": "23a0b0ac79a9020ccac9c013582a01f86e2df2ae7f914673583b9a3368313041", ".github/inditextech-ci-go.json": "d854041fe302eab86b99a4ee25414027fda743af14f26db7e8883ab90f60c5fd", - ".github/workflows/code-gomod_go-PR_verify.yml": "7145f586962f35425afbce4cdd00c61f0447d25f1c22ea48c8131f42343f893e", - ".github/workflows/code-gomod_go-sonarcloud-analysis.yml": "65b682f75a2fc0d9e928bc4816d79e1ff6aa68830c47148e23db9943f3a79c10", - ".github/workflows/codeql.yml": "a7ff83d8f89a5e02bfd729939a51a0a512a0f2f3fa7ddf9dc688dfdf41937748", - ".github/workflows/pr-verify.yml": "f52295fbfe81578c0a6459d8b384be569933ac78d32fff370932a8dec6943e46", - ".github/workflows/push-verify.yml": "217097070d0fdef329eae1a7187806a4623d692ce467ca8f1f9a7f56dddb29ed", + ".github/workflows/code-gomod_go-PR_verify.yml": "d148e5af0bc577e6deff1c6c57916de83af3e553fbe197d057ac2e7c6d7e22cb", + ".github/workflows/code-gomod_go-sonarcloud-analysis.yml": "fcb5c72a4ae59a3b429420b1e15eda5c4c0cb4a0aa89c3f4f99eb2e3198dcaca", + ".github/workflows/codeql.yml": "c939b9dea312d987ecc8df2b32c257fb7512b5ca5576c9a73dd0b21fcc770b33", + ".github/workflows/pr-verify.yml": "5628d1d93c09cb761602fcfc3857c325e2dc542cadc159a1edad58d1818d0de0", + ".github/workflows/push-verify.yml": "643f6905fa30284af4a2eabac752293b2e785a5e82449bb6d97950624386829c", + ".github/workflows/scorecard-analysis.yml": "b45c2a0f87801796cefb4363d9f1414891b0734bc07de3f50d4a880e22faea10", "repolinter.json": "0efb305c47a63f03c488a34906fc2d5471c84145c2ff553f2aacf3a41b92e1a1" }, "managed_variables": { @@ -42,7 +43,7 @@ }, "schema_version": 2, "source_digests": { - "base": "09dc9b8d0dcb29288bdb6adeb49cea193b4b79f8de78e55eed00c1c669b5ab5a", - "go": "963229e52884cccc1de8b2251ce7745a8fa5e36970553eeb513a9d1f914d4410" + "base": "c2c2d14df680360bc841fe7fe76552a3d009a60959d08d527526e78e1bbb2b38", + "go": "553836e941384482b416db2684c551a70a9f285f3734c010cfcd133cba266b39" } } diff --git a/.github/workflows/code-gomod_go-PR_verify.yml b/.github/workflows/code-gomod_go-PR_verify.yml index dbd442a..eeccbfc 100644 --- a/.github/workflows/code-gomod_go-PR_verify.yml +++ b/.github/workflows/code-gomod_go-PR_verify.yml @@ -16,9 +16,6 @@ concurrency: env: WORKING_DIRECTORY: ${{ vars.WORKING_DIRECTORY }} - # The project-owned verify command travels in the managed component descriptor, - # never in an Actions variable: the repo owns its build, governance owns only the - # envelope that invokes it. COMPONENT_DESCRIPTOR: .github/inditextech-ci-go.json ASDF_BRANCH_VERSION: '0.18.0' @@ -35,6 +32,27 @@ jobs: persist-credentials: false ref: ${{ github.event.pull_request.head.sha }} + - name: Resolve governed working directory + shell: bash + run: | + set -euo pipefail + if [[ ! -f "$COMPONENT_DESCRIPTOR" ]]; then + echo "::error title=Missing descriptor::${COMPONENT_DESCRIPTOR} not found." + exit 1 + fi + workdir="${WORKING_DIRECTORY:-$(jq -r '.working_directory // empty' "$COMPONENT_DESCRIPTOR")}" + case "$workdir" in + ""|/*|..|../*|*/..|*/../*) + echo "::error title=Invalid working directory::WORKING_DIRECTORY must be a non-empty relative repository path." >&2 + exit 1 + ;; + esac + if [[ ! -d "$workdir" ]]; then + echo "::error title=Missing working directory::'${workdir}' is not a directory in this checkout." >&2 + exit 1 + fi + echo "WORKING_DIRECTORY=${workdir}" >> "$GITHUB_ENV" + - name: Read governed tool versions id: tool-versions working-directory: ${{ env.WORKING_DIRECTORY }} @@ -43,18 +61,13 @@ jobs: set -euo pipefail resolved="" if [[ -f .tool-versions ]]; then - # Validate only real " " 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 @@ -67,7 +80,6 @@ jobs: 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 @@ -95,8 +107,6 @@ jobs: echo "::error title=Missing descriptor::${COMPONENT_DESCRIPTOR} not found." exit 1 fi - # The descriptor is governance-owned and schema-validated; verify_command - # always resolves, but fail closed if it is somehow blank. verify_command="$(jq -er '.verify_command // "make verify"' "$COMPONENT_DESCRIPTOR")" if [[ -z "$verify_command" ]]; then echo "::error title=Empty verify command::descriptor verify_command must not be blank." @@ -115,6 +125,4 @@ jobs: VERIFY_COMMAND: ${{ steps.descriptor.outputs.verify_command }} run: | set -euo pipefail - # The project owns its build; governance only invokes the governed - # verify_command inside the pinned working directory. bash -c "$VERIFY_COMMAND" diff --git a/.github/workflows/code-gomod_go-sonarcloud-analysis.yml b/.github/workflows/code-gomod_go-sonarcloud-analysis.yml index 2c79c89..b3d660a 100644 --- a/.github/workflows/code-gomod_go-sonarcloud-analysis.yml +++ b/.github/workflows/code-gomod_go-sonarcloud-analysis.yml @@ -57,8 +57,6 @@ jobs: shell: bash run: | set -euo pipefail - # Advisory + graceful-skip: opt-in via SONARCLOUD_ENABLED, but a repo whose - # SONAR_TOKEN is not provisioned simply skips analysis instead of failing. if [[ -z "${SONAR_TOKEN:-}" ]]; then echo "::notice title=SonarCloud skipped::SONAR_TOKEN is not configured; skipping advisory analysis." echo "enabled=false" >> "$GITHUB_OUTPUT" @@ -82,18 +80,13 @@ jobs: set -euo pipefail resolved="" if [[ -f .tool-versions ]]; then - # Validate only real " " 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 @@ -106,7 +99,6 @@ jobs: 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 @@ -132,8 +124,6 @@ jobs: shell: bash run: | set -euo pipefail - # Advisory coverage: emit a standard Go coverage profile for Sonar. A module - # without tests still gets a static analysis, so coverage generation never blocks. go test -covermode=atomic -coverprofile=coverage.out ./... \ || echo "::warning title=Coverage unavailable::go test produced no coverage profile; Sonar will analyze without coverage." diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8ecb10c..98b624c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -22,10 +22,10 @@ concurrency: env: WORKING_DIRECTORY: ${{ vars.WORKING_DIRECTORY }} + COMPONENT_DESCRIPTOR: .github/inditextech-ci-go.json 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: >- @@ -48,7 +48,7 @@ jobs: ) runs-on: ubuntu-24.04 permissions: - contents: read # Reads repository metadata to resolve availability. + contents: read outputs: enabled: ${{ steps.resolve.outputs.enabled }} steps: @@ -56,21 +56,19 @@ jobs: 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. + enabled=true elif [ "${seat}" = "enabled" ]; then - enabled=true # A Code Security seat is attached, so analysis is licensed. + enabled=true elif [ "${FORCE_ENABLED:-}" = "true" ]; then - enabled=true # Operator opted this repository in explicitly. + enabled=true fi echo "Code scanning availability: visibility=${visibility} seat=${seat} enabled=${enabled}" echo "enabled=${enabled}" >> "${GITHUB_OUTPUT}" @@ -81,20 +79,16 @@ jobs: if: needs.scanning-availability.outputs.enabled == 'true' runs-on: ubuntu-24.04 permissions: - actions: read # Lets CodeQL inspect workflow metadata. + actions: read contents: read - packages: read # Lets CodeQL resolve package metadata during analysis. - security-events: write # Uploads CodeQL results to Code Scanning. + packages: read + security-events: write 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: @@ -103,6 +97,28 @@ jobs: with: persist-credentials: false + - name: Resolve governed working directory + if: matrix.language == 'go' + shell: bash + run: | + set -euo pipefail + if [[ ! -f "$COMPONENT_DESCRIPTOR" ]]; then + echo "::error title=Missing descriptor::${COMPONENT_DESCRIPTOR} not found." + exit 1 + fi + workdir="${WORKING_DIRECTORY:-$(jq -r '.working_directory // empty' "$COMPONENT_DESCRIPTOR")}" + case "$workdir" in + ""|/*|..|../*|*/..|*/../*) + echo "::error title=Invalid working directory::WORKING_DIRECTORY must be a non-empty relative repository path." >&2 + exit 1 + ;; + esac + if [[ ! -d "$workdir" ]]; then + echo "::error title=Missing working directory::'${workdir}' is not a directory in this checkout." >&2 + exit 1 + fi + echo "WORKING_DIRECTORY=${workdir}" >> "$GITHUB_ENV" + - name: Read governed tool versions id: tool-versions if: matrix.language == 'go' @@ -112,18 +128,13 @@ jobs: set -euo pipefail resolved="" if [[ -f .tool-versions ]]; then - # Validate only real " " 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 @@ -136,7 +147,6 @@ jobs: 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 @@ -150,8 +160,6 @@ jobs: } >> "$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: diff --git a/.github/workflows/pr-verify.yml b/.github/workflows/pr-verify.yml index aa1b55f..ae540dc 100644 --- a/.github/workflows/pr-verify.yml +++ b/.github/workflows/pr-verify.yml @@ -19,7 +19,6 @@ jobs: timeout-minutes: 10 permissions: contents: read - checks: write # Lets reviewdog publish actionlint check results. steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -31,7 +30,7 @@ jobs: - name: Lint GitHub Actions workflows uses: reviewdog/action-actionlint@2085657ab2c7f48c58edcc767fba576f63bea76b # v1.77.0 with: - reporter: github-pr-check + reporter: github-annotations fail_level: error repo-linter: @@ -66,6 +65,15 @@ jobs: conventional-commits: name: Conventional Commits + if: >- + !( + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.user.login == 'inditextechbot[bot]' && + ( + github.head_ref == 'automated/sync-from-main-to-develop' || + startsWith(github.head_ref, 'automated/sync-release-') + ) + ) runs-on: ubuntu-24.04 timeout-minutes: 10 steps: diff --git a/.github/workflows/push-verify.yml b/.github/workflows/push-verify.yml index 6b26ac2..c2ebe6b 100644 --- a/.github/workflows/push-verify.yml +++ b/.github/workflows/push-verify.yml @@ -22,7 +22,6 @@ jobs: timeout-minutes: 10 permissions: contents: read - checks: write # Lets reviewdog publish actionlint check results. steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -32,56 +31,9 @@ jobs: - name: Lint GitHub Actions workflows uses: reviewdog/action-actionlint@2085657ab2c7f48c58edcc767fba576f63bea76b # v1.77.0 with: - reporter: github-check + reporter: github-annotations fail_level: error - scorecard-analysis: - if: >- - github.ref_type == 'branch' && - github.event.repository.visibility == 'public' && - github.ref_name == github.event.repository.default_branch - name: Scorecard analysis - runs-on: ubuntu-24.04 - timeout-minutes: 20 - permissions: - contents: read - id-token: write # Lets Scorecard prove the published result's provenance. - security-events: write # Uploads the generated Scorecard SARIF to Code Scanning. - steps: - - name: "Checkout code" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: false - - - name: "Run analysis" - uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 - with: - results_file: results.sarif - results_format: sarif - # Scorecard team runs a weekly scan of public GitHub repos, - # see https://github.com/ossf/scorecard#public-data. - # Setting `publish_results: true` helps us scale by leveraging your workflow to - # extract the results instead of relying on our own infrastructure to run scans. - # And it's free for you! - publish_results: true - - # Upload the results as artifacts (optional). Commenting out will disable - # uploads of run results in SARIF format to the repository Actions tab. - # https://docs.github.com/en/actions/advanced-guides/storing-workflow-data-as-artifacts - - name: "Upload artifact" - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: SARIF file - path: results.sarif - retention-days: 5 - - - name: Upload SARIF to Code Scanning - uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 - with: - sarif_file: results.sarif - category: scorecard - repo-linter: if: >- github.ref_type == 'branch' && diff --git a/.github/workflows/scorecard-analysis.yml b/.github/workflows/scorecard-analysis.yml new file mode 100644 index 0000000..8974776 --- /dev/null +++ b/.github/workflows/scorecard-analysis.yml @@ -0,0 +1,104 @@ +# SPDX-FileCopyrightText: 2026 INDUSTRIA DE DISEÑO TEXTIL S.A. (INDITEX S.A.) +# SPDX-License-Identifier: Apache-2.0 + +name: Scorecard analysis + +permissions: + contents: read + +on: + push: + +concurrency: + group: scorecard-analysis-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + resolve-code-scanning: + name: Resolve Code Scanning availability + if: >- + github.ref_type == 'branch' && + github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-24.04 + timeout-minutes: 5 + permissions: + contents: read + outputs: + enabled: ${{ steps.scanning-availability.outputs.enabled }} + steps: + - name: Resolve code scanning availability + id: scanning-availability + env: + GH_TOKEN: ${{ github.token }} + FORCE_ENABLED: ${{ vars.CODE_SCANNING_ENABLED }} + REPOSITORY_VISIBILITY: ${{ github.event.repository.visibility }} + run: | + set -euo pipefail + visibility="${REPOSITORY_VISIBILITY}" + seat="unknown" + if ! seat="$(gh api "/repos/${GITHUB_REPOSITORY}" --jq '.security_and_analysis.advanced_security.status // "unknown"')"; then + echo "::notice title=Code Scanning availability unavailable::Unable to query the Advanced Security seat; retaining the SARIF artifact only." + seat="unknown" + fi + enabled=false + if [ "${visibility}" = "public" ] || [ "${seat}" = "enabled" ] || [ "${FORCE_ENABLED:-}" = "true" ]; then + enabled=true + fi + echo "Code scanning availability: visibility=${visibility} seat=${seat} enabled=${enabled}" + echo "enabled=${enabled}" >> "${GITHUB_OUTPUT}" + + scorecard-analysis: + needs: resolve-code-scanning + if: needs.resolve-code-scanning.result == 'success' + name: Scorecard analysis + runs-on: ubuntu-24.04 + timeout-minutes: 20 + permissions: + contents: read + issues: read # Lets Scorecard read issue metadata. + pull-requests: read # Lets Scorecard read pull request metadata. + id-token: write # Lets Scorecard attest result provenance. + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Run analysis + uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + with: + results_file: results.sarif + results_format: sarif + publish_results: ${{ github.event.repository.visibility == 'public' }} + + - name: Upload artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + upload-scorecard-sarif: + name: Upload Scorecard SARIF + needs: [resolve-code-scanning, scorecard-analysis] + if: >- + always() && + needs.resolve-code-scanning.outputs.enabled == 'true' && + needs.scorecard-analysis.result == 'success' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + actions: read # Lets SARIF upload read run metadata. + security-events: write # Lets CodeQL upload Scorecard SARIF. + steps: + - name: Download artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: SARIF file + + - name: Upload SARIF to Code Scanning + uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 + with: + sarif_file: results.sarif + category: scorecard