NPM Package Release raindex v0.0.1-alpha.247 ui-components v0.0.1-alp… #947
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
| name: NPM Packages Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| concurrency: | |
| group: npm-package-release | |
| cancel-in-progress: false | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write | |
| contents: write | |
| env: | |
| DEPLOYMENT_KEY: ${{ secrets.PRIVATE_KEY }} | |
| CI_FORK_SEPOLIA_BLOCK_NUMBER: ${{ vars.CI_FORK_SEPOLIA_BLOCK_NUMBER }} | |
| CI_FORK_SEPOLIA_DEPLOYER_ADDRESS: ${{ vars.CI_FORK_SEPOLIA_DEPLOYER_ADDRESS }} | |
| CI_DEPLOY_SEPOLIA_RPC_URL: ${{ secrets.CI_DEPLOY_SEPOLIA_RPC_URL || vars.CI_DEPLOY_SEPOLIA_RPC_URL }} | |
| CI_SEPOLIA_METABOARD_URL: ${{ vars.CI_SEPOLIA_METABOARD_URL }} | |
| CI_DEPLOY_ARBITRUM_RPC_URL: ${{ secrets.CI_DEPLOY_ARBITRUM_RPC_URL }} | |
| CI_DEPLOY_BASE_RPC_URL: ${{ secrets.CI_DEPLOY_BASE_RPC_URL }} | |
| CI_DEPLOY_BASE_SEPOLIA_RPC_URL: ${{ secrets.CI_DEPLOY_BASE_SEPOLIA_RPC_URL }} | |
| CI_DEPLOY_POLYGON_RPC_URL: ${{ secrets.CI_DEPLOY_POLYGON_RPC_URL }} | |
| CI_DEPLOY_FLARE_RPC_URL: ${{ secrets.CI_DEPLOY_FLARE_RPC_URL }} | |
| RPC_URL_ETHEREUM_FORK: ${{ secrets.RPC_URL_ETHEREUM_FORK }} | |
| COMMIT_SHA: ${{ github.sha }} | |
| steps: | |
| # checkout with SSH key to allow pushing version bump commits back to repo | |
| - uses: actions/checkout@v4 | |
| with: | |
| ssh-key: ${{ secrets.PUBLISH_PRIVATE_KEY }} | |
| # WASM builds require significant disk space; free up space to prevent build failures | |
| - name: Free disk space | |
| uses: jlumbroso/free-disk-space@v1.3.1 | |
| with: | |
| swap-storage: false | |
| # install nix for building WASM artifacts and running tests | |
| - uses: nixbuild/nix-quick-install-action@v30 | |
| with: | |
| nix_conf: | | |
| keep-env-derivations = true | |
| keep-outputs = true | |
| # pull rainix derivations from shared Cachix; push new ones if the | |
| # token is set. continue-on-error so a token miss / Cachix outage | |
| # degrades gracefully. | |
| - uses: cachix/cachix-action@v15 | |
| continue-on-error: true | |
| with: | |
| name: rainlanguage | |
| authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} | |
| useDaemon: false | |
| - name: Restore and save Nix store | |
| uses: nix-community/cache-nix-action@v7 | |
| with: | |
| primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} | |
| restore-prefixes-first-match: nix-${{ runner.os }}- | |
| gc-max-store-size-linux: 8G | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| prefix-key: rust-${{ github.workflow }} | |
| - name: Cache npm | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.npm | |
| key: npm-${{ runner.os }}-${{ github.workflow }}-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: npm-${{ runner.os }}- | |
| # setup node with npm registry for OIDC-based publishing (no NPM_TOKEN needed) | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24.x" | |
| registry-url: "https://registry.npmjs.org" | |
| # npm 10.9+ required for OIDC provenance support | |
| - name: Upgrade npm for OIDC | |
| run: | | |
| npm install -g npm@latest | |
| npm --version | |
| # ensure GitHub Actions OIDC token is available for npm provenance attestation | |
| - name: Verify OIDC availability | |
| run: | | |
| if [ -n "${ACTIONS_ID_TOKEN_REQUEST_URL}" ] && [ -n "${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" ]; then | |
| echo "OIDC token available" | |
| echo "Endpoint: ${ACTIONS_ID_TOKEN_REQUEST_URL}" | |
| else | |
| echo "OIDC token NOT available" | |
| echo "Check workflow permissions include 'id-token: write'" | |
| exit 1 | |
| fi | |
| # npm OIDC publish requires package.json repository URL to match git remote | |
| # mismatch causes 422 error during publish | |
| - name: Verify repository configuration | |
| run: | | |
| echo "Checking repository consistency..." | |
| GIT_REPO=$(git remote get-url origin | sed 's/.*github.com[/:]//; s/.git$//') | |
| DEX_PKG_REPO=$(node -e "console.log(require('./packages/raindex/package.json').repository?.url || '')" | sed 's|https://github.com/||; s|git+||; s|.git$||') | |
| UC_PKG_REPO=$(node -e "console.log(require('./packages/ui-components/package.json').repository?.url || '')" | sed 's|https://github.com/||; s|git+||; s|.git$||') | |
| echo "Git remote: $GIT_REPO" | |
| echo "raindex package.json: $DEX_PKG_REPO" | |
| echo "ui-components package.json: $UC_PKG_REPO" | |
| if [ "$GIT_REPO" != "$DEX_PKG_REPO" ]; then | |
| echo "Repository mismatch for raindex!" | |
| echo "This will cause 422 error during publish" | |
| exit 1 | |
| fi | |
| if [ "$GIT_REPO" != "$UC_PKG_REPO" ]; then | |
| echo "Repository mismatch for ui-components!" | |
| echo "This will cause 422 error during publish" | |
| exit 1 | |
| fi | |
| echo "Repositories match" | |
| # forge install + build is no longer needed here — all sol! macros | |
| # read committed ABIs under crates/*/abis/, and the wasm + npm builds | |
| # below don't shell out to forge. The prior "Remove Unused Artifacts" | |
| # / debug-dir scrubs went with forge; rust-cache + free-disk-space@v1 | |
| # handle disk budget for the remaining WASM builds. | |
| # WASM linker can run out of memory on GitHub runners; add swap to prevent OOM | |
| - name: Add swap space | |
| run: | | |
| if [ -f /swapfile ]; then | |
| sudo swapoff /swapfile 2>/dev/null || true | |
| sudo rm -f /swapfile | |
| fi | |
| sudo fallocate -l 8G /swapfile | |
| sudo chmod 600 /swapfile | |
| sudo mkswap /swapfile | |
| sudo swapon /swapfile | |
| # build and test WASM bindings | |
| - name: Test JS/TS Binding 1/2 | |
| run: | | |
| nix develop .#wasm-shell -c bash -c "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner cargo test --target wasm32-unknown-unknown --lib -p raindex_quote -p raindex_bindings -p raindex_js_api -p raindex_common" | |
| # run JS/TS integration tests against WASM bindings | |
| - name: Test JS/TS Binding 2/2 | |
| run: | | |
| nix develop .#wasm-shell -c bash -c ' | |
| set -euxo pipefail | |
| npm install --no-check | |
| cd packages/raindex | |
| npm run build | |
| npm test | |
| ' | |
| # build and test UI components before publishing | |
| - name: Build UI Components | |
| run: nix develop .#wasm-shell -c npm run build -w @rainlanguage/ui-components | |
| - name: Test UI Components | |
| run: nix develop .#wasm-shell -c npm run test -w @rainlanguage/ui-components | |
| # check for npm package blacklists pkgs across all raindex related packages | |
| - uses: rainlanguage/github-chore/.github/actions/npm-blacklist@main | |
| - uses: rainlanguage/github-chore/.github/actions/npm-blacklist@main | |
| with: | |
| working-directory: packages/raindex | |
| - uses: rainlanguage/github-chore/.github/actions/npm-blacklist@main | |
| with: | |
| working-directory: packages/ui-components | |
| # configure git identity for version bump commits | |
| - name: Git Config | |
| run: | | |
| git config --global user.email "${{ secrets.CI_GIT_EMAIL }}" | |
| git config --global user.name "${{ secrets.CI_GIT_USER }}" | |
| # A release must only publish the current main head. Newer pushes queue a | |
| # fresh run through the workflow-level concurrency group. | |
| - name: Check release head | |
| run: | | |
| git fetch origin main | |
| if [ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ]; then | |
| echo "RELEASE_CURRENT=true" >> "$GITHUB_ENV" | |
| else | |
| echo "RELEASE_CURRENT=false" >> "$GITHUB_ENV" | |
| echo "Skipping superseded commit $(git rev-parse HEAD); current main is $(git rev-parse origin/main)" | |
| fi | |
| # Reconcile Git and npm before choosing versions. This makes the release | |
| # recoverable whether a prior attempt committed first, published first, | |
| # or only published one of the two packages. | |
| - name: Resolve package release state | |
| if: ${{ env.RELEASE_CURRENT == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| highest_published_version() { | |
| local package_name="$1" | |
| local versions | |
| if ! versions=$(npm view "$package_name" versions --json 2>/dev/null); then | |
| return 1 | |
| fi | |
| printf '%s' "$versions" | node -e ' | |
| const fs = require("fs"); | |
| const semver = require("semver"); | |
| const versions = JSON.parse(fs.readFileSync(0, "utf8")); | |
| const highest = semver.maxSatisfying( | |
| Array.isArray(versions) ? versions : [versions], | |
| "*", | |
| { includePrerelease: true }, | |
| ); | |
| if (!highest) process.exit(1); | |
| process.stdout.write(highest); | |
| ' | |
| } | |
| higher_version() { | |
| local repository_version="$1" | |
| local registry_version="$2" | |
| if [ -z "$registry_version" ]; then | |
| echo "$repository_version" | |
| return | |
| fi | |
| node -e ' | |
| const semver = require("semver"); | |
| process.stdout.write( | |
| semver.gte(process.argv[1], process.argv[2]) | |
| ? process.argv[1] | |
| : process.argv[2], | |
| ); | |
| ' "$repository_version" "$registry_version" | |
| } | |
| package_hash() { | |
| local workspace="$1" | |
| local tarball | |
| tarball=$(npm pack --silent -w "$workspace") | |
| shasum "$tarball" | cut -c1-40 | |
| rm -f "$tarball" | |
| } | |
| select_unpublished_prerelease() { | |
| local workspace="$1" | |
| local package_name="$2" | |
| local manifest="$3" | |
| local candidate | |
| while true; do | |
| npm version prerelease --preid alpha --no-git-tag-version -w "$workspace" >/dev/null | |
| candidate=$(jq -r '.version' "$manifest") | |
| if ! npm view "$package_name@$candidate" version >/dev/null 2>&1; then | |
| return | |
| fi | |
| echo "$package_name@$candidate already exists; trying the next prerelease" | |
| done | |
| } | |
| RAINDEX_REPOSITORY_VERSION=$(jq -r '.version' packages/raindex/package.json) | |
| RAINDEX_HIGHEST_PUBLISHED_VERSION=$(highest_published_version @rainlanguage/raindex || true) | |
| if [ -n "$RAINDEX_HIGHEST_PUBLISHED_VERSION" ]; then | |
| RAINDEX_FIRST_PUBLISH=false | |
| else | |
| RAINDEX_FIRST_PUBLISH=true | |
| fi | |
| RAINDEX_BASELINE_VERSION=$(higher_version "$RAINDEX_REPOSITORY_VERSION" "$RAINDEX_HIGHEST_PUBLISHED_VERSION") | |
| npm version "$RAINDEX_BASELINE_VERSION" --allow-same-version --no-git-tag-version -w @rainlanguage/raindex | |
| RAINDEX_LOCAL_HASH=$(package_hash @rainlanguage/raindex) | |
| RAINDEX_PUBLISHED_HASH=$(npm view "@rainlanguage/raindex@$RAINDEX_BASELINE_VERSION" dist.shasum 2>/dev/null || true) | |
| if [ -z "$RAINDEX_PUBLISHED_HASH" ]; then | |
| RAINDEX_PUBLISH=true | |
| elif [ "$RAINDEX_LOCAL_HASH" = "$RAINDEX_PUBLISHED_HASH" ]; then | |
| RAINDEX_PUBLISH=false | |
| else | |
| select_unpublished_prerelease @rainlanguage/raindex @rainlanguage/raindex packages/raindex/package.json | |
| RAINDEX_PUBLISH=true | |
| fi | |
| RAINDEX_NEW_VERSION=$(jq -r '.version' packages/raindex/package.json) | |
| UC_REPOSITORY_VERSION=$(jq -r '.version' packages/ui-components/package.json) | |
| UC_HIGHEST_PUBLISHED_VERSION=$(highest_published_version @rainlanguage/ui-components || true) | |
| UC_BASELINE_VERSION=$(higher_version "$UC_REPOSITORY_VERSION" "$UC_HIGHEST_PUBLISHED_VERSION") | |
| npm version "$UC_BASELINE_VERSION" --allow-same-version --no-git-tag-version -w @rainlanguage/ui-components | |
| jq --arg version "$RAINDEX_NEW_VERSION" '.dependencies."@rainlanguage/raindex" = $version' packages/ui-components/package.json > tmp.json | |
| mv tmp.json packages/ui-components/package.json | |
| npx prettier --write packages/ui-components/package.json | |
| UC_LOCAL_HASH=$(package_hash @rainlanguage/ui-components) | |
| UC_PUBLISHED_HASH=$(npm view "@rainlanguage/ui-components@$UC_BASELINE_VERSION" dist.shasum 2>/dev/null || true) | |
| if [ -z "$UC_PUBLISHED_HASH" ]; then | |
| UC_PUBLISH=true | |
| elif [ "$UC_LOCAL_HASH" = "$UC_PUBLISHED_HASH" ]; then | |
| UC_PUBLISH=false | |
| else | |
| select_unpublished_prerelease @rainlanguage/ui-components @rainlanguage/ui-components packages/ui-components/package.json | |
| UC_PUBLISH=true | |
| fi | |
| UC_NEW_VERSION=$(jq -r '.version' packages/ui-components/package.json) | |
| jq --indent 4 --arg rd "$RAINDEX_NEW_VERSION" --arg uc "$UC_NEW_VERSION" ' | |
| .packages."packages/raindex".version = $rd | | |
| .packages."packages/ui-components".version = $uc | | |
| .packages."packages/ui-components".dependencies."@rainlanguage/raindex" = $rd | |
| ' package-lock.json > tmp.json | |
| mv tmp.json package-lock.json | |
| if git diff --quiet -- packages/raindex/package.json packages/ui-components/package.json package-lock.json; then | |
| VERSION_COMMIT_REQUIRED=false | |
| else | |
| VERSION_COMMIT_REQUIRED=true | |
| fi | |
| RELEASE_TAG="npm-raindex-v$RAINDEX_NEW_VERSION-uc-v$UC_NEW_VERSION" | |
| if git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null 2>&1; then | |
| RELEASE_TAG_EXISTS=true | |
| else | |
| RELEASE_TAG_EXISTS=false | |
| fi | |
| if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then | |
| GITHUB_RELEASE_EXISTS=true | |
| else | |
| GITHUB_RELEASE_EXISTS=false | |
| fi | |
| if [ "$RAINDEX_PUBLISH" = true ] || [ "$UC_PUBLISH" = true ] || [ "$VERSION_COMMIT_REQUIRED" = true ] || [ "$RELEASE_TAG_EXISTS" = false ] || [ "$GITHUB_RELEASE_EXISTS" = false ]; then | |
| RELEASE_REQUIRED=true | |
| else | |
| RELEASE_REQUIRED=false | |
| fi | |
| { | |
| echo "RAINDEX_NEW_VERSION=$RAINDEX_NEW_VERSION" | |
| echo "UC_NEW_VERSION=$UC_NEW_VERSION" | |
| echo "RAINDEX_PUBLISH=$RAINDEX_PUBLISH" | |
| echo "RAINDEX_FIRST_PUBLISH=$RAINDEX_FIRST_PUBLISH" | |
| echo "UC_PUBLISH=$UC_PUBLISH" | |
| echo "VERSION_COMMIT_REQUIRED=$VERSION_COMMIT_REQUIRED" | |
| echo "RELEASE_TAG=$RELEASE_TAG" | |
| echo "RELEASE_TAG_EXISTS=$RELEASE_TAG_EXISTS" | |
| echo "GITHUB_RELEASE_EXISTS=$GITHUB_RELEASE_EXISTS" | |
| echo "RELEASE_REQUIRED=$RELEASE_REQUIRED" | |
| } >> "$GITHUB_ENV" | |
| echo "raindex: $RAINDEX_NEW_VERSION (publish: $RAINDEX_PUBLISH)" | |
| echo "ui-components: $UC_NEW_VERSION (publish: $UC_PUBLISH)" | |
| echo "release required: $RELEASE_REQUIRED" | |
| # Persist version reservations before publishing. If main moves, this | |
| # fails safely before npm is changed and the newer queued run takes over. | |
| - name: Commit package versions | |
| if: ${{ env.RELEASE_REQUIRED == 'true' && env.VERSION_COMMIT_REQUIRED == 'true' }} | |
| env: | |
| RAINDEX_NEW_VERSION: ${{ env.RAINDEX_NEW_VERSION }} | |
| UC_NEW_VERSION: ${{ env.UC_NEW_VERSION }} | |
| run: | | |
| git add packages/raindex/package.json packages/ui-components/package.json package-lock.json | |
| git commit -m "NPM Package Release raindex v$RAINDEX_NEW_VERSION ui-components v$UC_NEW_VERSION" | |
| git push origin HEAD:main | |
| # Create raindex npm package tarball | |
| - name: Create raindex NPM Package Tarball | |
| if: ${{ env.RELEASE_REQUIRED == 'true' }} | |
| run: echo "RAINDEX_NPM_PACKAGE=$(npm pack --silent -w @rainlanguage/raindex)" >> "$GITHUB_ENV" | |
| - name: Rename raindex NPM Package Tarball | |
| if: ${{ env.RELEASE_REQUIRED == 'true' }} | |
| env: | |
| RAINDEX_NPM_PACKAGE: ${{ env.RAINDEX_NPM_PACKAGE }} | |
| RAINDEX_NEW_VERSION: ${{ env.RAINDEX_NEW_VERSION }} | |
| run: mv "$RAINDEX_NPM_PACKAGE" "raindex_npm_package_$RAINDEX_NEW_VERSION.tgz" | |
| # First publish uses NPM_TOKEN because trusted publishing cannot be | |
| # configured until the package exists. | |
| - name: First Publish raindex pkg To NPM | |
| if: ${{ env.RAINDEX_PUBLISH == 'true' && env.RAINDEX_FIRST_PUBLISH == 'true' }} | |
| uses: JS-DevTools/npm-publish@v3 | |
| with: | |
| token: ${{ secrets.NPM_TOKEN }} | |
| access: public | |
| package: raindex_npm_package_${{ env.RAINDEX_NEW_VERSION }}.tgz | |
| tag: latest | |
| # publish raindex pkg to npm | |
| - name: Publish raindex pkg To NPM | |
| if: ${{ env.RAINDEX_PUBLISH == 'true' && env.RAINDEX_FIRST_PUBLISH == 'false' }} | |
| env: | |
| RAINDEX_NEW_VERSION: ${{ env.RAINDEX_NEW_VERSION }} | |
| run: | | |
| npm publish "raindex_npm_package_$RAINDEX_NEW_VERSION.tgz" \ | |
| --access public \ | |
| --tag latest \ | |
| --verbose | |
| # Create npm package tarball for ui-components | |
| - name: Create ui-components NPM Package Tarball | |
| if: ${{ env.RELEASE_REQUIRED == 'true' }} | |
| run: echo "UC_NPM_PACKAGE=$(npm pack --silent -w @rainlanguage/ui-components)" >> "$GITHUB_ENV" | |
| - name: Rename ui-components NPM Package Tarball | |
| if: ${{ env.RELEASE_REQUIRED == 'true' }} | |
| env: | |
| UC_NPM_PACKAGE: ${{ env.UC_NPM_PACKAGE }} | |
| UC_NEW_VERSION: ${{ env.UC_NEW_VERSION }} | |
| run: mv "$UC_NPM_PACKAGE" "ui_components_npm_package_$UC_NEW_VERSION.tgz" | |
| # publish ui-components to npm | |
| - name: Publish ui-components To NPM | |
| if: ${{ env.UC_PUBLISH == 'true' }} | |
| env: | |
| UC_NEW_VERSION: ${{ env.UC_NEW_VERSION }} | |
| run: | | |
| npm publish "ui_components_npm_package_$UC_NEW_VERSION.tgz" \ | |
| --access public \ | |
| --tag latest \ | |
| --verbose | |
| # Tags are created only after both required publishes succeed. Existing | |
| # tags are retained when recovering a missing GitHub release. | |
| - name: Push release tag | |
| if: ${{ env.RELEASE_REQUIRED == 'true' }} | |
| env: | |
| RELEASE_TAG: ${{ env.RELEASE_TAG }} | |
| RELEASE_TAG_EXISTS: ${{ env.RELEASE_TAG_EXISTS }} | |
| run: | | |
| if [ "$RELEASE_TAG_EXISTS" = false ]; then | |
| git tag "$RELEASE_TAG" | |
| git push origin "$RELEASE_TAG" | |
| else | |
| echo "Tag $RELEASE_TAG already exists" | |
| fi | |
| # Create gitHub release with tarballs | |
| - name: Create GitHub Release | |
| if: ${{ env.RELEASE_REQUIRED == 'true' && env.GITHUB_RELEASE_EXISTS == 'false' }} | |
| id: gh_release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ env.RELEASE_TAG }} | |
| name: NPM Package Release raindex v${{ env.RAINDEX_NEW_VERSION }} ui-components v${{ env.UC_NEW_VERSION }} | |
| files: | | |
| raindex_npm_package_${{ env.RAINDEX_NEW_VERSION }}.tgz | |
| ui_components_npm_package_${{ env.UC_NEW_VERSION }}.tgz | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # forwards status to telegram chat if this ci fails or gets canceled, only runs for default branch | |
| - name: Forward CI Status | |
| if: always() | |
| uses: rainlanguage/github-chore/.github/actions/telegram-status-report@main | |
| with: | |
| status: ${{ job.status }} | |
| telegram-bot-token: ${{ secrets.TELEGRAM_BOT_TOKEN }} | |
| telegram-chat-id: ${{ secrets.TELEGRAM_CHAT_ID }} |