diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index dbdd503..e7d2229 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -7,46 +7,23 @@ inputs: 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.26.0" >> $GITHUB_ENV - echo "LANGUAGE_VERSION=0.18.0" >> $GITHUB_ENV - - - name: Get yarn cache directory path + - name: Enable corepack shell: bash run: corepack enable - name: Cache turbo build setup - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: .turbo - key: ${{ runner.os }}-turbo-${{ hashFiles('.turbo/*') }}-${{ github.sha }} + key: ${{ runner.os }}-turbo-${{ github.sha }} 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: | - ~/.local/bin/compact - key: compact-compiler-${{ env.COMPILER_VERSION }}-${{ runner.os }} + ${{ runner.os }}-turbo- - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: node-version-file: ".nvmrc" cache: "yarn" @@ -62,77 +39,8 @@ runs: 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/.local/bin" - mkdir -p "$COMPACT_HOME" - - # Require COMPACT_INSTALLER_URL to be provided by the caller - if [ -z "${COMPACT_INSTALLER_URL:-}" ]; then - echo "::error::COMPACT_INSTALLER_URL is required but not set. Provide it via env or secrets." - exit 1 - fi - - echo "🔧 Installing Compact compiler from $COMPACT_INSTALLER_URL ..." - curl --proto '=https' --tlsv1.2 -LsSf "$COMPACT_INSTALLER_URL" | sh - - echo "🔧 Updating Compact compiler to $COMPILER_VERSION..." - "$COMPACT_HOME/compact" update "$COMPILER_VERSION" - - echo "✅ Compact compiler installed" - - - name: Setup Compact environment - if: inputs.skip-compact != 'true' - shell: bash - run: | - COMPACT_HOME="$HOME/.local/bin" - echo "📁 Setting Compact environment variables..." - echo "COMPACT_HOME=$COMPACT_HOME" >> "$GITHUB_ENV" - echo "$COMPACT_HOME" >> "$GITHUB_PATH" - - if [ -f "$COMPACT_HOME/compact" ]; 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/.local/bin" >> $GITHUB_OUTPUT - echo "version=$COMPILER_VERSION" >> $GITHUB_OUTPUT - - - name: Check compiler and language version - if: inputs.skip-compact != 'true' - shell: bash - run: | - set -euo pipefail - - echo "🔧 Updating Compact compiler to $COMPILER_VERSION..." - "$COMPACT_HOME/compact" update "$COMPILER_VERSION" - - echo "🔍 Checking Compact compiler version..." - COMPILER_OUTPUT=$("$COMPACT_HOME/compact" compile --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=$("$COMPACT_HOME/compact" compile --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" + - name: Setup Compact Compiler + if: ${{ inputs.skip-compact != 'true' }} + uses: midnightntwrk/setup-compact-action@4130145456ad3f45934788dd4a65647eb283e658 # loose commit/not released + with: + compact-version: "0.26.0" diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index b8f511c..3f624d5 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,8 +1,5 @@ name: Format and Lint -env: - TURBO_TELEMETRY_DISABLED: 1 - on: pull_request: push: @@ -14,12 +11,17 @@ jobs: name: Run Checks runs-on: ubuntu-24.04 - env: - COMPACT_INSTALLER_URL: ${{ vars.COMPACT_INSTALLER_URL }} + permissions: + contents: read steps: + - name: Harden Runner + uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 + with: + egress-policy: audit + - name: Check out code - uses: actions/checkout@v6 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 2 # Recommended by turbo team diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..00d8cc8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,88 @@ +name: Release Package + +on: + workflow_dispatch: + inputs: + package: + description: "Which package to release" + required: true + type: choice + options: + - compact-tools-cli + - compact-tools-simulator + version_bump: + description: "Version bump type" + required: true + type: choice + options: + - patch + - minor + - major + +jobs: + release: + name: Release ${{ inputs.package }} + runs-on: ubuntu-24.04 + environment: compact-npm-prod # Requires approval + + permissions: + contents: write # Required to push commits and tags + + steps: + - name: Check out code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + + - name: Set package directory + id: pkg + run: | + case "${{ inputs.package }}" in + "compact-tools-cli") + echo "dir=cli" >> $GITHUB_OUTPUT + ;; + "compact-tools-simulator") + echo "dir=simulator" >> $GITHUB_OUTPUT + ;; + esac + + - name: Setup Environment + uses: ./.github/actions/setup + + - name: Run tests for package + run: yarn test --filter=@openzeppelin/${{ inputs.package }} + + - name: Build package + run: yarn build --filter=@openzeppelin/${{ inputs.package }} + + - name: Bump version + id: version + run: | + cd packages/${{ steps.pkg.outputs.dir }} + yarn version ${{ inputs.version_bump }} + NEW_VERSION=$(node -p "require('./package.json').version") + echo "new=$NEW_VERSION" >> $GITHUB_OUTPUT + echo "### Release Summary" >> $GITHUB_STEP_SUMMARY + echo "- Package: ${{ inputs.package }}" >> $GITHUB_STEP_SUMMARY + echo "- New version: $NEW_VERSION" >> $GITHUB_STEP_SUMMARY + echo "- Bump type: ${{ inputs.version_bump }}" >> $GITHUB_STEP_SUMMARY + + - name: Verify package contents + run: | + cd packages/${{ steps.pkg.outputs.dir }} + yarn pack --dry-run + + - name: Commit and tag version bump + uses: stefanzweifel/git-auto-commit-action@28e16e81777b558cc906c8750092100bbb34c5e3 # v7.0.0 + with: + commit_message: "chore: release ${{ inputs.package }} v${{ steps.version.outputs.new }}" + file_pattern: "packages/${{ steps.pkg.outputs.dir }}/package.json" + tagging_message: "${{ inputs.package }}/v${{ steps.version.outputs.new }}" + + - name: Publish to npm + run: | + yarn config set npmAuthToken "$NPM_TOKEN" + cd packages/${{ steps.pkg.outputs.dir }} + yarn npm publish --access public --provenance + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..500eae5 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,17 @@ +# Releasing + +1. Go to "Release Package" in Actions. +2. Click on the "Run workflow" dropdown menu. +3. Choose the package to release and the version bump type. + Following [SemVer](https://semver.org/): + - **Patch** - Backward-compatible bug fixes. + - **Minor** - New functionality in a backward compatible way. + - **Major** - Breaking API changes. + +4. A maintainer must approve the release before it proceeds. +5. Once approved, the CI will automatically: + - Run tests. + - Bump the version. + - Create a git tag. + - Publish the package to npm. +6. Once published, go to "Releases" and create a GitHub release using the generated tag. \ No newline at end of file diff --git a/packages/cli/package.json b/packages/cli/package.json index 216816d..5cf08ab 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,5 @@ { "name": "@openzeppelin/compact-tools-cli", - "private": true, "description": "CLI for compiling and building Compact smart contracts", "version": "0.0.1", "keywords": [ diff --git a/packages/simulator/package.json b/packages/simulator/package.json index e57b06b..7b2684c 100644 --- a/packages/simulator/package.json +++ b/packages/simulator/package.json @@ -1,6 +1,5 @@ { "name": "@openzeppelin/compact-tools-simulator", - "private": true, "description": "Simulator for testing Compact smart contracts", "version": "0.0.1", "keywords": [ diff --git a/packages/simulator/test/setup.ts b/packages/simulator/test/setup.ts index 8687ae7..2b5f23e 100644 --- a/packages/simulator/test/setup.ts +++ b/packages/simulator/test/setup.ts @@ -3,7 +3,7 @@ * Runs once before all tests via Vitest's globalSetup. */ -import { exec } from 'node:child_process'; +import { exec, type SpawnSyncReturns } from 'node:child_process'; import { existsSync, mkdirSync, statSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -23,6 +23,27 @@ const CONTRACT_FILES = [ 'SampleZOwnable.compact', ]; +function isSpawnSyncRet( + err: unknown, +): err is SpawnSyncReturns { + if (typeof err !== 'object' || err === null) { + return false; + } + + const typedErr = err as Partial> & + Record; + + const okErr = typedErr.error instanceof Error; + const okStdout = + typeof typedErr.stdout === 'string' || Buffer.isBuffer(typedErr.stdout); + const okStderr = + typeof typedErr.stderr === 'string' || Buffer.isBuffer(typedErr.stderr); + const okStatus = + typeof typedErr.status === 'number' || typedErr.status === null; + + return okErr && okStdout && okStderr && okStatus; +} + async function compileContract(contractFile: string): Promise { const inputPath = join(SAMPLE_CONTRACTS_DIR, contractFile); const contractName = contractFile.replace('.compact', ''); @@ -48,7 +69,22 @@ async function compileContract(contractFile: string): Promise { mkdirSync(join(outputDir, 'keys'), { recursive: true }); const command = `compact compile --skip-zk "${inputPath}" "${outputDir}"`; - await execAsync(command); + try { + await execAsync(command); + } catch (err: unknown) { + if (!isSpawnSyncRet(err)) { + throw err; + } + + if (err.status === 127) { + throw new Error( + '`compact` not found (exit code 127). Is it installed and on PATH?', + ); + } + + throw err; + } + console.log(`✓ Compiled ${contractFile}`); }