chore(ci-governance): synchronize Node profile - #23
Open
inditextechci-sync[bot] wants to merge 2 commits into
Open
inditextechci-sync[bot] wants to merge 2 commits into
inditextechci-sync[bot] wants to merge 2 commits into
Conversation
Governance-Provenance: hmac-sha256:0837805e11b99a61122d875508937baaf8f6e7ea3020166fd7d18275ddf05021
Comment on lines
+739
to
+963
| env: | ||
| HANDOFF_DIR: ${{ runner.temp }}/node-delegated-release-handoff | ||
| RELEASES: ${{ steps.delegated-plan.outputs.releases_intermediate }} | ||
| working-directory: ${{ env.WORKING_DIRECTORY }} | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| DIST_DIR="$HANDOFF_DIR/tarballs" | ||
| RECORDS_FILE="$HANDOFF_DIR/tarballs.jsonl" | ||
| rm -rf "$HANDOFF_DIR" | ||
| mkdir -p "$DIST_DIR" | ||
| : > "$RECORDS_FILE" | ||
|
|
||
| inspect_declared_files() { | ||
| local package_dir="$1" | ||
| local tarball="$2" | ||
| local manifest="$package_dir/package.json" | ||
| local declared matched path relative | ||
| jq -e ' | ||
| .files | ||
| | type == "array" and length > 0 and | ||
| all(.[]; type == "string" and length > 0 and test("^[^\\r\\n]+$")) | ||
| ' "$manifest" > /dev/null | ||
| while IFS= read -r declared; do | ||
| if [[ "$declared" == /* || "$declared" == "." || "$declared" == ".." || "$declared" == ../* || "$declared" == */../* || "$declared" == */.. || "$declared" == "!"* || "$declared" == *"{"* || "$declared" == *"}"* || "$declared" == *\\* || "$declared" == *"@("* || "$declared" == *"+("* || "$declared" == *"?("* || "$declared" == *"*("* || "$declared" == *"!("* ]]; then | ||
| echo "::error title=Unsupported package contents declaration::${manifest} declares an unsafe or unverifiable pattern '$declared'." | ||
| exit 1 | ||
| fi | ||
| matched=0 | ||
| if [[ "$declared" == *"*"* || "$declared" == *"?"* || "$declared" == *"["* || "$declared" == *"]"* ]]; then | ||
| while IFS= read -r -d '' path; do | ||
| relative="${path#"$package_dir"/}" | ||
| matched=$((matched + 1)) | ||
| if ! tar -tzf "$tarball" | grep -qxF "package/$relative"; then | ||
| echo "::error title=Incomplete tarball::$(basename "$tarball") omits declared file '$relative'." | ||
| exit 1 | ||
| fi | ||
| done < <(node - "$package_dir" "$declared" <<'NODE' | ||
| const fs = require("fs"); | ||
| const path = require("path"); | ||
| const root = process.argv[2]; | ||
| const pattern = process.argv[3]; | ||
| let expression = "^"; | ||
| for (let index = 0; index < pattern.length; index += 1) { | ||
| const character = pattern[index]; | ||
| if (character === "*") { | ||
| let stars = 1; | ||
| while (pattern[index + stars] === "*") stars += 1; | ||
| if (stars > 1 && pattern[index + stars] === "/") { | ||
| expression += "(?:[^/]+/)*"; | ||
| index += stars; | ||
| } else { | ||
| expression += stars > 1 ? ".*" : "[^/]*"; | ||
| index += stars - 1; | ||
| } | ||
| } else if (character === "?") { | ||
| expression += "[^/]"; | ||
| } else if (character === "[") { | ||
| const closing = pattern.indexOf("]", index + 1); | ||
| if (closing === -1 || closing === index + 1) process.exit(2); | ||
| const characterClass = pattern.slice(index + 1, closing); | ||
| if (characterClass.includes("/") || characterClass.includes("\\")) process.exit(2); | ||
| expression += `[${characterClass[0] === "!" ? "^" + characterClass.slice(1) : characterClass}]`; | ||
| index = closing; | ||
| } else if (character === "]") { | ||
| process.exit(2); | ||
| } else { | ||
| expression += character.replace(/[|\\{}()[\]^$+?.]/g, "\\$&"); | ||
| } | ||
| } | ||
| const matcher = new RegExp(`${expression}$`); | ||
| const files = []; | ||
| const walk = (directory) => { | ||
| for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { | ||
| const candidate = path.join(directory, entry.name); | ||
| if (entry.isDirectory()) walk(candidate); | ||
| else if (entry.isFile()) files.push(path.relative(root, candidate).split(path.sep).join("/")); | ||
| } | ||
| }; | ||
| walk(root); | ||
| for (const file of files.filter((file) => matcher.test(file)).sort()) process.stdout.write(`${path.join(root, file)}\0`); | ||
| NODE | ||
| ) | ||
| elif [[ -d "$package_dir/$declared" ]]; then | ||
| while IFS= read -r -d '' path; do | ||
| relative="${path#"$package_dir"/}" | ||
| matched=$((matched + 1)) | ||
| if ! tar -tzf "$tarball" | grep -qxF "package/$relative"; then | ||
| echo "::error title=Incomplete tarball::$(basename "$tarball") omits declared file '$relative'." | ||
| exit 1 | ||
| fi | ||
| done < <(find "$package_dir/$declared" -type f -print0) | ||
| elif [[ -f "$package_dir/$declared" ]]; then | ||
| matched=1 | ||
| if ! tar -tzf "$tarball" | grep -qxF "package/$declared"; then | ||
| echo "::error title=Incomplete tarball::$(basename "$tarball") omits declared file '$declared'." | ||
| exit 1 | ||
| fi | ||
| fi | ||
| if [[ "$matched" -eq 0 ]]; then | ||
| echo "::error title=Missing declared package content::${manifest} declaration '$declared' matches no built files." | ||
| exit 1 | ||
| fi | ||
| done < <(jq -r '.files[]' "$manifest") | ||
| } | ||
|
|
||
| release_tag_for() { | ||
| local package_name="$1" | ||
| local package_version="$2" | ||
| local member record release_tag release_version | ||
| local -a exact_matches=() | ||
| local -a version_matches=() | ||
| member="$(printf '%s' "${package_name##*/}" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//')" | ||
| while IFS= read -r record; do | ||
| release_tag="$(jq -r '.tag' <<< "$record")" | ||
| release_version="$(jq -r '.version' <<< "$record")" | ||
| [[ "$release_version" == "$package_version" ]] || continue | ||
| version_matches+=("$release_tag") | ||
| if [[ "$release_tag" == "$package_version" || "$release_tag" == "${member}-${package_version}" || "$release_tag" == "${package_name}-${package_version}" ]]; then | ||
| exact_matches+=("$release_tag") | ||
| fi | ||
| done < <(jq -c '.[]' <<< "$RELEASES") | ||
| if [[ ${#exact_matches[@]} -eq 1 ]]; then | ||
| printf '%s' "${exact_matches[0]}" | ||
| elif [[ ${#exact_matches[@]} -eq 0 && ${#version_matches[@]} -eq 1 ]]; then | ||
| printf '%s' "${version_matches[0]}" | ||
| else | ||
| echo "::error title=Ambiguous package release::Package '$package_name@$package_version' does not resolve to exactly one prepared release tag." >&2 | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| pack_package() { | ||
| local package_dir="$1" | ||
| local package_name="$2" | ||
| local before="$HANDOFF_DIR/tarballs-before.txt" | ||
| local after="$HANDOFF_DIR/tarballs-after.txt" | ||
| local added="$HANDOFF_DIR/tarballs-added.txt" | ||
| local tarball | ||
| find "$DIST_DIR" -maxdepth 1 -name '*.tgz' -print | LC_ALL=C sort > "$before" | ||
| case "${PACKAGE_MANAGER:-npm}" in | ||
| npm) | ||
| npm pack "./$package_dir" --pack-destination "$DIST_DIR" | ||
| ;; | ||
| pnpm) | ||
| corepack enable | ||
| ( cd "$package_dir" && pnpm pack --pack-destination "$DIST_DIR" ) | ||
| ;; | ||
| *) | ||
| echo "::error title=Invalid package manager::PACKAGE_MANAGER must be npm or pnpm." | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| find "$DIST_DIR" -maxdepth 1 -name '*.tgz' -print | LC_ALL=C sort > "$after" | ||
| comm -13 "$before" "$after" > "$added" | ||
| if [[ "$(wc -l < "$added" | tr -d ' ')" -ne 1 ]]; then | ||
| echo "::error title=Invalid package artifact::Packing '$package_name' must produce exactly one new tarball." | ||
| exit 1 | ||
| fi | ||
| tarball="$(<"$added")" | ||
| inspect_declared_files "$package_dir" "$tarball" | ||
| tarball_package="$(tar -xzOf "$tarball" package/package.json | jq -er '.name | select(type == "string" and length > 0)')" | ||
| tarball_version="$(tar -xzOf "$tarball" package/package.json | jq -er '.version | select(type == "string" and length > 0)')" | ||
| if [[ "$tarball_package" != "$package_name" ]]; then | ||
| echo "::error title=Invalid package artifact::$(basename "$tarball") identifies '$tarball_package', not declared package '$package_name'." | ||
| exit 1 | ||
| fi | ||
| tarball_release_tag="$(release_tag_for "$tarball_package" "$tarball_version")" | ||
| jq -cn \ | ||
| --arg file "$(basename "$tarball")" \ | ||
| --arg package "$package_name" \ | ||
| --arg release_tag "$tarball_release_tag" \ | ||
| --arg sha256 "$(sha256sum "$tarball" | awk '{print $1}')" \ | ||
| --arg version "$tarball_version" \ | ||
| '{file: $file, package: $package, release_tag: $release_tag, sha256: $sha256, version: $version}' >> "$RECORDS_FILE" | ||
| } | ||
|
|
||
| case "${PACKAGE_MANAGER:-npm}" in | ||
| npm) | ||
| npm run build | ||
| ;; | ||
| pnpm) | ||
| corepack enable | ||
| pnpm run build | ||
| ;; | ||
| *) | ||
| echo "::error title=Invalid package manager::PACKAGE_MANAGER must be npm or pnpm." | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| case "$PROJECT_TYPE" in | ||
| single) | ||
| package_name="$(jq -er '.name | select(type == "string" and length > 0)' package.json)" | ||
| pack_package "." "$package_name" | ||
| ;; | ||
| workspaces) | ||
| jq -e ' | ||
| .release.packages | ||
| | type == "array" and length > 0 and | ||
| all(.[]; type == "string" and length > 0) | ||
| ' "$GITHUB_WORKSPACE/.github/inditextech-ci-node.json" > /dev/null | ||
| while IFS= read -r package_name; do | ||
| member="$(printf '%s' "${package_name##*/}" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//')" | ||
| package_dir="packages/$member" | ||
| if [[ ! -f "$package_dir/package.json" ]] || [[ "$(jq -r '.name' "$package_dir/package.json")" != "$package_name" ]]; then | ||
| echo "::error title=Unknown workspace package::release.packages entry '$package_name' does not resolve to its declared workspace manifest." | ||
| exit 1 | ||
| fi | ||
| pack_package "$package_dir" "$package_name" | ||
| done < <(jq -r '.release.packages[]' "$GITHUB_WORKSPACE/.github/inditextech-ci-node.json") | ||
| ;; | ||
| *) | ||
| echo "::error title=Invalid project type::PROJECT_TYPE must be single or workspaces." | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| jq -cs . "$RECORDS_FILE" > "$HANDOFF_DIR/tarballs.json" | ||
| echo "handoff_dir=$HANDOFF_DIR" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Create delegated release handoff | ||
| id: delegated-handoff | ||
| if: vars.RELEASE_LIFECYCLE == 'delegated' | ||
| env: |
| echo "::error title=Invalid package manager::PACKAGE_MANAGER must be npm or pnpm." | ||
| exit 1 | ||
| ;; | ||
| npm) npm ci ;; |
Governance-Provenance: hmac-sha256:9f99e6a9dcda0869e8a2b16e662ab3934595ca748bc2a31590f6fe405a176c10
|
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




CI governance synchronization
This PR was generated by the PR-only controller. Review and merge it; the controller never pushes directly to the default branch.
Profile
node(Node)Source revisions
base:614b384ab7cc1c9111bafafe76ed5d2c22809486node:2169182171a0c208ba36bac34ccc41e618126f12Planned changes
update.github/inditextech-ci-sync-manifest.jsonupdate.github/workflows/code-npm_node-PR_verify.ymlupdate.github/workflows/code-npm_node-publish-release-and-snapshot.ymlupdate.github/workflows/code-npm_node-release-core.ymlupdate.github/workflows/code-npm_node-sonarcloud-analysis.ymlupdate.github/workflows/code-release_preview.ymlupdate.github/workflows/codeql.ymlupdate.github/workflows/pr-verify.ymlupdate.github/workflows/push-verify.ymladd.github/workflows/scorecard-analysis.ymlupdate.github/workflows/sync-to-develop.yml