Skip to content

chore(ci-governance): synchronize Node profile - #22

Closed
inditextechci-sync[bot] wants to merge 1 commit into
developfrom
automated/ci-governance-sync
Closed

inditextechci-sync[bot] wants to merge 1 commit into
developfrom
automated/ci-governance-sync

Conversation

@inditextechci-sync

Copy link
Copy Markdown
Contributor

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: 1af484ce27180513e8cb4ca8905163fdea7c77cb
  • node: 7bccee5d36c614c467ec8dafc85891fb4cf05121

Planned changes

  • update .github/inditextech-ci-sync-manifest.json
  • update .github/workflows/code-npm_node-PR_verify.yml
  • update .github/workflows/code-npm_node-publish-release-and-snapshot.yml
  • update .github/workflows/code-npm_node-release-core.yml
  • update .github/workflows/code-npm_node-sonarcloud-analysis.yml
  • update .github/workflows/code-release_preview.yml
  • update .github/workflows/codeql.yml
  • update .github/workflows/pr-verify.yml
  • update .github/workflows/push-verify.yml
  • add .github/workflows/scorecard-analysis.yml
  • update .github/workflows/sync-to-develop.yml

Governance-Provenance: hmac-sha256:0837805e11b99a61122d875508937baaf8f6e7ea3020166fd7d18275ddf05021
@inditextechci-sync inditextechci-sync Bot added the skip-release Do not publish a release for this change. label Sep 17, 2026
@inditextechci-sync
inditextechci-sync Bot requested a review from a team as a code owner September 17, 2026 19:38
Comment on lines +736 to +960
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:

@ivanasabi ivanasabi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Engine batch (test consumer SYNC): auto-generated Node profile sync after fixing the bounded provenance window. Author=inditextechci-sync[bot], last pusher=ivanasabi. Bypass toggle applied (require_last_push_approval toggled off, restored after merge).

@ivanasabi

Copy link
Copy Markdown
Contributor

Closing without merge to keep the Node SYNC lane inside the bounded provenance inspection window. The PR is mergeable per --admin bypass but the consumer's ruleset gitflow-protect-integration requires code owner review from @InditexTech/swagger-ui-plugin-diff-highlight-maintainers (= bpedro, amagan), with require_extra_approval_for_unattributed_changes=true. The maintainer team cannot self-grant these approvals for their own generated SYNC. Same code-owner constraint that prevented #21 merge.

Closing deletes the bot branch so the next planner dispatch creates a fresh branch from current develop (ahead_by=0, inside the 100-commit window). The SYNC content is regenerated identically next run.

@ivanasabi ivanasabi closed this Sep 17, 2026
@ivanasabi
ivanasabi deleted the automated/ci-governance-sync branch September 17, 2026 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-release Do not publish a release for this change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants