Skip to content

ci(fresh-install): widen prod-dep update to examples and playground #41

ci(fresh-install): widen prod-dep update to examples and playground

ci(fresh-install): widen prod-dep update to examples and playground #41

name: Fresh Install Tests
# Periodically tests BlockNote with the latest versions of its production
# dependencies (within declared semver ranges). This catches breakage when a
# new release of a dep like @tiptap/* or prosemirror-* ships and conflicts
# with BlockNote's declared ranges — the kind of failure a user would hit when
# running `npm install @blocknote/react` in a fresh project.
#
# Only production dependencies of published (non-private) packages are updated.
# DevDependencies (vitest, vite, typescript, etc.) stay pinned to the lockfile,
# so test tooling churn doesn't cause false positives.
on:
push:
branches:
- package-upgrades
- feat/fresh-install-debug
schedule:
- cron: "0 2 * * *" # Daily at 02:00 UTC
workflow_dispatch: # Allow manual runs
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
pnpm_config_store_dir: ./node_modules/.pnpm-store
jobs:
fresh-install-unit-tests:
name: Unit Tests (Fresh Dep Resolution)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- id: checkout
uses: actions/checkout@v6
- id: install_pnpm
name: Install pnpm
uses: pnpm/action-setup@v5
- id: setup_node
uses: actions/setup-node@v6
with:
node-version-file: ".nvmrc"
# Intentionally no pnpm cache — we want fresh prod dep resolution
- id: install_dependencies
name: Install dependencies
run: pnpm install
- id: update_prod_deps
name: Update prod deps of packages, examples, and playground
# Resolves production dependencies to the latest versions within declared
# semver ranges for:
# - packages/* – the published @blocknote/* packages
# - examples/*/* – the individual example apps
# - playground – the example-editor app that bundles the examples
# Examples and playground are included so shared peer-dep'd libs
# (e.g. @tiptap/core + @tiptap/pm) resolve to the same version across
# publishable packages and the apps consuming them; otherwise pnpm ends
# up with two virtual-store copies keyed on different peer versions and
# TypeScript treats their exports as unrelated types.
# docs/, shared/, tests/, fumadocs/ are deliberately excluded — they're
# not part of the "fresh npm install @blocknote/react" user experience
# this workflow simulates.
# DevDependencies are left at their lockfile versions.
run: >-
pnpm update --prod
--filter "./packages/*"
--filter "./examples/*/*"
--filter "./playground"
- id: build_packages
name: Build packages
run: pnpm run build
env:
NX_SKIP_NX_CACHE: "true"
- id: run_unit_tests
name: Run unit tests
run: pnpm run test
env:
NX_SKIP_NX_CACHE: "true"
- name: Notify Slack on workflow failure
if: ${{ failure() }}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
REPOSITORY: ${{ github.repository }}
WORKFLOW: ${{ github.workflow }}
RUN_ID: ${{ github.run_id }}
RUN_NUMBER: ${{ github.run_number }}
RUN_ATTEMPT: ${{ github.run_attempt }}
BRANCH: ${{ github.ref_name }}
run: |
if [ -z "$SLACK_WEBHOOK_URL" ]; then
echo "SLACK_WEBHOOK_URL is not configured; skipping Slack notification."
exit 0
fi
failed_step="Unknown step"
if [ "${{ steps.checkout.outcome }}" = "failure" ]; then
failed_step="Checkout repository"
elif [ "${{ steps.install_pnpm.outcome }}" = "failure" ]; then
failed_step="Install pnpm"
elif [ "${{ steps.setup_node.outcome }}" = "failure" ]; then
failed_step="Setup Node.js"
elif [ "${{ steps.install_dependencies.outcome }}" = "failure" ]; then
failed_step="Install dependencies"
elif [ "${{ steps.update_prod_deps.outcome }}" = "failure" ]; then
failed_step="Update prod deps of published packages"
elif [ "${{ steps.build_packages.outcome }}" = "failure" ]; then
failed_step="Build packages"
elif [ "${{ steps.run_unit_tests.outcome }}" = "failure" ]; then
failed_step="Run unit tests"
fi
run_url="https://github.com/${REPOSITORY}/actions/runs/${RUN_ID}"
message=$(printf '%s\n%s\n%s\n%s' \
":warning: Fresh Install Tests failed in *${REPOSITORY}* on branch *${BRANCH}*." \
"*Workflow:* ${WORKFLOW}" \
"*Run:* <${run_url}|#${RUN_NUMBER} (attempt ${RUN_ATTEMPT})>" \
"*Failed step:* ${failed_step}")
payload=$(jq --compact-output --null-input --arg text "$message" '{text: $text}')
curl -sS -X POST \
--fail \
--retry 4 \
--retry-all-errors \
--retry-max-time 60 \
--connect-timeout 10 \
--max-time 30 \
-H "Content-type: application/json" \
--data "$payload" \
"$SLACK_WEBHOOK_URL"