From 607cc37c5787fad201b54bd1e4a9a4868d9e888b Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Tue, 9 Sep 2025 16:29:58 -0700 Subject: [PATCH 01/18] remove npm token --- .github/workflows/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 40190d6bca..3689c67442 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -321,8 +321,6 @@ jobs: else npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ fi - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Notify Slack success if: success() From 93986101d74b8060ef6e756b674ca84910e6cf39 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Tue, 9 Sep 2025 17:15:23 -0700 Subject: [PATCH 02/18] test release --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3689c67442..dcdf5815c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -194,7 +194,7 @@ jobs: set -euo pipefail echo "Packaging ${{ env.PACKAGE_NAME }}" find "packages/${{ env.PACKAGE_NAME }}" -maxdepth 1 -name '*.tgz' -delete || true - TARBALL=$(npx lerna exec --scope "${{ env.PACKAGE_NAME }}" -- npm pack --json | jq -r '.[0].filename') + TARBALL=$(npx lerna exec --scope "@shichengsh001/${{ env.PACKAGE_NAME }}" -- npm pack --json | jq -r '.[0].filename') echo "TARBALL=packages/${{ env.PACKAGE_NAME }}/${TARBALL}" >> "$GITHUB_ENV" env: NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/.npmrc From 8d91388d1155572b336ae12376b7cf975cfa3a08 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Tue, 9 Sep 2025 17:51:32 -0700 Subject: [PATCH 03/18] test release --- .github/workflows/release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dcdf5815c9..a312cee787 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -196,8 +196,6 @@ jobs: find "packages/${{ env.PACKAGE_NAME }}" -maxdepth 1 -name '*.tgz' -delete || true TARBALL=$(npx lerna exec --scope "@shichengsh001/${{ env.PACKAGE_NAME }}" -- npm pack --json | jq -r '.[0].filename') echo "TARBALL=packages/${{ env.PACKAGE_NAME }}/${TARBALL}" >> "$GITHUB_ENV" - env: - NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/.npmrc - name: Upload tarball as artifact uses: actions/upload-artifact@v4 @@ -316,6 +314,7 @@ jobs: cd dist PKG=$(ls *.tgz) echo $PKG + npm whoami if [[ "${{ github.event.inputs.dry-run }}" == "true" ]]; then npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ --dry-run else From b95ff5e5dde256628273a7abb54e15c5e408eb78 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 10 Sep 2025 07:56:37 -0700 Subject: [PATCH 04/18] test release --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a312cee787..26bb54ead3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -314,12 +314,13 @@ jobs: cd dist PKG=$(ls *.tgz) echo $PKG - npm whoami if [[ "${{ github.event.inputs.dry-run }}" == "true" ]]; then npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ --dry-run else npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Notify Slack success if: success() From e9879b49b24a021da7ed074749af9757711fca28 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 10 Sep 2025 08:18:34 -0700 Subject: [PATCH 05/18] test release --- .github/workflows/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 26bb54ead3..efd1dd53ba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -319,8 +319,6 @@ jobs: else npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ fi - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Notify Slack success if: success() From 70b2c3373bb1cd48dcc400bd6639af28c6e2a23b Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 10 Sep 2025 10:38:53 -0700 Subject: [PATCH 06/18] test release --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index efd1dd53ba..9b05473f1f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -314,6 +314,7 @@ jobs: cd dist PKG=$(ls *.tgz) echo $PKG + npm install -g npm@latest if [[ "${{ github.event.inputs.dry-run }}" == "true" ]]; then npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ --dry-run else From cb7ddf0cd488a8186a180107798c677d6a0d2210 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Tue, 16 Sep 2025 13:24:31 +0800 Subject: [PATCH 07/18] changes for issue https://github.com/XRPLF/xrpl.js/issues/3070 --- .github/actions/create-vuln-issue/action.yml | 73 ++ .github/workflows/release.yml | 674 +++++++++++-------- 2 files changed, 473 insertions(+), 274 deletions(-) create mode 100644 .github/actions/create-vuln-issue/action.yml diff --git a/.github/actions/create-vuln-issue/action.yml b/.github/actions/create-vuln-issue/action.yml new file mode 100644 index 0000000000..7460f22a77 --- /dev/null +++ b/.github/actions/create-vuln-issue/action.yml @@ -0,0 +1,73 @@ +name: "Create Vulnerability Issue" +description: "Creates a GitHub issue if CRITICAL/HIGH vulnerabilities are found in the scan report" + +inputs: + report_path: + description: "Path to the vulnerability report file" + required: true + package_name: + description: "Name of the package being released" + required: true + package_version: + description: "Version of the package being released" + required: true + release_branch: + description: "Release branch name" + required: true + labels: + description: "Labels to apply to the created issue" + required: false + default: "security" + +runs: + using: "composite" + steps: + - name: Check vulnerabilities in report + id: check_vulns + shell: bash + run: | + set -euo pipefail + REPORT="${{ inputs.report_path }}" + + if grep -qE "CRITICAL|HIGH" "$REPORT"; then + echo "found=true" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + + - name: Create GitHub Issue + if: steps.check_vulns.outputs.found == 'true' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + TITLE="πŸ”’ Security vulnerabilities in ${{ inputs.package_name }}@${{ inputs.package_version }}" + BODY=$(cat < /dev/null; then - echo "βœ… Found release branch: ${{ github.event.inputs.release_branch }}" - else - echo "❌ Release branch ${{ github.event.inputs.release_branch }} not found in remote. Failing workflow." - exit 1 - fi - - if grep -R --exclude-dir=.git --exclude-dir=.github "artifactory.ops.ripple.com" .; then - echo "❌ Internal Artifactory URL found" - exit 1 - else - echo "βœ… No Internal Artifactory URL found" - fi - - - name: Get package version from package.json - id: get_version - run: | - set -euo pipefail - PACKAGE_NAME="${{ github.event.inputs.package_name }}" - PKG_JSON="packages/${PACKAGE_NAME}/package.json" - if [[ ! -f "$PKG_JSON" ]]; then - echo "package.json not found at $PKG_JSON. Check 'package_name' input." >&2 - exit 1 - fi - VERSION=$(jq -er .version "$PKG_JSON") - if [[ -z "$VERSION" || "$VERSION" == "null" ]]; then - echo "Version is empty or missing in $PKG_JSON" >&2 - exit 1 - fi - echo "PACKAGE_VERSION=$VERSION" >> "$GITHUB_ENV" - echo "version=$VERSION" >> "$GITHUB_OUTPUT" + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.release_branch }} + fetch-depth: 0 + + - name: Validate inputs + run: | + set -euo pipefail + if git ls-remote --exit-code origin "refs/heads/${{ github.event.inputs.release_branch }}" > /dev/null; then + echo "βœ… Found release branch: ${{ github.event.inputs.release_branch }}" + else + echo "❌ Release branch ${{ github.event.inputs.release_branch }} not found in remote. Failing workflow." + exit 1 + fi + + if grep -R --exclude-dir=.git --exclude-dir=.github "artifactory.ops.ripple.com" .; then + echo "❌ Internal Artifactory URL found" + exit 1 + else + echo "βœ… No Internal Artifactory URL found" + fi + + - name: Get package version from package.json + id: get_version + run: | + set -euo pipefail + PACKAGE_NAME="${{ github.event.inputs.package_name }}" + PKG_JSON="packages/${PACKAGE_NAME}/package.json" + if [[ ! -f "$PKG_JSON" ]]; then + echo "package.json not found at $PKG_JSON. Check 'package_name' input." >&2 + exit 1 + fi + VERSION=$(jq -er .version "$PKG_JSON") + if [[ -z "$VERSION" || "$VERSION" == "null" ]]; then + echo "Version is empty or missing in $PKG_JSON" >&2 + exit 1 + fi + echo "PACKAGE_VERSION=$VERSION" >> "$GITHUB_ENV" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" run_faucet_test: name: Run faucet tests ${{ needs.get_version.outputs.package_version }} @@ -79,13 +77,13 @@ jobs: git_ref: ${{ github.event.inputs.release_branch }} secrets: inherit - run_tests: name: Run unit/integration tests ${{ needs.get_version.outputs.package_version }} permissions: contents: read id-token: write pages: write + issues: write needs: [get_version] uses: ./.github/workflows/nodejs.yml with: @@ -99,109 +97,114 @@ jobs: env: PACKAGE_VERSION: "${{ needs.get_version.outputs.package_version }}" PACKAGE_NAME: "${{ github.event.inputs.package_name }}" - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.release_branch }} - fetch-depth: 0 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - registry-url: 'https://registry.npmjs.org' - - - name: Build package - run: | - # dubugging info - npm --version - node --version - ls -l - pwd - - #build - npm ci - npm run build - - - name: Notify Slack if tests fail - if: failure() - env: - SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} - run: | - MESSAGE="❌ Build failed for xrpl.js ${{ env.PACKAGE_VERSION }}. Check the logs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - curl -X POST https://slack.com/api/chat.postMessage \ - -H "Authorization: Bearer $SLACK_TOKEN" \ - -H "Content-Type: application/json" \ - -d "$(jq -n \ - --arg channel "#xrpl-js" \ - --arg text "$MESSAGE" \ - '{channel: $channel, text: $text}')" - - - name: Install cyclonedx-npm - run: npm install -g @cyclonedx/cyclonedx-npm - - - name: Generate CycloneDX SBOM - run: cyclonedx-npm --output-format json --output-file sbom.json - - - name: Scan SBOM for vulnerabilities using Trivy - uses: aquasecurity/trivy-action@0.28.0 - with: - scan-type: sbom - scan-ref: sbom.json - format: table - exit-code: 0 - output: vuln-report.txt - severity: CRITICAL,HIGH - - - name: Upload sbom to OWASP - run: | - curl -X POST \ - -H "X-Api-Key: ${{ secrets.OWASP_TOKEN }}" \ - -F "project=7c40c8ea-ea0f-4a5f-9b9f-368e53232397" \ - -F "bom=@sbom.json" \ - https://owasp-dt-api.prod.ripplex.io/api/v1/bom - - - name: Upload SBOM artifact - uses: actions/upload-artifact@v4 - with: - name: sbom - path: sbom.json - - - name: Print scan report - run: cat vuln-report.txt - - - name: Upload vulnerability report artifact - uses: actions/upload-artifact@v4 - with: - name: vulnerability-report - path: vuln-report.txt - - - name: Generate lerna.json for choosen the package - run: | - - echo "πŸ”§ Updating lerna.json to include only packages/${{ env.PACKAGE_NAME }}" - - # Use jq to update the packages field safely - jq --arg pkg "packages/${{ env.PACKAGE_NAME }}" '.packages = [$pkg]' lerna.json > lerna.tmp.json && mv lerna.tmp.json lerna.json - - echo "βœ… lerna.json updated:" - cat lerna.json - - - name: Pack tarball - run: | - set -euo pipefail - echo "Packaging ${{ env.PACKAGE_NAME }}" - find "packages/${{ env.PACKAGE_NAME }}" -maxdepth 1 -name '*.tgz' -delete || true - TARBALL=$(npx lerna exec --scope "@shichengsh001/${{ env.PACKAGE_NAME }}" -- npm pack --json | jq -r '.[0].filename') - echo "TARBALL=packages/${{ env.PACKAGE_NAME }}/${TARBALL}" >> "$GITHUB_ENV" - - - name: Upload tarball as artifact - uses: actions/upload-artifact@v4 - with: - name: npm-package-tarball - path: ${{ env.TARBALL }} + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.release_branch }} + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: 'https://registry.npmjs.org' + + - name: Build package + run: | + # dubugging info + npm --version + node --version + ls -l + pwd + + #build + npm ci + npm run build + + - name: Notify Slack if tests fail + if: failure() + env: + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + run: | + MESSAGE="❌ Build failed for xrpl.js ${{ env.PACKAGE_VERSION }}. Check the logs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $SLACK_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg channel "#test-alert" \ + --arg text "$MESSAGE" \ + '{channel: $channel, text: $text}')" + + - name: Install cyclonedx-npm + run: npm install -g @cyclonedx/cyclonedx-npm + + - name: Generate CycloneDX SBOM + run: cyclonedx-npm --output-format json --output-file sbom.json + + - name: Scan SBOM for vulnerabilities using Trivy + uses: aquasecurity/trivy-action@0.28.0 + with: + scan-type: sbom + scan-ref: sbom.json + format: table + exit-code: 0 + output: vuln-report.txt + severity: CRITICAL,HIGH + + - name: Upload sbom to OWASP + run: | + curl -X POST \ + -H "X-Api-Key: ${{ secrets.OWASP_TOKEN }}" \ + -F "project=7c40c8ea-ea0f-4a5f-9b9f-368e53232397" \ + -F "bom=@sbom.json" \ + https://owasp-dt-api.prod.ripplex.io/api/v1/bom + + - name: Upload SBOM artifact + uses: actions/upload-artifact@v4 + with: + name: sbom + path: sbom.json + + - name: Print scan report + run: cat vuln-report.txt + + - name: Upload vulnerability report artifact + uses: actions/upload-artifact@v4 + with: + name: vulnerability-report + path: vuln-report.txt + + - name: Create issue if CRITICAL/HIGH found (inline) + uses: ./.github/actions/create-vuln-issue + with: + report_path: vuln-report.txt + package_name: ${{ env.PACKAGE_NAME }} + package_version: ${{ env.PACKAGE_VERSION }} + release_branch: ${{ github.event.inputs.release_branch }} + labels: "security" + + - name: Generate lerna.json for choosen the package + run: | + echo "πŸ”§ Updating lerna.json to include only packages/${{ env.PACKAGE_NAME }}" + # Use jq to update the packages field safely + jq --arg pkg "packages/${{ env.PACKAGE_NAME }}" '.packages = [$pkg]' lerna.json > lerna.tmp.json && mv lerna.tmp.json lerna.json + echo "βœ… lerna.json updated:" + cat lerna.json + + - name: Pack tarball + run: | + set -euo pipefail + echo "Packaging ${{ env.PACKAGE_NAME }}" + find "packages/${{ env.PACKAGE_NAME }}" -maxdepth 1 -name '*.tgz' -delete || true + TARBALL=$(npx lerna exec --scope "@shichengsh001/${{ env.PACKAGE_NAME }}" -- npm pack --json | jq -r '.[0].filename') + echo "TARBALL=packages/${{ env.PACKAGE_NAME }}/${TARBALL}" >> "$GITHUB_ENV" + + - name: Upload tarball as artifact + uses: actions/upload-artifact@v4 + with: + name: npm-package-tarball + path: ${{ env.TARBALL }} review: runs-on: ubuntu-latest @@ -211,44 +214,141 @@ jobs: PACKAGE_VERSION: "${{ needs.get_version.outputs.package_version }}" PACKAGE_NAME: "${{ github.event.inputs.package_name }}" steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.release_branch }} - fetch-depth: 0 - - name: Release summary for review - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPO: ${{ github.repository }} - RUN_ID: ${{ github.run_id }} - run: | - ARTIFACT_NAME="vulnerability-report" - RELEASE_BRANCH="${{ github.event.inputs.release_branch }}" - COMMIT_SHA="$(git rev-parse --short HEAD)" - - echo "Fetching artifact ID for ${ARTIFACT_NAME}..." - ARTIFACTS=$(curl -s -H "Authorization: Bearer $GH_TOKEN" \ - -H "Accept: application/vnd.github+json" \ - https://api.github.com/repos/$REPO/actions/runs/$RUN_ID/artifacts) - - ARTIFACT_ID=$(echo "$ARTIFACTS" | jq -r ".artifacts[] | select(.name == \"$ARTIFACT_NAME\") | .id") - - if [ -z "$ARTIFACT_ID" ]; then - echo "❌ Artifact not found." - exit 1 - fi - echo "πŸ” Please review the following details before proceeding:" - echo "πŸ“¦ Package Name: $PACKAGE_NAME" - echo "πŸ”– Package Version: $PACKAGE_VERSION" - echo "🌿 Release Branc: $RELEASE_BRANCH" - echo "πŸ”’ Commit SHA: $COMMIT_SHA" - echo "πŸ”— Please review Vulnerabilities detected: https://github.com/$REPO/actions/runs/${{ github.run_id }}/artifacts/$ARTIFACT_ID" + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.release_branch }} + fetch-depth: 0 + - name: Create PR from release branch to main (skips for rc/beta) + if: | + !contains(needs.get_version.outputs.package_version, '-rc') && + !contains(needs.get_version.outputs.package_version, '-beta') + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + RELEASE_BRANCH: ${{ github.event.inputs.release_branch }} + VERSION: ${{ needs.get_version.outputs.package_version }} + run: | + set -euo pipefail + echo "πŸ”Ž Checking if a PR already exists for $RELEASE_BRANCH β†’ main…" + PR_NUMBER="$(gh pr list --repo "$REPO" --base main --head "$RELEASE_BRANCH" --state open --json number --jq '.[0].number' || true)" + if [ -n "${PR_NUMBER:-}" ]; then + echo "ℹ️ PR already exists: #$PR_NUMBER" + PR_URL="https://github.com/$REPO/pull/$PR_NUMBER" + else + echo "πŸ“ Creating PR for release $VERSION from $RELEASE_BRANCH β†’ main" + PR_URL="$(gh pr create \ + --repo "$REPO" \ + --base main \ + --head "$RELEASE_BRANCH" \ + --title "Release $VERSION: $RELEASE_BRANCH β†’ main" \ + --body "Automated PR for release **$VERSION** from **$RELEASE_BRANCH** β†’ **main**. Workflow Run: https://github.com/$REPO/actions/runs/${{ github.run_id }}" \ + --label release \ + --json url --jq .url)" + fi + echo "PR_URL=$PR_URL" >> "$GITHUB_ENV" + + - name: Release summary for review + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + REPO: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + ENV_NAME: official-release + PACKAGE_NAME: ${{ env.PACKAGE_NAME }} + PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }} + NPMJS_DIST_TAG: ${{ github.event.inputs.npmjs_dist_tag }} + GITHUB_ACTOR: ${{ github.actor }} + GITHUB_TRIGGERING_ACTOR: ${{ github.triggering_actor }} + RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + + run: | + set -euo pipefail + ARTIFACT_NAME="vulnerability-report" + RELEASE_BRANCH="${{ github.event.inputs.release_branch }}" + COMMIT_SHA="$(git rev-parse --short HEAD)" + + echo "Fetching artifact ID for ${ARTIFACT_NAME}..." + ARTIFACTS=$(curl -s -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/$REPO/actions/runs/$RUN_ID/artifacts") + + ARTIFACT_ID=$(echo "$ARTIFACTS" | jq -r ".artifacts[] | select(.name == \"$ARTIFACT_NAME\") | .id") + + if [ -z "${ARTIFACT_ID:-}" ]; then + echo "❌ Artifact not found." + exit 1 + fi + + echo "πŸ” Please review the following details before proceeding:" + echo "πŸ“¦ Package Name: $PACKAGE_NAME" + echo "πŸ”– Package Version: $PACKAGE_VERSION" + echo "🌿 Release Branch: $RELEASE_BRANCH" + echo "πŸ”’ Commit SHA: $COMMIT_SHA" + echo "πŸ”— Vulnerabilities: https://github.com/$REPO/actions/runs/$RUN_ID/artifacts/$ARTIFACT_ID" + + # executor = the person who triggered the pipeline + EXECUTOR="${GITHUB_TRIGGERING_ACTOR:-$GITHUB_ACTOR}" + + # Fetch environment and extract required reviewers + ENV_JSON="$(curl -sSf \ + -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/$REPO/environments/$ENV_NAME")" + + REVIEWERS="$(printf '%s' "$ENV_JSON" | jq -r ' + (.protection_rules // []) + | map(select(.type=="required_reviewers") | .reviewers // []) + | add // [] + | map( + if .type=="User" then (.reviewer.login) + elif .type=="Team" then (.reviewer.slug) + else (.reviewer.login // .reviewer.slug // "unknown") + end + ) + | unique + | join(", ") + ')" + + if [ -z "$REVIEWERS" ] || [ "$REVIEWERS" = "null" ]; then + REVIEWERS="(no required reviewers configured)" + fi + + # RC detection: skip step 2 if RC (dist-tag starts with rc OR version contains -rc) + IS_RC="false" + [[ "${NPMJS_DIST_TAG:-}" =~ ^rc ]] || [[ "${PACKAGE_VERSION:-}" =~ -rc ]] && IS_RC="true" + + # Build Step 2 line with PR URL (if available), unless RC + if [ "$IS_RC" = "true" ]; then + STEP2_LINE="2. Review the package update PR – **SKIPPED for release candidates**." + else + if [ -n "${PR_URL:-}" ]; then + STEP2_LINE="2. Review the package update PR and provide two approvals. DO NOT MERGE - ${EXECUTOR} will verify the package on npm registry and merge this approved PR. (${PR_URL})" + else + STEP2_LINE="2. Review the package update PR and provide two approvals. DO NOT MERGE - ${EXECUTOR} will verify the package on npm registry and merge this approved PR." + fi + fi + + # Compose message robustly + printf -v MESSAGE '%s is releasing %s@%s. Two approvers from %s need to take following actions:\n1. Review the release artifacts and approve/reject the release. (%s)\n%s' \ + "$EXECUTOR" "$PACKAGE_NAME" "$PACKAGE_VERSION" "$REVIEWERS" "$RUN_URL" "$STEP2_LINE" + + echo "$MESSAGE" + + # Post to Slack (channel can be changed as needed) + curl -sS -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $SLACK_TOKEN" \ + -H "Content-Type: application/json; charset=utf-8" \ + -d "$(jq -n \ + --arg channel "#test-alert" \ + --arg text "$MESSAGE" \ + '{channel: $channel, text: $text}')" release: runs-on: ubuntu-latest permissions: - id-token: write - contents: write + id-token: write + contents: write needs: [get_version, run_faucet_test, run_tests, pre_release, review] name: Release Pipeline for ${{ needs.get_version.outputs.package_version }} env: @@ -258,93 +358,119 @@ jobs: name: official-release url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.release_branch }} - fetch-depth: 0 - - - name: Ensure Git tag exists - id: create_tag - run: | - set -euo pipefail - BASE_TAG="${{ env.PACKAGE_NAME }}@${{ env.PACKAGE_VERSION }}" - DRY_RUN="${{ github.event.inputs.dry-run }}" - TAG="$BASE_TAG" - - if [ "$DRY_RUN" = "true" ]; then - TAG="draft-$BASE_TAG" - fi - - git fetch --tags origin - - if git rev-parse "$TAG" >/dev/null 2>&1 && [ "$DRY_RUN" != "true" ]; then - echo "❌ Tag $TAG already exists (not a draft). Failing." - exit 1 - fi - - echo "πŸ”– Tagging $TAG" - git tag -f "$TAG" - git push origin -f "$TAG" - - echo "tag_name=$TAG" >> "$GITHUB_OUTPUT" - - - name: Create GitHub release - uses: softprops/action-gh-release@v2 - with: - tag_name: "${{ steps.create_tag.outputs.tag_name }}" - name: "${{ steps.create_tag.outputs.tag_name }}" - draft: ${{ github.event.inputs.dry-run == 'true' }} - generate_release_notes: true - make_latest: ${{ github.event.inputs.dry-run != 'true' }} - - - name: Download artifact - uses: actions/download-artifact@v4 - with: - name: npm-package-tarball - path: dist - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - registry-url: 'https://registry.npmjs.org/' - - name: Publish to npm - run: | - cd dist - PKG=$(ls *.tgz) - echo $PKG - npm install -g npm@latest - if [[ "${{ github.event.inputs.dry-run }}" == "true" ]]; then - npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ --dry-run - else - npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ - fi - - - name: Notify Slack success - if: success() - env: - SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} - run: | - MESSAGE="βœ… Released xrpl.js v${{ env.PACKAGE_VERSION }}. Published to npm and GitHub successfully." - curl -X POST https://slack.com/api/chat.postMessage \ - -H "Authorization: Bearer $SLACK_TOKEN" \ - -H "Content-Type: application/json" \ - -d "$(jq -n \ - --arg channel "#xrpl-js" \ - --arg text "$MESSAGE" \ - '{channel: $channel, text: $text}')" - - - name: Notify Slack if tests fail - if: failure() - env: - SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} - run: | - MESSAGE="❌ Tests failed for xrpl.js ${{ env.PACKAGE_VERSION }}. Check the logs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - curl -X POST https://slack.com/api/chat.postMessage \ - -H "Authorization: Bearer $SLACK_TOKEN" \ - -H "Content-Type: application/json" \ - -d "$(jq -n \ - --arg channel "#xrpl-js" \ - --arg text "$MESSAGE" \ - '{channel: $channel, text: $text}')" + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.release_branch }} + fetch-depth: 0 + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: npm-package-tarball + path: dist + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + registry-url: 'https://registry.npmjs.org/' + + - name: Publish to npm + run: | + cd dist + PKG=$(ls *.tgz) + echo $PKG + NPM_DIST_TAG="${{ github.event.inputs.npmjs_dist_tag }}" + if [ -z "$TAG" ]; then + TAG="latest" + fi + npm install -g npm@latest + npm publish "$PKG" --provenance --access public --registry=https://registry.npmjs.org/ --tag "$NPM_DIST_TAG" + + - name: Ensure Git tag exists + id: create_tag + run: | + set -euo pipefail + TAG="${{ env.PACKAGE_NAME }}@${{ env.PACKAGE_VERSION }}" + + git fetch --tags origin + + if git rev-parse "$TAG" >/dev/null 2>&1 ; then + echo "❌ Tag $TAG already exists (not a draft). Failing." + exit 1 + fi + + echo "πŸ”– Tagging $TAG" + git tag -f "$TAG" + git push origin -f "$TAG" + + echo "tag_name=$TAG" >> "$GITHUB_OUTPUT" + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: "${{ steps.create_tag.outputs.tag_name }}" + name: "${{ steps.create_tag.outputs.tag_name }}" + draft: false + generate_release_notes: true + make_latest: true + + - name: Fetch generated release notes + id: fetch_notes + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + RELEASE_ID: ${{ steps.gh_release.outputs.id }} # <- reliable key to fetch by + run: | + set -euo pipefail + resp="$(curl -sSf \ + -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/$REPO/releases/$RELEASE_ID")" + + body="$(printf '%s' "$resp" | jq -r '.body // ""')" + url="$(printf '%s' "$resp" | jq -r '.html_url // ""')" + + # Fallback if empty and trim if overly long (Slack safety) + [ -z "$body" ] || [ "$body" = "null" ] && body="(No release notes were generated.)" + max=38000 + [ "${#body}" -gt "$max" ] && body="${body:0:$max}\n…(truncated)" + + esc_body="${body//'%'/'%25'}" + esc_body="${esc_body//$'\n'/'%0A'}" + esc_body="${esc_body//$'\r'/'%0D'}" + esc_url="${url//'%'/'%25'}" + echo "notes=$esc_body" >> "$GITHUB_OUTPUT" + echo "release_url=$esc_url" >> "$GITHUB_OUTPUT" + + - name: Notify Slack success + if: success() + env: + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + TAG: ${{ steps.gh_release.outputs.tag_name }} + NOTES: ${{ steps.fetch_notes.outputs.notes }} + RELEASE_URL: ${{ steps.fetch_notes.outputs.release_url }} + run: | + set -euo pipefail + notes="${NOTES//'%0A'/$'\n'}" + notes="${notes//'%0D'/}" + text="πŸ“£ New release has been published successfully: ${TAG}\n${RELEASE_URL}\n\n${notes}" + curl -sS -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $SLACK_TOKEN" \ + -H "Content-Type: application/json; charset=utf-8" \ + -d "$(jq -n --arg channel "#test-alert" --arg text "$text" '{channel: $channel, text: $text}')" + + - name: Notify Slack if tests fail + if: failure() + env: + SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} + run: | + MESSAGE="❌ Tests failed for xrpl.js ${{ env.PACKAGE_VERSION }}. Check the logs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" + curl -X POST https://slack.com/api/chat.postMessage \ + -H "Authorization: Bearer $SLACK_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg channel "#test-alert" \ + --arg text "$MESSAGE" \ + '{channel: $channel, text: $text}')" From ee870a7abf0463bb2ca65a27cca20fcf100018c2 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Tue, 16 Sep 2025 14:20:39 +0800 Subject: [PATCH 08/18] fix permissions --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 65986216ae..ed96814a76 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,7 +83,6 @@ jobs: contents: read id-token: write pages: write - issues: write needs: [get_version] uses: ./.github/workflows/nodejs.yml with: @@ -94,6 +93,8 @@ jobs: runs-on: ubuntu-latest needs: [get_version, run_faucet_test, run_tests] name: Pre Release Pipeline for ${{ needs.get_version.outputs.package_version }} + permissions: + issues: write env: PACKAGE_VERSION: "${{ needs.get_version.outputs.package_version }}" PACKAGE_NAME: "${{ github.event.inputs.package_name }}" From d9eb89303e1c77eed8406facd07ee779bee243cf Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Tue, 16 Sep 2025 17:17:52 +0800 Subject: [PATCH 09/18] add the vulnerability report url to the Github issue --- .github/actions/create-vuln-issue/action.yml | 17 ++++------------- .github/workflows/release.yml | 9 ++++++++- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/actions/create-vuln-issue/action.yml b/.github/actions/create-vuln-issue/action.yml index 7460f22a77..c18fcb53b7 100644 --- a/.github/actions/create-vuln-issue/action.yml +++ b/.github/actions/create-vuln-issue/action.yml @@ -18,6 +18,9 @@ inputs: description: "Labels to apply to the created issue" required: false default: "security" + vuln_art_url: + description: "URL to the vulnerability report artifact" + required: false runs: using: "composite" @@ -48,7 +51,7 @@ runs: **Release Branch:** \`${{ inputs.release_branch }}\` **Package Version:** \`${{ inputs.package_version }}\` - Please review the attached vulnerability report and take necessary action. + Please review the attached vulnerability report and take necessary action. URL of the vulnerability report ${{ inputs.vuln_art_url }}. --- _This issue was automatically generated by the Release Pipeline._ @@ -59,15 +62,3 @@ runs: --title "$TITLE" \ --body "$BODY" \ --label "${{ inputs.labels }}" - - - name: Upload report as artifact to issue (comment with attachment) - if: steps.check_vulns.outputs.found == 'true' - shell: bash - env: - GH_TOKEN: ${{ github.token }} - run: | - ISSUE_NUMBER=$(gh issue list --state open --search "πŸ”’ Security vulnerabilities in ${{ inputs.package_name }}@${{ inputs.package_version }}" --json number --jq '.[0].number') - if [ -n "$ISSUE_NUMBER" ]; then - gh issue comment "$ISSUE_NUMBER" --body "πŸ“Ž Attaching vulnerability report as artifact." - gh upload-artifact --name vulnerability-report "${{ inputs.report_path }}" - fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed96814a76..378494701d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -171,14 +171,21 @@ jobs: run: cat vuln-report.txt - name: Upload vulnerability report artifact + id: upload_vuln uses: actions/upload-artifact@v4 with: name: vulnerability-report path: vuln-report.txt - - name: Create issue if CRITICAL/HIGH found (inline) + - name: Build vuln artifact URL + id: vuln_art + run: | + echo "art_url=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.upload_vuln.outputs.artifact-id }}" >> "$GITHUB_OUTPUT" + + - name: Create issue if CRITICAL/HIGH found uses: ./.github/actions/create-vuln-issue with: + vuln_art_url: ${{ steps.vuln_art.outputs.url }} report_path: vuln-report.txt package_name: ${{ env.PACKAGE_NAME }} package_version: ${{ env.PACKAGE_VERSION }} From fe718f2201cc9a117f62beb5d4153ed2b61eee92 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Tue, 16 Sep 2025 17:42:52 +0800 Subject: [PATCH 10/18] add the vulnerability report url to the Github issue --- .../action.yml | 0 .github/actions/create-vuln-issue.b/README.md | 19 ++++ .../actions/create-vuln-issue.b/action.yml | 105 ++++++++++++++++++ .github/workflows/release.yml | 2 +- 4 files changed, 125 insertions(+), 1 deletion(-) rename .github/actions/{create-vuln-issue => create-github-issue}/action.yml (100%) create mode 100644 .github/actions/create-vuln-issue.b/README.md create mode 100644 .github/actions/create-vuln-issue.b/action.yml diff --git a/.github/actions/create-vuln-issue/action.yml b/.github/actions/create-github-issue/action.yml similarity index 100% rename from .github/actions/create-vuln-issue/action.yml rename to .github/actions/create-github-issue/action.yml diff --git a/.github/actions/create-vuln-issue.b/README.md b/.github/actions/create-vuln-issue.b/README.md new file mode 100644 index 0000000000..65d82a0bc0 --- /dev/null +++ b/.github/actions/create-vuln-issue.b/README.md @@ -0,0 +1,19 @@ +# Create Vulnerability Issue (local composite action) + +Creates a GitHub Issue **iff** the given vulnerability report file is **non-empty**. + +## Inputs +- `report_path` (required): Path to the vulnerability report, e.g. `vuln-report.txt`. +- `package_name` (required) +- `package_version` (required) +- `release_branch` (required) +- `title_prefix` (optional, default: "Security vulnerabilities detected") +- `labels` (optional, comma-separated, default: "security,automated") +- `artifact_name` (optional): If provided, the action will link to the matching run artifact. + +## Permissions +Job must grant: +```yaml +permissions: + contents: read + issues: write diff --git a/.github/actions/create-vuln-issue.b/action.yml b/.github/actions/create-vuln-issue.b/action.yml new file mode 100644 index 0000000000..eeab7ae70d --- /dev/null +++ b/.github/actions/create-vuln-issue.b/action.yml @@ -0,0 +1,105 @@ +name: "Create Vulnerability Issue (Inline)" +description: "Creates a GitHub issue only if vuln-report.txt contains CRITICAL/HIGH findings, inlining the report." +branding: + icon: alert-triangle + color: red + +inputs: + report_path: + description: "Path to vulnerability report (e.g., vuln-report.txt)" + required: true + package_name: + description: "Package name for context" + required: false + default: "" + package_version: + description: "Package version for context" + required: false + default: "" + release_branch: + description: "Release branch for context" + required: false + default: "" + title_prefix: + description: "Issue title prefix" + required: false + default: "Security vulnerabilities detected" + labels: + description: "Comma-separated labels to apply to the issue" + required: false + default: "security,automated,trivy" + +runs: + using: "composite" + steps: + - name: Inspect report for CRITICAL/HIGH + id: scan + shell: bash + run: | + set -euo pipefail + FILE="${{ inputs.report_path }}" + + if [[ ! -f "$FILE" ]]; then + echo "has_findings=false" >> "$GITHUB_OUTPUT" + echo "❎ Report not found at $FILE. Skipping issue creation." + exit 0 + fi + + if ! [[ -s "$FILE" ]]; then + echo "has_findings=false" >> "$GITHUB_OUTPUT" + echo "βœ… Report exists but is empty. Skipping issue creation." + exit 0 + fi + + # Look for CRITICAL or HIGH (case-insensitive, whole word) + if grep -Eqi '\b(CRITICAL|HIGH)\b' "$FILE"; then + echo "has_findings=true" >> "$GITHUB_OUTPUT" + + # Escape backticks so the code block renders correctly in Markdown + REPORT_ESCAPED=$(sed 's/`/\\`/g' "$FILE") + + { + echo "VULN_BODY<<'EOF_VULN'" + echo "$REPORT_ESCAPED" + echo "EOF_VULN" + } >> "$GITHUB_ENV" + else + echo "has_findings=false" >> "$GITHUB_OUTPUT" + echo "βœ… No CRITICAL/HIGH findings in $FILE. Skipping issue creation." + fi + + - name: Create GitHub issue with inline report + if: steps.scan.outputs.has_findings == 'true' + uses: actions/github-script@v7 + with: + github-token: ${{ github.token }} + script: | + const labelsCsv = `${{ inputs.labels }}`.trim(); + const labels = labelsCsv ? labelsCsv.split(',').map(s => s.trim()).filter(Boolean) : []; + + const pkg = `${{ inputs.package_name }}` || '(unknown package)'; + const version = `${{ inputs.package_version }}` || '(unknown version)'; + const branch = `${{ inputs.release_branch }}` || '(unknown branch)'; + const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`; + const report = process.env.VULN_BODY || '(no report content)'; + + const title = `${{ inputs.title_prefix }} in ${pkg}@${ver}`; + const body = + `Automated scan detected **CRITICAL/HIGH** vulnerabilities.\n\n` + + `**Package:** ${pkg}\n` + + `**Version:** ${version}\n` + + `**Release branch:** ${branch}\n` + + `**Workflow run:** ${runUrl}\n\n` + + `
Trivy Report (table)\n\n` + + '```\n' + report + '\n```\n\n' + + `
\n`; + + const { data: issue } = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title, + body, + labels + }); + + core.info(`βœ… Issue created: ${issue.html_url}`); diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 378494701d..777c63e0a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -183,7 +183,7 @@ jobs: echo "art_url=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.upload_vuln.outputs.artifact-id }}" >> "$GITHUB_OUTPUT" - name: Create issue if CRITICAL/HIGH found - uses: ./.github/actions/create-vuln-issue + uses: ./.github/actions/create-github-issue with: vuln_art_url: ${{ steps.vuln_art.outputs.url }} report_path: vuln-report.txt From 894241f1d83f83ba98654fc1a459d993d4c4dc40 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 17 Sep 2025 10:22:01 +0800 Subject: [PATCH 11/18] fix creating github issue --- .github/workflows/release.yml | 51 ++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 777c63e0a7..9c631f2449 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -182,15 +182,48 @@ jobs: run: | echo "art_url=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.upload_vuln.outputs.artifact-id }}" >> "$GITHUB_OUTPUT" - - name: Create issue if CRITICAL/HIGH found - uses: ./.github/actions/create-github-issue - with: - vuln_art_url: ${{ steps.vuln_art.outputs.url }} - report_path: vuln-report.txt - package_name: ${{ env.PACKAGE_NAME }} - package_version: ${{ env.PACKAGE_VERSION }} - release_branch: ${{ github.event.inputs.release_branch }} - labels: "security" + - name: Check vulnerabilities in report + id: check_vulns + shell: bash + env: + REPORT_PATH: vuln-report.txt # change if different + run: | + set -euo pipefail + if grep -qE "CRITICAL|HIGH" "$REPORT_PATH"; then + echo "found=true" >> "$GITHUB_OUTPUT" + else + echo "found=false" >> "$GITHUB_OUTPUT" + fi + + - name: Create GitHub Issue (links to report artifact) + if: steps.check_vulns.outputs.found == 'true' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + PKG_NAME: ${{ env.PACKAGE_NAME }} + PKG_VER: ${{ env.PACKAGE_VERSION }} + REL_BRANCH: ${{ github.event.inputs.release_branch }} + VULN_ART_URL: ${{ steps.vuln_art.outputs.art_url }} + LABELS: security # or set via env/input if you prefer + run: | + set -euo pipefail + TITLE="πŸ”’ Security vulnerabilities in ${PKG_NAME}@${PKG_VER}" + BODY=$(cat < Date: Wed, 17 Sep 2025 10:32:59 +0800 Subject: [PATCH 12/18] fix creating github issue --- .github/workflows/release.yml | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c631f2449..25f4e1c932 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -205,25 +205,24 @@ jobs: PKG_VER: ${{ env.PACKAGE_VERSION }} REL_BRANCH: ${{ github.event.inputs.release_branch }} VULN_ART_URL: ${{ steps.vuln_art.outputs.art_url }} - LABELS: security # or set via env/input if you prefer + LABELS: security run: | set -euo pipefail TITLE="πŸ”’ Security vulnerabilities in ${PKG_NAME}@${PKG_VER}" - BODY=$(cat < issue_body.md + + echo "The vulnerability scan has detected **CRITICAL/HIGH** vulnerabilities for \`${PKG_NAME}@${PKG_VER}\` on branch \`${REL_BRANCH}\`." >> issue_body.md + echo "" >> issue_body.md + echo "**Release Branch:** \`${REL_BRANCH}\`" >> issue_body.md + echo "**Package Version:** \`${PKG_VER}\`" >> issue_body.md + echo "" >> issue_body.md + echo "**Full vulnerability report:** ${VULN_ART_URL}" >> issue_body.md + echo "" >> issue_body.md + echo "Please review the report and take necessary action." >> issue_body.md + echo "" >> issue_body.md + echo "---" >> issue_body.md + echo "_This issue was automatically generated by the Release Pipeline._" >> issue_body.md + gh issue create --title "$TITLE" --body-file issue_body.md --label "$LABELS" - name: Generate lerna.json for choosen the package run: | From 03b02b46089fc8dcce089bab4db86b3f314e239c Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 17 Sep 2025 12:54:05 +0800 Subject: [PATCH 13/18] fix PR raising for non beta release --- .github/workflows/release.yml | 45 +++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 25f4e1c932..13d54472a2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -270,23 +270,48 @@ jobs: VERSION: ${{ needs.get_version.outputs.package_version }} run: | set -euo pipefail + echo "πŸ”Ž Checking if a PR already exists for $RELEASE_BRANCH β†’ main…" - PR_NUMBER="$(gh pr list --repo "$REPO" --base main --head "$RELEASE_BRANCH" --state open --json number --jq '.[0].number' || true)" + OWNER="${REPO%%/*}" + + # List open PRs with base=main and head=OWNER:RELEASE_BRANCH + PRS_JSON="$(gh api \ + -H 'Accept: application/vnd.github+json' \ + "/repos/$REPO/pulls?state=open&base=main&head=${OWNER}:${RELEASE_BRANCH}")" + + PR_NUMBER="$(printf '%s' "$PRS_JSON" | jq -r '.[0].number // empty')" + PR_URL="$(printf '%s' "$PRS_JSON" | jq -r '.[0].html_url // empty')" + if [ -n "${PR_NUMBER:-}" ]; then echo "ℹ️ PR already exists: #$PR_NUMBER" - PR_URL="https://github.com/$REPO/pull/$PR_NUMBER" else echo "πŸ“ Creating PR for release $VERSION from $RELEASE_BRANCH β†’ main" - PR_URL="$(gh pr create \ - --repo "$REPO" \ - --base main \ - --head "$RELEASE_BRANCH" \ - --title "Release $VERSION: $RELEASE_BRANCH β†’ main" \ - --body "Automated PR for release **$VERSION** from **$RELEASE_BRANCH** β†’ **main**. Workflow Run: https://github.com/$REPO/actions/runs/${{ github.run_id }}" \ - --label release \ - --json url --jq .url)" + CREATE_JSON="$(jq -n \ + --arg title "Release $VERSION: $RELEASE_BRANCH β†’ main" \ + --arg head "$RELEASE_BRANCH" \ + --arg base "main" \ + --arg body "Automated PR for release **$VERSION** from **$RELEASE_BRANCH** β†’ **main**. Workflow Run: https://github.com/$REPO/actions/runs/${{ github.run_id }}" \ + '{title:$title, head:$head, base:$base, body:$body}')" + + RESP="$(gh api \ + -H 'Accept: application/vnd.github+json' \ + --method POST \ + /repos/$REPO/pulls \ + --input <(printf '%s' "$CREATE_JSON"))" + + PR_NUMBER="$(printf '%s' "$RESP" | jq -r '.number')" + PR_URL="$(printf '%s' "$RESP" | jq -r '.html_url')" + + # (Optional) add a label to the PR (labels are an Issues API) + gh api \ + -H 'Accept: application/vnd.github+json' \ + --method POST \ + "/repos/$REPO/issues/$PR_NUMBER/labels" \ + --input <(jq -n --arg l "release" '{labels:[$l]}') >/dev/null || true fi + echo "PR_URL=$PR_URL" >> "$GITHUB_ENV" + echo "βœ… PR URL: $PR_URL" - name: Release summary for review env: From 73b7adefe5e33851e5a3ac4573adf54e17132677 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 17 Sep 2025 13:34:35 +0800 Subject: [PATCH 14/18] fix PR raising for non beta release --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 13d54472a2..3aa10ba36b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -249,6 +249,8 @@ jobs: review: runs-on: ubuntu-latest needs: [get_version, run_faucet_test, run_tests, pre_release] + permissions: + pull-requests: write name: Review test and security scan result env: PACKAGE_VERSION: "${{ needs.get_version.outputs.package_version }}" From 87830bb8db8e81b8ee5888859bdfc8418b22f3ae Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 17 Sep 2025 14:38:12 +0800 Subject: [PATCH 15/18] fix slack msg for release note --- .github/workflows/release.yml | 71 ++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3aa10ba36b..e91667927e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -483,46 +483,83 @@ jobs: generate_release_notes: true make_latest: true - - name: Fetch generated release notes + - name: Fetch release notes id: fetch_notes env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} - RELEASE_ID: ${{ steps.gh_release.outputs.id }} # <- reliable key to fetch by + TAG: ${{ steps.create_tag.outputs.tag_name }} run: | set -euo pipefail - resp="$(curl -sSf \ - -H "Authorization: Bearer $GH_TOKEN" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/$REPO/releases/$RELEASE_ID")" - body="$(printf '%s' "$resp" | jq -r '.body // ""')" - url="$(printf '%s' "$resp" | jq -r '.html_url // ""')" + # URL-encode the tag (handles '@' -> %40, etc.) + enc_tag="$(printf '%s' "$TAG" | jq -sRr @uri)" + endpoint="https://api.github.com/repos/$REPO/releases/tags/$enc_tag" + + tries=8 + sleep_sec=2 + resp_file="$(mktemp)" + + # Retry to tolerate eventual consistency after release creation + for i in $(seq 1 "$tries"); do + code=$(curl -sS -o "$resp_file" -w '%{http_code}' \ + -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "$endpoint") || code=$? + + if [ "$code" = "200" ]; then + break + fi + + if [ "$i" -lt "$tries" ]; then + sleep "$sleep_sec" + else + # fallback: give a valid URL and empty notes + url="https://github.com/$REPO/releases/tag/$enc_tag" + notes="(No release notes were generated.)" + + esc_notes="${notes//'%'/'%25'}" + esc_notes="${esc_notes//$'\n'/'%0A'}" + esc_notes="${esc_notes//$'\r'/'%0D'}" + esc_url="${url//'%'/'%25'}" + + echo "notes=$esc_notes" >> "$GITHUB_OUTPUT" + echo "release_url=$esc_url" >> "$GITHUB_OUTPUT" + exit 0 + fi + done + + body="$(jq -r '.body // ""' < "$resp_file")" + url="$(jq -r '.html_url // ""' < "$resp_file")" - # Fallback if empty and trim if overly long (Slack safety) - [ -z "$body" ] || [ "$body" = "null" ] && body="(No release notes were generated.)" + # Safety: trim very long notes for Slack + [ -z "$body" ] && body="(No release notes were generated.)" max=38000 [ "${#body}" -gt "$max" ] && body="${body:0:$max}\n…(truncated)" + [ -z "$url" ] && url="https://github.com/$REPO/releases/tag/$enc_tag" - esc_body="${body//'%'/'%25'}" - esc_body="${esc_body//$'\n'/'%0A'}" - esc_body="${esc_body//$'\r'/'%0D'}" + # Escape for GITHUB_OUTPUT + esc_notes="${body//'%'/'%25'}" + esc_notes="${esc_notes//$'\n'/'%0A'}" + esc_notes="${esc_notes//$'\r'/'%0D'}" esc_url="${url//'%'/'%25'}" - echo "notes=$esc_body" >> "$GITHUB_OUTPUT" + + echo "notes=$esc_notes" >> "$GITHUB_OUTPUT" echo "release_url=$esc_url" >> "$GITHUB_OUTPUT" - name: Notify Slack success if: success() env: SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }} - TAG: ${{ steps.gh_release.outputs.tag_name }} NOTES: ${{ steps.fetch_notes.outputs.notes }} - RELEASE_URL: ${{ steps.fetch_notes.outputs.release_url }} + URL: ${{ steps.fetch_notes.outputs.release_url }} + TAG: ${{ steps.create_tag.outputs.tag_name }} run: | set -euo pipefail notes="${NOTES//'%0A'/$'\n'}" notes="${notes//'%0D'/}" - text="πŸ“£ New release has been published successfully: ${TAG}\n${RELEASE_URL}\n\n${notes}" + text="πŸ“£ New release: ${TAG}\n${URL}\n\n${notes}" + curl -sS -X POST https://slack.com/api/chat.postMessage \ -H "Authorization: Bearer $SLACK_TOKEN" \ -H "Content-Type: application/json; charset=utf-8" \ From a0346d3ae7a4ea23f8f728874825d4e23d0790a4 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 17 Sep 2025 14:42:59 +0800 Subject: [PATCH 16/18] release xrpl 4.6.7 --- package-lock.json | 7 ++++++- packages/xrpl/package.json | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f8ca81cb3..a0a9343e0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2689,6 +2689,10 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@shichengsh001/xrpl": { + "resolved": "packages/xrpl", + "link": true + }, "node_modules/@shikijs/engine-oniguruma": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.4.2.tgz", @@ -14888,7 +14892,8 @@ } }, "packages/xrpl": { - "version": "4.4.1", + "name": "@shichengsh001/xrpl", + "version": "4.6.7", "license": "ISC", "dependencies": { "@scure/bip32": "^1.3.1", diff --git a/packages/xrpl/package.json b/packages/xrpl/package.json index f9990fd0e0..0934bfddc9 100644 --- a/packages/xrpl/package.json +++ b/packages/xrpl/package.json @@ -1,6 +1,6 @@ { - "name": "xrpl", - "version": "4.4.1", + "name": "@shichengsh001/xrpl", + "version": "4.6.7", "license": "ISC", "description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser", "files": [ @@ -69,7 +69,7 @@ "prettier": "@xrplf/prettier-config", "repository": { "type": "git", - "url": "git@github.com:XRPLF/xrpl.js.git" + "url": "git@github.com:xpring-eng/xrpl.js.git" }, "readmeFilename": "README.md", "keywords": [ From fce03cd58edd3eef4523f9ba45673e18e70fff29 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 17 Sep 2025 18:38:28 +0800 Subject: [PATCH 17/18] release xrpl 4.6.8 --- package-lock.json | 2 +- packages/xrpl/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0a9343e0f..6537d188c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14893,7 +14893,7 @@ }, "packages/xrpl": { "name": "@shichengsh001/xrpl", - "version": "4.6.7", + "version": "4.6.8", "license": "ISC", "dependencies": { "@scure/bip32": "^1.3.1", diff --git a/packages/xrpl/package.json b/packages/xrpl/package.json index 0934bfddc9..e8f57053e1 100644 --- a/packages/xrpl/package.json +++ b/packages/xrpl/package.json @@ -1,6 +1,6 @@ { "name": "@shichengsh001/xrpl", - "version": "4.6.7", + "version": "4.6.8", "license": "ISC", "description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser", "files": [ From de6f2555820e0be5f0a19e17a70c73c517c90a05 Mon Sep 17 00:00:00 2001 From: Shi Cheng Date: Wed, 17 Sep 2025 19:32:24 +0800 Subject: [PATCH 18/18] release xrpl 4.6.9 --- package-lock.json | 2 +- packages/xrpl/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6537d188c1..60ea215cbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14893,7 +14893,7 @@ }, "packages/xrpl": { "name": "@shichengsh001/xrpl", - "version": "4.6.8", + "version": "4.6.9", "license": "ISC", "dependencies": { "@scure/bip32": "^1.3.1", diff --git a/packages/xrpl/package.json b/packages/xrpl/package.json index e8f57053e1..8466ff3af5 100644 --- a/packages/xrpl/package.json +++ b/packages/xrpl/package.json @@ -1,6 +1,6 @@ { "name": "@shichengsh001/xrpl", - "version": "4.6.8", + "version": "4.6.9", "license": "ISC", "description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser", "files": [