diff --git a/.github/inditextech-ci-sync-manifest.json b/.github/inditextech-ci-sync-manifest.json index 3df27dd..a19abb6 100644 --- a/.github/inditextech-ci-sync-manifest.json +++ b/.github/inditextech-ci-sync-manifest.json @@ -17,7 +17,7 @@ "creation_year": 2026, "integrity": { "algorithm": "hmac-sha256", - "signature": "0e3eba3359012eb8df06afa4bbb44ebdb0778cf75acc8566bdd6f092888b7fcc" + "signature": "1726fed4e2095a5d6fffdd98e61fc426f5137247194239d143c19c32a382395f" }, "managed_by": "InditexTech CI governance", "managed_paths": { @@ -27,14 +27,14 @@ ".github/ISSUE_TEMPLATE/config.yml": "10e5dee4d49aa9e7792ceb4c986160c61f95da1ba00a64bf06e5f1f636cd17ec", ".github/PULL_REQUEST_TEMPLATE.md": "23a0b0ac79a9020ccac9c013582a01f86e2df2ae7f914673583b9a3368313041", ".github/inditextech-ci-node.json": "72449d1243d714b1ef9bd615e6dc67d0ec0b25f0c73440a08fa34d1e498864ac", - ".github/workflows/code-npm_node-PR_verify.yml": "89e11bc8aae9ec44ab47222cc570c27dffb8b426e88665598ea7065a9cc95d54", - ".github/workflows/code-npm_node-publish-release-and-snapshot.yml": "adf500b9ac304359d560bbeab1176734c8b34b6571c979d80498a7b49064a8c1", - ".github/workflows/code-npm_node-release-core.yml": "f58968dc8465418613deffc38d1077ddaed9921de3e64f766113eb78f2df142e", - ".github/workflows/code-npm_node-sonarcloud-analysis.yml": "1a4227164d591980b02baddcc0814b735013b162d70cc3909401687287119b5e", + ".github/workflows/code-npm_node-PR_verify.yml": "2d4430d765f4a7539ae64fc75cb8254cc09fdcaa7a2a805faada3865887262ce", + ".github/workflows/code-npm_node-publish-release-and-snapshot.yml": "ad2241467f491c6d158a1dcb85f41f31a76d2cf786f4ea5b198c75e4b8fa8a11", + ".github/workflows/code-npm_node-release-core.yml": "da11bed8544814e98d34ce4a20aeec3818fa3e2ab4f738027a64163d9de19709", + ".github/workflows/code-npm_node-sonarcloud-analysis.yml": "d2fb005c98eb0b68081dcce9611b4cf70e905847119839316fa9034bcac73213", ".github/workflows/code-release_preview.yml": "4e2e95d60a498eb12d97ba5c8330b1173f04b02c807140beddad06a6e225b166", ".github/workflows/codeql.yml": "94084661946332025fbc91374dba51e0c82ab53f95de717c491ba63f384fad75", - ".github/workflows/pr-verify.yml": "dfcff10f7b2ff38a112ea4051e79462f4f2c6ad1a82035db3145327e79a34fbb", - ".github/workflows/push-verify.yml": "49e6e9c6a7015ea59a2bc831982b2fc4bfd1d5721302f6041170b4a15b7d56e1", + ".github/workflows/pr-verify.yml": "5628d1d93c09cb761602fcfc3857c325e2dc542cadc159a1edad58d1818d0de0", + ".github/workflows/push-verify.yml": "643f6905fa30284af4a2eabac752293b2e785a5e82449bb6d97950624386829c", ".github/workflows/scorecard-analysis.yml": "b45c2a0f87801796cefb4363d9f1414891b0734bc07de3f50d4a880e22faea10", ".github/workflows/sync-to-develop.yml": "2f80696aeb3aa35a47944535539ca1887a8cea0cf11dd62e1a103497aa395acc", ".tool-versions": "5e7b05edf5d8df174df5dd99d012e57666ec9923ac8506df7dc7753ba41815fd", @@ -56,7 +56,7 @@ "retained_paths": {}, "schema_version": 2, "source_digests": { - "base": "77bf82cf50d017f83cb2c98448f5abd89c838e085a747c1af3569034f6b38997", - "node": "1540216f127bb4ab46877c3ea2212835327c9af870d055afb1bfbaf82deebfcd" + "base": "d0ba4f054c8e0a0d6c828d02718efc01251f7897cfcbe864d3c76ef304dc4a13", + "node": "da5aefad643c8b4ce2a07646e6216366993ab6296603af9bc9165b195f193c95" } } diff --git a/.github/workflows/code-npm_node-PR_verify.yml b/.github/workflows/code-npm_node-PR_verify.yml index a9b5d66..90a0949 100644 --- a/.github/workflows/code-npm_node-PR_verify.yml +++ b/.github/workflows/code-npm_node-PR_verify.yml @@ -33,6 +33,37 @@ jobs: persist-credentials: false ref: ${{ github.event.pull_request.head.sha }} + - name: Resolve governed project type + shell: bash + run: | + set -euo pipefail + descriptor="$GITHUB_WORKSPACE/.github/inditextech-ci-node.json" + if [[ ! -f "$descriptor" ]]; then + echo "::error title=Missing descriptor::.github/inditextech-ci-node.json not found." + exit 1 + fi + project_type="$(jq -r '.project_type // empty' "$descriptor")" + case "$project_type" in + single|workspaces) ;; + *) + echo "::error title=Invalid project type::Descriptor .project_type must be single or workspaces." + exit 1 + ;; + esac + workdir="${WORKING_DIRECTORY:-$(jq -r '.working_directory // empty' "$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 "PROJECT_TYPE=$project_type" >> "$GITHUB_ENV" + echo "WORKING_DIRECTORY=${workdir}" >> "$GITHUB_ENV" + - name: Read governed tool versions id: tool-versions working-directory: ${{ env.WORKING_DIRECTORY }} diff --git a/.github/workflows/code-npm_node-publish-release-and-snapshot.yml b/.github/workflows/code-npm_node-publish-release-and-snapshot.yml index 87580c0..5715ebc 100644 --- a/.github/workflows/code-npm_node-publish-release-and-snapshot.yml +++ b/.github/workflows/code-npm_node-publish-release-and-snapshot.yml @@ -26,6 +26,12 @@ on: issue_comment: types: [created] +# actionlint cache-call-unrestricted: this workflow is reachable from the +# low-trust issue_comment trigger (release re-run gated to OWNER, MEMBER and +# COLLABORATOR); cap its cache access explicitly. Declared at workflow level -- +# the job-level `cache-mode` key is not part of the workflow-call job schema. +cache-mode: none + permissions: contents: read @@ -290,7 +296,7 @@ jobs: path: ${{ runner.temp }}/publish-dist - name: Publish via npm trusted publishing - uses: InditexTech/gh-actions/npm@83ec0fe0f13556d2b31f79fc29dd0fe65c67f86f # v1.1.3 + uses: InditexTech/gh-actions/npm@8a719baa6e8d514ccdfe87cff2baae9007a8b168 # v1.1.5 env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} with: @@ -343,7 +349,7 @@ jobs: path: ${{ runner.temp }}/publish-dist - name: Publish via npm trusted publishing - uses: InditexTech/gh-actions/npm@83ec0fe0f13556d2b31f79fc29dd0fe65c67f86f # v1.1.3 + uses: InditexTech/gh-actions/npm@8a719baa6e8d514ccdfe87cff2baae9007a8b168 # v1.1.5 env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} with: @@ -562,7 +568,7 @@ jobs: git -C "$source" push --atomic origin "$RELEASE_COMMIT:$EXPECTED_REF" "${tag_refs[@]}" - name: Publish verified delegated tarballs via npm trusted publishing - uses: InditexTech/gh-actions/npm@83ec0fe0f13556d2b31f79fc29dd0fe65c67f86f # v1.1.3 + uses: InditexTech/gh-actions/npm@8a719baa6e8d514ccdfe87cff2baae9007a8b168 # v1.1.5 env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} with: diff --git a/.github/workflows/code-npm_node-release-core.yml b/.github/workflows/code-npm_node-release-core.yml index 82fede1..e33cb0b 100644 --- a/.github/workflows/code-npm_node-release-core.yml +++ b/.github/workflows/code-npm_node-release-core.yml @@ -342,7 +342,33 @@ jobs: member_dir() { printf '%s' "${1##*/}" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' } + member_directory() { + local mapped + if mapped="$(jq -er --arg p "$1" '.[$p]' <<< "$PACKAGE_PATHS" 2>/dev/null)"; then + if [[ ! "$mapped" =~ ^[a-z0-9][a-z0-9._-]*$ ]]; then + echo "::error title=Invalid release.package_paths::directory for '$1' must be a single lowercase [a-z0-9][a-z0-9._-]* segment under packages/." >&2 + exit 1 + fi + printf '%s' "$mapped" + return + fi + echo "::notice title=Derived release member::directory for '$1' derived from the package name." >&2 + member_dir "$1" + } + load_package_paths() { + PACKAGE_PATHS="$(jq -c '.release.package_paths // {}' "$descriptor")" + } strip_snapshot() { printf '%s' "${1%-SNAPSHOT}"; } + require_member() { + local dir="." + if [[ -n "$1" ]]; then + dir="packages/$1" + fi + if [[ ! -f "$dir/package.json" ]]; then + echo "::error title=Release member missing::package.json not found at '$dir'. Check release.package_paths in $descriptor." + exit 1 + fi + } read_version() { if [[ -n "$1" ]]; then jq -r '.version' "packages/$1/package.json" @@ -373,9 +399,10 @@ jobs: printf '%s\n' "$(jq -r '.name' package.json)" > "$members_file" elif [[ "$PROJECT_TYPE" == "workspaces" ]]; then release_strategy="$(jq -er '.release.strategy' "$descriptor")" + load_package_paths : > "$members_file" while IFS= read -r package; do - printf '%s\n' "$(member_dir "$package")" >> "$members_file" + printf '%s\n' "$(member_directory "$package")" >> "$members_file" done < <(jq -er '.release.packages[]' "$descriptor") else echo "::error title=Invalid project type::PROJECT_TYPE must be single or workspaces." @@ -386,6 +413,7 @@ jobs: : > "$records_file" if [[ "$release_strategy" == "single" ]]; then package_name="$(<"$members_file")" + require_member "" version="$(strip_snapshot "$(read_version "")")" next_dev="$(bump_semver "$version" "$release_bump")-SNAPSHOT" jq -cn \ @@ -402,10 +430,11 @@ jobs: while IFS= read -r member; do members+=("$member") done < "$members_file" - versions=() - for member in "${members[@]}"; do - versions+=("$(strip_snapshot "$(read_version "$member")")") - done + versions=() + for member in "${members[@]}"; do + require_member "$member" + versions+=("$(strip_snapshot "$(read_version "$member")")") + done if [[ "$(printf '%s\n' "${versions[@]}" | sort -u | wc -l | tr -d ' ')" -ne 1 ]]; then echo "::error title=Invalid locked-step reactor::All release members must share the same version." exit 1 @@ -422,16 +451,17 @@ jobs: return fi - while IFS= read -r member; do - version="$(strip_snapshot "$(read_version "$member")")" - next_dev="$(bump_semver "$version" "$release_bump")-SNAPSHOT" - jq -cn \ - --arg member "$member" \ - --arg version "$version" \ - --arg tag "${member}-${version}" \ - --arg next_dev "$next_dev" \ - '{packages: [$member], version: $version, tag: $tag, next_dev: $next_dev}' >> "$records_file" - done < "$members_file" + while IFS= read -r member; do + require_member "$member" + version="$(strip_snapshot "$(read_version "$member")")" + next_dev="$(bump_semver "$version" "$release_bump")-SNAPSHOT" + jq -cn \ + --arg member "$member" \ + --arg version "$version" \ + --arg tag "${member}-${version}" \ + --arg next_dev "$next_dev" \ + '{packages: [$member], version: $version, tag: $tag, next_dev: $next_dev}' >> "$records_file" + done < "$members_file" } if [[ "$resume" == "false" ]]; then @@ -1322,10 +1352,34 @@ jobs: member_dir() { printf '%s' "${1##*/}" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' } + member_directory() { + local mapped + if mapped="$(jq -er --arg p "$1" '.[$p]' <<< "$PACKAGE_PATHS" 2>/dev/null)"; then + if [[ ! "$mapped" =~ ^[a-z0-9][a-z0-9._-]*$ ]]; then + echo "::error title=Invalid release.package_paths::directory for '$1' must be a single lowercase [a-z0-9][a-z0-9._-]* segment under packages/." >&2 + exit 1 + fi + printf '%s' "$mapped" + return + fi + echo "::notice title=Derived release member::directory for '$1' derived from the package name." >&2 + member_dir "$1" + } + require_member() { + local dir="." + if [[ -n "$1" ]]; then + dir="packages/$1" + fi + if [[ ! -f "$dir/package.json" ]]; then + echo "::error title=Release member missing::package.json not found at '$dir'. Check release.package_paths in $descriptor." + exit 1 + fi + } if [[ "$(git rev-parse HEAD)" != "$RELEASE_COMMIT" ]]; then echo "::notice title=Next-dev skipped::Branch already advanced past the release commit." else + PACKAGE_PATHS="$(jq -c '.release.package_paths // {}' "$descriptor")" { if [[ "$PROJECT_TYPE" == "single" ]]; then next_dev="$(jq -r '.[0].next_dev' <<< "$RELEASES")" @@ -1333,7 +1387,8 @@ jobs: elif [[ "$(jq -er '.release.strategy' "$descriptor")" == "locked-step" ]]; then next_dev="$(jq -r '.[0].next_dev' <<< "$RELEASES")" while IFS= read -r package; do - member="$(member_dir "$package")" + member="$(member_directory "$package")" + require_member "$member" npm pkg set "version=$next_dev" --workspace "packages/$member" done < <(jq -er '.release.packages[]' "$descriptor") else diff --git a/.github/workflows/code-npm_node-sonarcloud-analysis.yml b/.github/workflows/code-npm_node-sonarcloud-analysis.yml index 6d62ba7..1971228 100644 --- a/.github/workflows/code-npm_node-sonarcloud-analysis.yml +++ b/.github/workflows/code-npm_node-sonarcloud-analysis.yml @@ -87,7 +87,7 @@ jobs: set -euo pipefail case "$PROJECT_TYPE" in single|workspaces) - npm ci + npm ci --ignore-scripts npm run build npm run test ;; diff --git a/.github/workflows/pr-verify.yml b/.github/workflows/pr-verify.yml index 17c6e8a..7ceb084 100644 --- a/.github/workflows/pr-verify.yml +++ b/.github/workflows/pr-verify.yml @@ -28,7 +28,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Lint GitHub Actions workflows - uses: reviewdog/action-actionlint@dbe5299849118fd6f099ba563d263d770955a64a # v1.73.2 + uses: reviewdog/action-actionlint@320fcdd9c860767cf17fab3b20e22e739d5d02b8 # v1.76.0 with: reporter: github-annotations fail_level: error diff --git a/.github/workflows/push-verify.yml b/.github/workflows/push-verify.yml index fc23062..d8bb882 100644 --- a/.github/workflows/push-verify.yml +++ b/.github/workflows/push-verify.yml @@ -29,7 +29,7 @@ jobs: persist-credentials: false - name: Lint GitHub Actions workflows - uses: reviewdog/action-actionlint@dbe5299849118fd6f099ba563d263d770955a64a # v1.73.2 + uses: reviewdog/action-actionlint@320fcdd9c860767cf17fab3b20e22e739d5d02b8 # v1.76.0 with: reporter: github-annotations fail_level: error