diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 9cdc577c..d9a8565f 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,9 +1,29 @@ name: "Setup Environment" -description: "Sets up the environment with yarn, Node.js, and turbo" +description: "Sets up the environment with yarn, Node.js, turbo, and Compact compiler" + +inputs: + skip-compact: + description: "Skip Compact compiler installation" + required: false + default: "false" + +outputs: + compact-home: + description: "Path to Compact compiler installation" + value: ${{ steps.compact-outputs.outputs.compact-home }} + compact-version: + description: "Installed Compact compiler version" + value: ${{ steps.compact-outputs.outputs.version }} runs: using: "composite" steps: + - name: Set shared environment variables + shell: bash + run: | + echo "COMPILER_VERSION=0.24.0" >> $GITHUB_ENV + echo "LANGUAGE_VERSION=0.16.0" >> $GITHUB_ENV + - name: Get yarn cache directory path shell: bash id: yarn-cache-dir-path @@ -25,6 +45,14 @@ runs: restore-keys: | ${{ runner.os }}-turbo-${{ hashFiles('.turbo/*') }} + - name: Cache Compact compiler + if: inputs.skip-compact != 'true' + uses: actions/cache@v4 + id: compact-cache + with: + path: ~/compactc + key: compact-compiler-${{ env.COMPILER_VERSION }}-${{ runner.os }} + - name: Setup Node.js uses: actions/setup-node@v4 with: @@ -40,5 +68,86 @@ runs: env: TURBO_MAJOR_VERSION: 2 TURBO_TELEMETRY_DISABLED: 1 + run: npm install turbo@${{ env.TURBO_MAJOR_VERSION }} -g + + - name: Install Compact compiler + if: inputs.skip-compact != 'true' && steps.compact-cache.outputs.cache-hit != 'true' + shell: bash + run: | + set -euo pipefail + + COMPACT_HOME="$HOME/compactc" + COMPACT_ZIP_DIR="$HOME/compactc_download" + + echo "๐ง Installing Compact compiler v$COMPILER_VERSION..." + + mkdir -p "$COMPACT_HOME" + mkdir -p "$COMPACT_ZIP_DIR" + + ZIP_FILE="compactc_v${COMPILER_VERSION}_x86_64-unknown-linux-musl.zip" + DOWNLOAD_URL="https://d3fazakqrumx6p.cloudfront.net/artifacts/compiler/compactc_${COMPILER_VERSION}/${ZIP_FILE}" + + echo "โฌ๏ธ Downloading Compact compiler from $DOWNLOAD_URL..." + curl -fLs "$DOWNLOAD_URL" -o "$COMPACT_ZIP_DIR/compactc.zip" + + echo "๐งช Validating ZIP archive..." + if ! unzip -tq "$COMPACT_ZIP_DIR/compactc.zip"; then + echo "::error::โ ZIP file is invalid or corrupted." + exit 1 + fi + + echo "๐ฆ Extracting Compact compiler..." + unzip -q "$COMPACT_ZIP_DIR/compactc.zip" -d "$COMPACT_HOME" + chmod +x "$COMPACT_HOME"/{compactc,compactc.bin,zkir} + + echo "โ Compact compiler extracted to $COMPACT_HOME" + + - name: Setup Compact environment + if: inputs.skip-compact != 'true' + shell: bash + run: | + COMPACT_HOME="$HOME/compactc" + echo "๐ Setting Compact environment variables..." + echo "COMPACT_HOME=$COMPACT_HOME" >> "$GITHUB_ENV" + echo "$COMPACT_HOME" >> "$GITHUB_PATH" + + if [ -f "$COMPACT_HOME/compactc" ]; then + echo "โ Compact compiler is installed at $COMPACT_HOME" + else + echo "::error::โ Compact compiler not found in $COMPACT_HOME" + exit 1 + fi + + - name: Set Compact outputs + if: inputs.skip-compact != 'true' + id: compact-outputs + shell: bash + run: | + echo "compact-home=$HOME/compactc" >> $GITHUB_OUTPUT + echo "version=$COMPILER_VERSION" >> $GITHUB_OUTPUT + + - name: Check compiler and language version + if: inputs.skip-compact != 'true' + shell: bash run: | - npm install turbo@${{ env.TURBO_MAJOR_VERSION }} -g + set -euo pipefail + + echo "๐ Checking Compact compiler version..." + COMPILER_OUTPUT=$(compactc --version) + COMPUTED_COMPILER_VERSION=$(echo "$COMPILER_OUTPUT" | grep -oP '\b0\.[0-9]+\.[0-9]+\b' | head -n 1) + + if [ "$COMPUTED_COMPILER_VERSION" != "$COMPILER_VERSION" ]; then + echo "::error::โ Compiler version mismatch!%0AExpected: $COMPILER_VERSION%0AGot: $COMPUTED_COMPILER_VERSION" + exit 1 + fi + echo "โ Compiler version matches: $COMPUTED_COMPILER_VERSION" + + echo "๐ Checking Compact language version..." + LANGUAGE_OUTPUT=$(compactc --language-version) + COMPUTED_LANGUAGE_VERSION=$(echo "$LANGUAGE_OUTPUT" | grep -oP '\b0\.[0-9]+\.[0-9]+\b' | tail -n 1) + + if [ "$COMPUTED_LANGUAGE_VERSION" != "$LANGUAGE_VERSION" ]; then + echo "::error::โ Language version mismatch!%0AExpected: $LANGUAGE_VERSION%0AGot: $COMPUTED_LANGUAGE_VERSION" + exit 1 + fi + echo "โ Language version matches: $COMPUTED_LANGUAGE_VERSION" diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 00000000..82b1cc93 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,60 @@ +name: Update version on new release branch + +on: + create: + +permissions: + contents: write + pull-requests: write + +jobs: + update_version: + if: github.ref_type == 'branch' && startsWith(github.ref, 'refs/heads/release-v') + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Extract current version + run: | + CURRENT_VERSION=$(node -p "require('./contracts/package.json').version") + echo "CURRENT_VERSION=$CURRENT_VERSION" >> "$GITHUB_ENV" + + - name: Extract new version number + run: echo "NEW_VERSION=${GITHUB_REF#refs/heads/release-v}" >> "$GITHUB_ENV" + + - name: Replace version in files + run: | + echo "Current version: $CURRENT_VERSION" + echo "New version: $NEW_VERSION" + + # Update package.json version field manually + cd contracts + node -e " + const fs = require('fs'); + const pkg = require('./package.json'); + pkg.version = '$NEW_VERSION'; + fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n'); + console.log('Updated package.json to version $NEW_VERSION'); + " + # Update yarn.lock to reflect the new version + yarn install + cd .. + + # Escape special characters for sed + ESCAPED_CURRENT=$(printf '%s' "$CURRENT_VERSION" | sed -e 's/[\/&]/\\&/g') + ESCAPED_NEW=$(printf '%s' "$NEW_VERSION" | sed -e 's/[\/&]/\\&/g') + + # Replace version in contracts/src/ + find ./contracts/src/ -type d -name '.*' -prune -o \ + -type f -exec sed -i "s#$ESCAPED_CURRENT#$ESCAPED_NEW#g" {} + + + # Replace version in docs/, excluding package-lock.json + find ./docs/ -type d -name '.*' -prune -o \ + -type f ! -name 'package-lock.json' -exec sed -i "s#$ESCAPED_CURRENT#$ESCAPED_NEW#g" {} + + + - name: Auto-commit changes + uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 #v6.0.1 + with: + commit_message: Bump version to ${{ env.NEW_VERSION }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..7a57be73 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,94 @@ +name: Publish Package on Release + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Environment + uses: ./.github/actions/setup + + - name: Build contracts + run: turbo build --filter=!'docs' + + - name: Validate version consistency + run: | + RELEASE_VERSION=${GITHUB_REF#refs/tags/v} + PACKAGE_VERSION=$(node -p "require('./contracts/package.json').version") + if [ "$RELEASE_VERSION" != "$PACKAGE_VERSION" ]; then + echo "โ Version mismatch: Release $RELEASE_VERSION vs Package $PACKAGE_VERSION" + exit 1 + fi + echo "โ Version consistency validated: $RELEASE_VERSION" + + - name: Setup npm registry + uses: actions/setup-node@v4 + with: + registry-url: 'https://registry.npmjs.org' + + - name: Pack tarball + id: pack + run: | + cd contracts/dist + TARBALL=$(npm pack | tail -1) + echo "tarball_name=$TARBALL" >> $GITHUB_OUTPUT + echo "tarball=$(pwd)/$TARBALL" >> $GITHUB_OUTPUT + + # Determine dist-tag based on semver prerelease + PACKAGE_VERSION=$(node -p "require('./package.json').version") + if [[ "$PACKAGE_VERSION" =~ -.*$ ]]; then + # Has prerelease suffix (anything after -) + if [[ "$PACKAGE_VERSION" =~ -(alpha|beta|rc) ]]; then + echo "tag=beta" >> $GITHUB_OUTPUT + else + echo "tag=next" >> $GITHUB_OUTPUT + fi + else + # Stable release + echo "tag=latest" >> $GITHUB_OUTPUT + fi + + - name: Verify tarball integrity + run: | + echo "=== Verifying tarball contents ===" + PACKAGE_NAME=$(tar xfO "${{ steps.pack.outputs.tarball }}" package/package.json | jq -r .name) + PACKAGE_VERSION=$(tar xfO "${{ steps.pack.outputs.tarball }}" package/package.json | jq -r .version) + PRIVATE_FIELD=$(tar xfO "${{ steps.pack.outputs.tarball }}" package/package.json | jq -r '.private // "not found"') + + echo "๐ฆ Package: $PACKAGE_NAME@$PACKAGE_VERSION" + echo "๐ท๏ธ Tag: ${{ steps.pack.outputs.tag }}" + echo "๐ Private field: $PRIVATE_FIELD" + + # Ensure no private field + if [ "$PRIVATE_FIELD" = "true" ]; then + echo "โ Tarball contains private: true - cannot publish" + exit 1 + fi + + - name: Publish to npm + run: | + # Create .npmrc with auth token + echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc + + # Publish the tarball with appropriate tag + npm publish "${{ steps.pack.outputs.tarball }}" --tag "${{ steps.pack.outputs.tag }}" --access public + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: true + + - name: Log success + run: | + PACKAGE_NAME=$(tar xfO "${{ steps.pack.outputs.tarball }}" package/package.json | jq -r .name) + PACKAGE_VERSION=$(tar xfO "${{ steps.pack.outputs.tarball }}" package/package.json | jq -r .version) + echo "โ Successfully published $PACKAGE_NAME@$PACKAGE_VERSION to npm with tag ${{ steps.pack.outputs.tag }}" + echo "๐ฆ Install with: npm install $PACKAGE_NAME@${{ steps.pack.outputs.tag }}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb523efd..a21f657b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,11 +6,6 @@ on: branches: - main -env: - TURBO_TELEMETRY_DISABLED: 1 - COMPILER_VERSION: "0.24.0" - LANGUAGE_VERSION: "0.16.0" - jobs: run-suite: name: Run Test Suite @@ -26,82 +21,8 @@ jobs: - name: Setup Environment uses: ./.github/actions/setup - - name: Install Compact compiler - id: setup - shell: bash - run: | - set -euo pipefail - # Create directory for compiler - COMPACT_HOME="$HOME/compactc" - mkdir -p "$COMPACT_HOME" - - # Create URL - ZIP_FILE="compactc_v${COMPILER_VERSION}_x86_64-unknown-linux-musl.zip" - DOWNLOAD_URL="https://d3fazakqrumx6p.cloudfront.net/artifacts/compiler/compactc_${COMPILER_VERSION}/${ZIP_FILE}" - - echo "โฌ๏ธ Downloading Compact compiler..." - curl -Ls "$DOWNLOAD_URL" -o "$COMPACT_HOME/compactc.zip" - - echo "๐ฆ Extracting..." - unzip -q "$COMPACT_HOME/compactc.zip" -d "$COMPACT_HOME" - chmod +x "$COMPACT_HOME"/{compactc,compactc.bin,zkir} - - echo "๐ Setting environment variables..." - echo "COMPACT_HOME=$COMPACT_HOME" >> "$GITHUB_ENV" - echo "$COMPACT_HOME" >> "$GITHUB_PATH" - - echo "โ Verifying installation..." - if [ ! -f "$COMPACT_HOME/compactc" ]; then - echo "::error::โ compactc not found in $COMPACT_HOME" - exit 1 - fi - - echo "๐ค Testing installation..." - "$COMPACT_HOME/compactc" --version - - - name: Check compiler and language version - run: | - COMPILER_OUTPUT=$(compactc --version) - COMPUTED_COMPILER_VERSION=$(echo "$COMPILER_OUTPUT" | grep -oP '\b0\.[0-9]+\.[0-9]+\b' | head -n 1) - if [ "$COMPUTED_COMPILER_VERSION" != "$COMPILER_VERSION" ]; then - errMsg="โ Compiler version mismatch!%0AExpected: $COMPILER_VERSION%0AGot: $COMPUTED_COMPILER_VERSION" - echo "::error::$errMsg" - exit 1 - fi - echo "โ Compiler version matches: $COMPUTED_COMPILER_VERSION" - - LANGUAGE_OUTPUT=$(compactc --language-version) - COMPUTED_LANGUAGE_VERSION=$(echo "$LANGUAGE_OUTPUT" | grep -oP '\b0\.[0-9]+\.[0-9]+\b' | tail -n 1) - if [ "$COMPUTED_LANGUAGE_VERSION" != "$LANGUAGE_VERSION" ]; then - errMsg="โ Language version mismatch!%0AExpected: $LANGUAGE_VERSION%0AGot: $COMPUTED_LANGUAGE_VERSION" - echo "::error::$errMsg" - exit 1 - fi - - echo "โ Language version matches: $COMPUTED_LANGUAGE_VERSION" - - - name: Compile contracts (with retry on hash mismatch) - shell: bash - run: | - set -euo pipefail - - compile() { - echo "โ๏ธ Running Compact compilation..." - if ! output=$(turbo compact --concurrency=1 2>&1); then - echo "โ Compilation failed." - if echo "$output" | grep -q "Hash mismatch" && [ -d "$HOME/.cache/midnight/zk-params" ]; then - echo "โ ๏ธ Hash mismatch detected *and* zk-params exists. Removing cache..." - rm -rf "$HOME/.cache/midnight/zk-params" - echo "::notice::โป๏ธ Retrying compilation after clearing zk-params..." - turbo compact --concurrency=1 || { echo "::error::โ Retry also failed."; exit 1; } - else - echo "๐ซ Compilation failed for another reason or zk-params missing. No retry." - exit 1 - fi - fi - } - - compile + - name: Compile contracts (with retry) + run: turbo compact --filter=@openzeppelin-compact/contracts --concurrency=1 - name: Run type checks run: turbo types diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 00000000..6ad36f80 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,42 @@ +# Releasing + +(1) Checkout the branch to be released. +This will usually be `main` except in the event of a hotfix. +For hotfixes, checkout the release branch you want to fix. + +(2) Create a new release branch. + +```sh +git checkout -b release-v0.2.0 +``` + +(3) Push and open a PR targeting `main` to carefully review the release changes. +This will trigger a GitHub workflow that automatically bumps the version number throughout the project. + +```sh +git push release-v0.2.0 +``` + +(4) Once merged, pull the changes from the release branch. +Then, create a tag on the release branch and push it to the main repository. +Note that the version changes must be pulled *before* the tag is created; +otherwise, the version validation check will fail in the release workflow. + +```sh +git pull +git tag v0.2.0 +git push origin v0.2.0 +``` + +(5) After that, go to the repo's [releases page](https://github.com/OpenZeppelin/compact-contracts/releases/). +[Create a new release](https://github.com/OpenZeppelin/compact-contracts/releases/new) with the new tag and the base branch as target (`main` except in the event of a hotfix). +Make sure to write a detailed release description and a short changelog. +Once published, this will trigger a workflow to upload the release tarball to npm. + +(6) Finally, from the released tag, +create and push a doc branch to deploy the corresponding version to the doc-site. + +```sh +git checkout -b docs-v0.2.0 +git push docs-v0.2.0 +``` diff --git a/compact/src/Builder.ts b/compact/src/Builder.ts index b8f05197..3c4d2c78 100755 --- a/compact/src/Builder.ts +++ b/compact/src/Builder.ts @@ -13,8 +13,9 @@ const execAsync = promisify(exec); /** * A class to handle the build process for a project. * Runs CompactCompiler as a prerequisite, then executes build steps (TypeScript compilation, - * artifact copying, etc.) - * with progress feedback and colored output for success and error states. + * artifact copying, etc.) with progress feedback and colored output for success and error states. + * + * Creates a clean distribution structure without src/ paths for professional import experience. * * @notice `cmd` scripts discard `stderr` output and fail silently because this is * handled in `executeStep`. @@ -32,45 +33,51 @@ const execAsync = promisify(exec); * Compactc version: 0.24.0 * โ [COMPILE] [2/2] Compiled MockAccessControl.compact * Compactc version: 0.24.0 - * โ [BUILD] [1/3] Compiling TypeScript - * โ [BUILD] [2/3] Copying artifacts - * โ [BUILD] [3/3] Copying and cleaning .compact files - * ``` - * - * @example