Skip to content

WASM 3. Yoga Layout #249

WASM 3. Yoga Layout

WASM 3. Yoga Layout #249

Workflow file for this run

name: WASM 3. Yoga Layout
on:
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run (build only, no release)'
type: boolean
default: true
force:
description: 'Force rebuild (ignore cache)'
type: boolean
default: false
build_mode:
description: 'Build mode'
type: choice
options:
- prod
- dev
default: prod
debug:
description: 'Debug (0|1|namespace[,...])'
type: string
default: '0'
workflow_call:
inputs:
dry_run:
type: boolean
default: true
force:
type: boolean
default: false
build_mode:
type: string
default: prod
debug:
type: string
default: '0'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build:
name: Build Yoga Layout WASM
permissions:
contents: read
id-token: write # OIDC authentication for Depot builds
runs-on: ubuntu-24.04
timeout-minutes: 90
env:
# Yoga Layout builds WASM on Linux only; derive platform-arch from the
# Node.js platform naming that the source code uses for layout paths.
PLATFORM_ARCH: linux-x64
steps:
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@ea1986b8019fedee5fb38b485690b13ad8e0217f # main
- name: Enable debug logging
uses: ./.github/actions/enable-debug-logging
with:
debug: ${{ inputs.debug }}
- name: Load tool versions
id: tool-versions
run: |
NODE_VERSION=$(cat .node-version | tr -d '\n')
YOGA_VERSION=$(jq -r '.sources.yoga.version' packages/yoga-layout-builder/package.json)
if [ -z "$YOGA_VERSION" ] || [ "$YOGA_VERSION" = "null" ]; then
echo "error: packages/yoga-layout-builder/package.json missing .sources.yoga.version" >&2
exit 1
fi
echo "node-version=$NODE_VERSION" >> $GITHUB_OUTPUT
echo "yoga-version=$YOGA_VERSION" >> $GITHUB_OUTPUT
echo "Loaded Node.js: $NODE_VERSION, Yoga Layout: $YOGA_VERSION"
- name: Set build mode
id: build-mode
uses: ./.github/actions/set-build-mode
with:
build-mode: ${{ inputs.build_mode }}
- name: Load cache version from centralized config
id: cache-version
uses: ./.github/actions/load-cache-version
with:
package-name: yoga-layout
- name: Generate Yoga cache key
id: cache-key
env:
CACHE_VERSION: ${{ steps.cache-version.outputs.version }}
run: |
# Cross-platform hash function.
if command -v shasum &> /dev/null; then
hash_cmd="shasum -a 256"
elif command -v sha256sum &> /dev/null; then
hash_cmd="sha256sum"
else
echo "Error: No SHA-256 command found"
exit 1
fi
# Per-checkpoint cumulative hashing (like node-smol)
# Each checkpoint includes its dependencies (cumulative)
# Helper to hash directory contents.
# Hashes ALL files, not just *.mts — external-tools.json and other
# non-script configs affect the build too.
hash_dir() {
local dir=$1
if [ -d "$dir" ]; then
# LC_COLLATE=C: keep ordering locale-independent so cache
# keys don't diverge across runner locales.
find "$dir" -type f 2>/dev/null | LC_COLLATE=C sort | xargs $hash_cmd 2>/dev/null | $hash_cmd | cut -d' ' -f1 || echo ""
else
echo ""
fi
}
# Common scripts (used by all checkpoints)
COMMON=$(hash_dir packages/yoga-layout-builder/scripts/common)
PACKAGE_JSON=$($hash_cmd packages/yoga-layout-builder/package.json | cut -d' ' -f1)
BUILD_MJS=$($hash_cmd packages/yoga-layout-builder/scripts/build.mts | cut -d' ' -f1)
# Shared TS helpers used by all phases + pnpm-lock + .node-version.
BUILD_INFRA_LIB=$(hash_dir packages/build-infra/lib)
BIN_INFRA_LIB=$(hash_dir packages/bin-infra/lib)
PNPM_LOCK_HASH=$($hash_cmd pnpm-lock.yaml 2>/dev/null | cut -d' ' -f1)
NODE_VERSION_HASH=$($hash_cmd .node-version 2>/dev/null | cut -d' ' -f1)
# external-tools.json pins cmake / emscripten / python3 versions
# consumed by pinned-versions.mts — bumps there produce different
# WASM output. package-root metadata (package.json) pins sources
# and devDependencies. Top-level scripts/paths.mts is re-exported
# by every per-phase shared/paths.mts, and scripts/lib/
# (optimization-flags, etc.) is imported cross-phase. None were
# previously hashed.
EXTERNAL_TOOLS_ROOT=$($hash_cmd packages/yoga-layout-builder/external-tools.json 2>/dev/null | cut -d' ' -f1)
SCRIPTS_TOP=$($hash_cmd packages/yoga-layout-builder/scripts/paths.mts 2>/dev/null | cut -d' ' -f1)
# get-checkpoint-chain.mts drives the cache-key plan later in
# this workflow (used at "Set checkpoint chain for build mode").
# Edit the chain-producing script without this hash and the
# cache key stays the same while the chain output differs —
# a stale cache survives what should be an invalidation.
GET_CHECKPOINT_CHAIN=$($hash_cmd packages/yoga-layout-builder/scripts/get-checkpoint-chain.mts 2>/dev/null | cut -d' ' -f1)
SCRIPTS_LIB_DIR=$(if [ -d packages/yoga-layout-builder/scripts/lib ]; then hash_dir packages/yoga-layout-builder/scripts/lib; else echo ""; fi)
# build-infra/external-tools.json pins the foundational
# toolchain (cmake/emscripten/python via install-tools.mts);
# build-infra/scripts/ holds get-checkpoint-chain +
# smoke-test-binary used across all builders. Both unhashed
# before R20 — bumps there silently hit stale cache.
BUILD_INFRA_EXTERNAL_TOOLS=$($hash_cmd packages/build-infra/external-tools.json 2>/dev/null | cut -d' ' -f1)
BUILD_INFRA_SCRIPTS=$(hash_dir packages/build-infra/scripts)
# Root package.json + pnpm-workspace.yaml hold catalog versions,
# overrides, trustPolicy, minimumReleaseAge. Unhashed before R24.
ROOT_PACKAGE_JSON_HASH=$($hash_cmd package.json 2>/dev/null | cut -d' ' -f1)
PNPM_WORKSPACE_HASH=$($hash_cmd pnpm-workspace.yaml 2>/dev/null | cut -d' ' -f1)
# Socket-registry propagation SHA — extracted from THIS
# workflow's own YAML at runtime, not via a catalog lookup.
# A registry cascade bump (which edits the @<sha> pin in
# this file) flows through to a cache-key change here, so
# the checkpoint cache invalidates whenever the registry
# provisions a new pnpm version / zizmor build / etc. That
# makes .build-context/registry-tools.json — whose contents
# are materialized per-run from SOCKET_TOOL_CHECKSUMS_FILE
# — implicitly part of the cache key.
WORKFLOW_FILE="${GITHUB_WORKFLOW_REF%@*}"
WORKFLOW_FILE="${WORKFLOW_FILE#${GITHUB_REPOSITORY}/}"
SOCKET_REGISTRY_SHA=$(grep -ohE 'SocketDev/socket-registry/[^@]+@[0-9a-f]{40}' "$WORKFLOW_FILE" 2>/dev/null | head -1 | grep -oE '[0-9a-f]{40}$' || echo "")
SHARED_DEPS="${BUILD_INFRA_LIB}${BIN_INFRA_LIB}${PNPM_LOCK_HASH}${NODE_VERSION_HASH}${EXTERNAL_TOOLS_ROOT}${SCRIPTS_TOP}${GET_CHECKPOINT_CHAIN}${SCRIPTS_LIB_DIR}${BUILD_INFRA_EXTERNAL_TOOLS}${BUILD_INFRA_SCRIPTS}${ROOT_PACKAGE_JSON_HASH}${PNPM_WORKSPACE_HASH}${SOCKET_REGISTRY_SHA}"
# source-cloned: cache-version + common + source-cloned + build.mjs + package.json + shared deps
SOURCE_CLONED_DIR=$(hash_dir packages/yoga-layout-builder/scripts/source-cloned)
SOURCE_CLONED_HASH=$(echo "${CACHE_VERSION}${COMMON}${SOURCE_CLONED_DIR}${BUILD_MJS}${PACKAGE_JSON}${SHARED_DEPS}" | $hash_cmd | cut -d' ' -f1)
# source-configured: source-cloned + source-configured
SOURCE_CONFIGURED_DIR=$(hash_dir packages/yoga-layout-builder/scripts/source-configured)
SOURCE_CONFIGURED_HASH=$(echo "${SOURCE_CLONED_HASH}${SOURCE_CONFIGURED_DIR}" | $hash_cmd | cut -d' ' -f1)
# wasm-compiled: source-configured + wasm-compiled
WASM_COMPILED_DIR=$(hash_dir packages/yoga-layout-builder/scripts/wasm-compiled)
WASM_COMPILED_HASH=$(echo "${SOURCE_CONFIGURED_HASH}${WASM_COMPILED_DIR}" | $hash_cmd | cut -d' ' -f1)
# wasm-released: wasm-compiled + wasm-released
WASM_RELEASE_DIR=$(hash_dir packages/yoga-layout-builder/scripts/wasm-released)
WASM_RELEASE_HASH=$(echo "${WASM_COMPILED_HASH}${WASM_RELEASE_DIR}" | $hash_cmd | cut -d' ' -f1)
# wasm-optimized: wasm-released + wasm-optimized
WASM_OPTIMIZED_DIR=$(hash_dir packages/yoga-layout-builder/scripts/wasm-optimized)
WASM_OPTIMIZED_HASH=$(echo "${WASM_RELEASE_HASH}${WASM_OPTIMIZED_DIR}" | $hash_cmd | cut -d' ' -f1)
# wasm-synced: wasm-optimized + wasm-synced + shared wasm-sync
# helpers + src/wrapper/. generate-sync.mts reads src/wrapper/
# YGEnums.mts + wrapAssembly.mts and delegates to shared helpers
# in build-infra/wasm-synced/ (wasm-sync-wrapper, transform,
# generate-sync-cjs/esm). Hash the whole shared directory so
# helper edits invalidate this checkpoint too.
WASM_SYNC_DIR=$(hash_dir packages/yoga-layout-builder/scripts/wasm-synced)
WASM_SYNC_SHARED=$(hash_dir packages/build-infra/wasm-synced)
SRC_WRAPPER=$(hash_dir packages/yoga-layout-builder/src/wrapper)
WASM_SYNC_HASH=$(echo "${WASM_OPTIMIZED_HASH}${WASM_SYNC_DIR}${WASM_SYNC_SHARED}${SRC_WRAPPER}" | $hash_cmd | cut -d' ' -f1)
# finalized: wasm-synced + finalized (most complete hash)
# Also includes Dockerfile (build recipe) and yoga upstream submodule SHA.
FINAL_DIR=$(hash_dir packages/yoga-layout-builder/scripts/finalized)
DOCKERFILE_HASH=$(if [ -f packages/yoga-layout-builder/docker/Dockerfile.linux ]; then $hash_cmd packages/yoga-layout-builder/docker/Dockerfile.linux | cut -d' ' -f1; else echo ""; fi)
YOGA_SHA=$(git ls-tree HEAD packages/yoga-layout-builder/upstream/yoga 2>/dev/null | awk '{print $3}' || echo "none")
FINAL_HASH=$(echo "${WASM_SYNC_HASH}${FINAL_DIR}${DOCKERFILE_HASH}${YOGA_SHA}" | $hash_cmd | cut -d' ' -f1)
echo "cache_version=${CACHE_VERSION}" >> $GITHUB_OUTPUT
echo "source_cloned_hash=${SOURCE_CLONED_HASH}" >> $GITHUB_OUTPUT
echo "final_hash=${FINAL_HASH}" >> $GITHUB_OUTPUT
- name: Restore Yoga checkpoint cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
id: yoga-checkpoint-cache
if: ${{ !inputs.force }}
with:
path: |
packages/yoga-layout-builder/build/shared/checkpoints
packages/yoga-layout-builder/build/${{ steps.build-mode.outputs.mode }}/${{ env.PLATFORM_ARCH }}/checkpoints
key: yoga-checkpoints-${{ steps.cache-key.outputs.cache_version }}-v${{ steps.tool-versions.outputs.yoga-version }}-${{ runner.os }}-${{ steps.build-mode.outputs.mode }}-${{ steps.cache-key.outputs.final_hash }}
# NOTE: No restore-keys - we require exact match to prevent stale cache restoration.
# Partial matches with restore-keys cause builds to use outdated artifacts when
# source changes bump the final_hash but the prefix still matches.
- name: Set checkpoint chain for build mode
id: checkpoint-chain
env:
BUILD_MODE: ${{ steps.build-mode.outputs.mode }}
run: |
# Use get-checkpoint-chain.mts to ensure consistency with local builds
# Dev mode: skip wasm-optimized (optimization is disabled)
# Prod mode: include wasm-optimized
CHAIN=$(node packages/yoga-layout-builder/scripts/get-checkpoint-chain.mts --$BUILD_MODE)
echo "checkpoint_chain=$CHAIN" >> $GITHUB_OUTPUT
# Convert comma-separated to space-separated for validation
CHECKPOINTS=$(echo "$CHAIN" | tr ',' ' ')
echo "checkpoints=$CHECKPOINTS" >> $GITHUB_OUTPUT
echo "Checkpoint chain for $BUILD_MODE mode: $CHAIN"
- name: Validate checkpoint cache integrity
id: validate-cache
if: steps.yoga-checkpoint-cache.outputs.cache-hit == 'true'
uses: ./.github/actions/validate-checkpoints
with:
checkpoint-dirs: packages/yoga-layout-builder/build/shared/checkpoints:packages/yoga-layout-builder/build/${{ steps.build-mode.outputs.mode }}/${{ env.PLATFORM_ARCH }}/checkpoints
checkpoints: ${{ steps.checkpoint-chain.outputs.checkpoints }}
package-name: yoga-layout-builder
- name: Restore build output from checkpoint chain
id: restore-checkpoint
uses: ./.github/actions/restore-checkpoint
with:
package-name: 'yoga-layout-builder'
build-mode: ${{ steps.build-mode.outputs.mode }}
checkpoint-chain: ${{ steps.checkpoint-chain.outputs.checkpoint_chain }}
cache-hit: ${{ steps.yoga-checkpoint-cache.outputs.cache-hit }}
cache-valid: ${{ steps.validate-cache.outputs.valid }}
platform-arch: ${{ env.PLATFORM_ARCH }}
- name: Setup Depot CLI
if: (steps.yoga-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') || steps.restore-checkpoint.outputs.build-required == 'true'
uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1
with:
oidc: true
- name: Initialize Yoga submodule
# Cache-miss only: the cache-key uses `git ls-tree HEAD` so the
# submodule does not need to be initialized to compute it.
if: (steps.yoga-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') || steps.restore-checkpoint.outputs.build-required == 'true'
uses: ./.github/actions/init-submodules
with:
submodules: packages/yoga-layout-builder/upstream/yoga
- name: Stage registry tool-checksums for Docker build context
if: |
(steps.yoga-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') ||
steps.restore-checkpoint.outputs.build-required == 'true'
shell: bash
run: |
# Socket-registry's setup action exports SOCKET_TOOL_CHECKSUMS_FILE
# pointing at a stable on-runner copy of external-tools.json.
# Dockerfiles `COPY .build-context/registry-tools.json …` to
# resolve pnpm version + per-platform asset + sha256 at build
# time without duplicating per-Dockerfile constants.
mkdir -p .build-context
cp "$SOCKET_TOOL_CHECKSUMS_FILE" .build-context/registry-tools.json
- name: Build Yoga Layout WASM with Depot (offloads compute)
id: build
if: |
(steps.yoga-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') ||
steps.restore-checkpoint.outputs.build-required == 'true'
uses: depot/build-push-action@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1.17.0
with:
project: 8fpj9495vw
platforms: linux/amd64
build-args: |
BUILD_MODE=${{ steps.build-mode.outputs.mode }}
PLATFORM_ARCH=${{ env.PLATFORM_ARCH }}
CACHE_VERSION=${{ steps.cache-version.outputs.version }}
no-cache: ${{ inputs.force == true }}
file: packages/yoga-layout-builder/docker/Dockerfile.linux
target: export
outputs: type=local,dest=packages/yoga-layout-builder/build
context: .
- name: Verify Depot build output
if: |
(steps.yoga-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') ||
steps.restore-checkpoint.outputs.build-required == 'true'
env:
BUILD_MODE: ${{ steps.build-mode.outputs.mode }}
run: |
echo "Verifying Depot build output structure..."
ls -lah packages/yoga-layout-builder/build/
# Check if build artifacts exist.
if [ ! -d "packages/yoga-layout-builder/build/$BUILD_MODE" ]; then
echo "× Build directory not found!"
echo "Contents of output directory:"
find packages/yoga-layout-builder/build/ -type f
exit 1
fi
echo "✅ Build artifacts found"
ls -lh packages/yoga-layout-builder/build/$BUILD_MODE/
- name: Validate Depot checkpoints
if: |
(steps.yoga-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') ||
steps.restore-checkpoint.outputs.build-required == 'true'
uses: ./.github/actions/validate-depot-checkpoints
with:
package-path: packages/yoga-layout-builder
build-mode: ${{ steps.build-mode.outputs.mode }}
package-name: yoga-layout-builder
platform-arch: ${{ env.PLATFORM_ARCH }}
# Debug output only needed on cache-miss / invalid / build-required
# runs. Full cache-hit rounds have a known-good layout from the
# restored checkpoint tarball — skip the full tree walk to save I/O.
- name: Debug build output structure
if: (steps.yoga-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') || steps.restore-checkpoint.outputs.build-required == 'true'
env:
BUILD_MODE: ${{ steps.build-mode.outputs.mode }}
run: |
echo "=== Debugging build output structure ==="
echo "Looking for files in: packages/yoga-layout-builder/build/${BUILD_MODE}/"
if [ -d "packages/yoga-layout-builder/build/${BUILD_MODE}" ]; then
echo "Build directory exists, showing full structure:"
find packages/yoga-layout-builder/build/${BUILD_MODE} -type f -o -type d | sort
else
echo "× Build directory not found!"
echo "Available directories in packages/yoga-layout-builder/build/:"
ls -la packages/yoga-layout-builder/build/ 2>/dev/null || echo "build/ directory doesn't exist"
fi
echo "=== End debug output ==="
# Skip on full cache-hit — files came from the restored checkpoint
# tarball and were already integrity-checked by validate-cache.
- name: Validate build output
if: (steps.yoga-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.valid == 'false') || steps.restore-checkpoint.outputs.build-required == 'true'
env:
BUILD_MODE: ${{ steps.build-mode.outputs.mode }}
run: |
echo "Validating Yoga Layout build output..."
if [ ! -f "packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out/Final/yoga.wasm" ]; then
echo "× Build failed: WASM file missing"
echo "Expected path: packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out/Final/yoga.wasm"
exit 1
fi
if [ ! -f "packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out/Final/yoga.mjs" ]; then
echo "× Build failed: ES module file missing"
exit 1
fi
if [ ! -f "packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out/Final/yoga-sync.cjs" ]; then
echo "× Build failed: Sync CJS file missing"
exit 1
fi
if [ ! -f "packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out/Final/yoga-sync.mjs" ]; then
echo "× Build failed: Sync ESM file missing"
exit 1
fi
WASM_SIZE=$(stat -c%s "packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out/Final/yoga.wasm" 2>/dev/null || stat -f%z "packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out/Final/yoga.wasm")
if [ "$WASM_SIZE" -lt 100000 ]; then
echo "× Build failed: WASM file too small ($WASM_SIZE bytes)"
exit 1
fi
echo "✅ Build validation passed"
ls -lh "packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out/Final/"
- name: Upload Yoga Layout artifacts
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: yoga-layout-wasm
path: packages/yoga-layout-builder/build/${{ steps.build-mode.outputs.mode }}/${{ env.PLATFORM_ARCH }}/wasm/out/Final/
retention-days: 30
compression-level: 0 # Already-compressed binary artifact
if-no-files-found: error
- name: Cleanup before cache save
if: always()
env:
BUILD_MODE: ${{ steps.build-mode.outputs.mode }}
run: |
echo "Cleaning up temporary files before cache save..."
# Remove temporary build files that shouldn't be cached
TEMP_DIRS=(
"packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/temp"
"packages/yoga-layout-builder/build/${BUILD_MODE}/${PLATFORM_ARCH}/wasm/out"
)
for DIR in "${TEMP_DIRS[@]}"; do
if [ -d "$DIR" ]; then
echo "Removing: $DIR"
rm -rf "$DIR"
fi
done
echo "✅ Cleanup complete"
release:
name: Release Yoga Layout
needs: build
if: github.event_name == 'workflow_dispatch' && !inputs.dry_run
runs-on: ubuntu-24.04
timeout-minutes: 10
environment: release # Dedicated environment for secret access with approval gates
permissions:
contents: write # Required to create GitHub releases
env:
# Release job downloads the Linux x64 build artifact into this layout
# to match the build job's output path.
PLATFORM_ARCH: linux-x64
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
sparse-checkout: |
packages/yoga-layout-builder/package.json
.github/scripts/generate-version.sh
sparse-checkout-cone-mode: false
- name: Load Yoga version from package.json
id: tool-versions
run: |
YOGA_VERSION=$(jq -r '.sources.yoga.version' packages/yoga-layout-builder/package.json)
if [ -z "$YOGA_VERSION" ] || [ "$YOGA_VERSION" = "null" ]; then
echo "error: packages/yoga-layout-builder/package.json missing .sources.yoga.version" >&2
exit 1
fi
echo "yoga-version=$YOGA_VERSION" >> $GITHUB_OUTPUT
echo "Loaded Yoga Layout: $YOGA_VERSION"
- name: Download Yoga Layout artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: yoga-layout-wasm
path: packages/yoga-layout-builder/build/prod/${{ env.PLATFORM_ARCH }}/wasm/out/Final/
- name: Generate version
id: version
env:
GH_TOKEN: ${{ github.token }}
run: |
source .github/scripts/generate-version.sh
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
TAG="yoga-layout-${VERSION}"
if gh release view "$TAG" &>/dev/null; then
echo "Release $TAG already exists, skipping."
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Rename assets with version
if: steps.version.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
cd packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final
mv yoga.wasm yoga-${VERSION}.wasm
mv yoga.mjs yoga-${VERSION}.mjs
mv yoga-sync.cjs yoga-sync-${VERSION}.cjs
mv yoga-sync.mjs yoga-sync-${VERSION}.mjs
- name: Generate checksums
if: steps.version.outputs.skip != 'true'
run: |
cd packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final
$(command -v shasum &> /dev/null && echo "shasum -a 256" || echo "sha256sum") yoga-*.wasm yoga-*.mjs yoga-sync-*.cjs yoga-sync-*.mjs > checksums.txt
cat checksums.txt
- name: Import GPG key
if: steps.version.outputs.skip != 'true'
env:
GPG_PRIVATE_KEY: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
if [ -n "$GPG_PRIVATE_KEY" ]; then
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
echo "GPG key imported successfully"
else
echo "GPG_PRIVATE_KEY secret not set, skipping signature"
fi
- name: Sign checksums
if: steps.version.outputs.skip != 'true'
env:
GPG_PRIVATE_KEY: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
if [ -n "$GPG_PRIVATE_KEY" ]; then
cd packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final
if [ -n "$GPG_PASSPHRASE" ]; then
echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 --detach-sign --armor checksums.txt
else
gpg --batch --yes --detach-sign --armor checksums.txt
fi
echo "[OK] Created checksums.txt.asc"
ls -lh checksums.txt.asc
fi
- name: Create GitHub Release
if: steps.version.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }}
YOGA_VERSION: ${{ steps.tool-versions.outputs.yoga-version }}
run: |
VERSION="${STEPS_VERSION_OUTPUTS_VERSION}"
RELEASE_NAME="yoga-layout"
TAG="${RELEASE_NAME}-${VERSION}"
echo "Creating new release $TAG..."
# Extract date-hash from tag for title
TITLE_SUFFIX="${TAG#yoga-layout-}"
gh release create "$TAG" \
--title "yoga-layout ${TITLE_SUFFIX}" \
--notes "Yoga Layout v${YOGA_VERSION} compiled to WebAssembly.
## Files
- \`yoga-${VERSION}.wasm\` - WASM binary
- \`yoga-${VERSION}.mjs\` - ES module loader
- \`yoga-sync-${VERSION}.cjs\` - Synchronous CommonJS wrapper
- \`yoga-sync-${VERSION}.mjs\` - Synchronous ES module wrapper
- \`checksums.txt\` - SHA256 checksums
## Usage
Import the module in your project:
\`\`\`javascript
import yoga from './yoga-${VERSION}.mjs';
\`\`\`" \
packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final/yoga-${VERSION}.wasm \
packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final/yoga-${VERSION}.mjs \
packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final/yoga-sync-${VERSION}.cjs \
packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final/yoga-sync-${VERSION}.mjs \
packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final/checksums.txt \
$([ -f packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final/checksums.txt.asc ] && echo "packages/yoga-layout-builder/build/prod/${PLATFORM_ARCH}/wasm/out/Final/checksums.txt.asc" || echo "")
update-checksums:
needs: release
if: github.event_name == 'workflow_dispatch' && !inputs.dry_run
permissions:
contents: write # release-assets.json commit after publishing GitHub release
uses: ./.github/workflows/update-checksums.yml
secrets:
BOT_GPG_PRIVATE_KEY: ${{ secrets.BOT_GPG_PRIVATE_KEY }}