Skip to content

NPM Package Release raindex v0.0.1-alpha.247 ui-components v0.0.1-alp… #947

NPM Package Release raindex v0.0.1-alpha.247 ui-components v0.0.1-alp…

NPM Package Release raindex v0.0.1-alpha.247 ui-components v0.0.1-alp… #947

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 }}