Skip to content

fix(core): Patch TypeORM relation id alias mismatch breaking relation custom field hydration #7190

fix(core): Patch TypeORM relation id alias mismatch breaking relation custom field hydration

fix(core): Patch TypeORM relation id alias mismatch breaking relation custom field hydration #7190

name: Publish & Install
on:
workflow_dispatch:
push:
branches:
- master
- minor
- major
paths:
- 'packages/**'
- 'package.json'
- 'bun.lock'
- 'bunfig.toml'
pull_request:
branches:
- master
- major
- minor
paths:
- 'packages/**'
- 'package.json'
- 'bun.lock'
- 'bunfig.toml'
defaults:
run:
shell: bash
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
# Job 1: Build all packages and publish to Verdaccio (runs once)
build_and_publish:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Use Node.js 22.x
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22.x
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: '1.3.10'
- name: Install Verdaccio
run: |
npm install -g verdaccio
npm install -g wait-on
mkdir -p $HOME/.config/verdaccio
cp -v ./.github/workflows/verdaccio/config.yaml $HOME/.config/verdaccio/config.yaml
nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
wait-on http://localhost:4873
TOKEN_RES=$(curl -XPUT \
-H "Content-type: application/json" \
-d '{ "name": "test", "password": "test" }' \
'http://localhost:4873/-/user/org.couchdb.user:test')
TOKEN=$(echo "$TOKEN_RES" | jq -r '.token')
npm set //localhost:4873/:_authToken $TOKEN
- name: bun install
run: bun install --frozen-lockfile
env:
CI: true
- name: Publish to Verdaccio
run: |
nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
wait-on http://localhost:4873
bunx lerna publish prepatch --preid ci --no-push --no-git-tag-version --no-commit-hooks --force-publish "*" --yes --dist-tag ci --registry http://localhost:4873
- name: Package Verdaccio storage
run: |
cd $HOME/.config/verdaccio
tar -czf verdaccio-storage.tar.gz storage htpasswd
- name: Upload Verdaccio storage
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: verdaccio-storage
path: ~/.config/verdaccio/verdaccio-storage.tar.gz
retention-days: 1
# Job 2: Test installation on various OS/Node combinations
test:
needs: build_and_publish
runs-on: ${{ matrix.os }}
timeout-minutes: 45
permissions:
contents: read
strategy:
matrix:
# For PRs: run minimal matrix (1 job). For push: run full matrix (9 jobs).
# Only non-EOL Node versions are tested: native deps (e.g. better-sqlite3)
# stop publishing prebuilt binaries for EOL versions, and the node-gyp
# bundled with an EOL Node cannot drive newer Visual Studio releases.
os: ${{ github.event_name == 'pull_request' && fromJSON('["ubuntu-latest"]') || fromJSON('["ubuntu-latest", "windows-latest", "macos-latest"]') }}
node-version: ${{ github.event_name == 'pull_request' && fromJSON('["22.x"]') || fromJSON('["22.x", "24.x", "26.x"]') }}
fail-fast: false
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ matrix.node-version }}
- name: Download Verdaccio storage
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: verdaccio-storage
path: ~/verdaccio-download
- name: Setup Verdaccio with pre-built packages
run: |
npm install -g verdaccio
npm install -g wait-on
mkdir -p $HOME/.config/verdaccio
cp -v ./.github/workflows/verdaccio/config.yaml $HOME/.config/verdaccio/config.yaml
# Extract the pre-built storage
cd $HOME/.config/verdaccio
tar -xzf ~/verdaccio-download/verdaccio-storage.tar.gz
# Start Verdaccio
nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
wait-on http://localhost:4873
# Setup auth token
TOKEN_RES=$(curl -XPUT \
-H "Content-type: application/json" \
-d '{ "name": "test", "password": "test" }' \
'http://localhost:4873/-/user/org.couchdb.user:test')
TOKEN=$(echo "$TOKEN_RES" | jq -r '.token')
npm set //localhost:4873/:_authToken $TOKEN
- name: Windows dependencies
if: matrix.os == 'windows-latest'
run: npm install -g @angular/cli
- name: Install via @vendure/create
run: |
mkdir -p $HOME/install
cd $HOME/install
nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
wait-on http://localhost:4873
npm set registry=http://localhost:4873
npm dist-tag ls @vendure/create
npx @vendure/create@ci test-app --ci --use-npm --log-level info
- name: Server smoke tests
run: |
cd $HOME/install/test-app
npm run dev &
node $GITHUB_WORKSPACE/.github/workflows/scripts/smoke-tests
- name: Kill dev server after smoke tests
shell: bash
run: |
# Kill everything on ports 3000 and 5173 so the dashboard tests can start fresh.
# `npm run dev` spawns child processes (server on 3000, dashboard Vite on 5173)
# that aren't killed by killing the parent, so we kill by port. Freeing 5173 here
# is essential: otherwise the smoke-test dashboard lingers and the next step's
# dashboard fails to bind it.
if [[ "$RUNNER_OS" == "Windows" ]]; then
# Windows: use netstat to find PIDs and taskkill
netstat -ano | grep -E ':(3000|5173)[[:space:]]' | grep 'LISTENING' | awk '{print $5}' | sort -u | xargs -r -I {} taskkill //F //PID {} 2>/dev/null || true
else
# Linux/macOS: use lsof
lsof -ti:3000,5173 | xargs kill 2>/dev/null || true
fi
- name: Copy files (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
cd ~/install/test-app
New-Item -ItemType Directory -Force -Path src/plugins/test-plugin
Copy-Item "$env:GITHUB_WORKSPACE/.github/workflows/scripts/test-plugin/*" -Destination "src/plugins/test-plugin/" -Recurse -Force
Copy-Item "$env:GITHUB_WORKSPACE/.github/workflows/scripts/setup-test-plugin.js" -Destination "./setup-test-plugin.js"
- name: Copy files (Unix)
if: runner.os != 'Windows'
run: |
cd ~/install/test-app
mkdir -p src/plugins/test-plugin
cp -r "$GITHUB_WORKSPACE/.github/workflows/scripts/test-plugin/." src/plugins/test-plugin/
cp "$GITHUB_WORKSPACE/.github/workflows/scripts/setup-test-plugin.js" ./setup-test-plugin.js
- name: Run setup script
shell: bash
run: |
cd ~/install/test-app
node setup-test-plugin.js
- name: Cache Playwright browsers
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
restore-keys: |
playwright-${{ runner.os }}-
- name: Install Playwright
run: |
cd ~/install/test-app
npm install --no-save playwright
npx playwright install chromium
npx playwright install-deps chromium
- name: Start dashboard and run tests
run: |
cd ~/install/test-app
# `npm run dev` already starts BOTH the Vendure server (port 3000) and the
# dashboard Vite dev server (port 5173) concurrently. Starting a second Vite
# on 5173 here caused an intermittent EADDRINUSE race against the one that
# `npm run dev` had already bound, so we rely on the single dev process.
npm run dev &
DEV_PID=$!
# Wait for the server (use /health endpoint, not root) and the dashboard to be available
wait-on http://localhost:3000/health --timeout 60000
wait-on http://localhost:5173/dashboard --timeout 120000
# Run the dashboard tests
NODE_PATH=~/install/test-app/node_modules node $GITHUB_WORKSPACE/.github/workflows/scripts/dashboard-tests.js
# Clean up dev server process
kill $DEV_PID 2>/dev/null || true
- name: Upload dashboard test screenshots
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: dashboard-test-screenshots-${{ matrix.os }}-${{ matrix.node-version }}
path: /tmp/dashboard-test-*.png
retention-days: 28
# Job 3: Verify `@vendure/create` works end-to-end when invoked via bun, pnpm and yarn.
# The package manager is detected from npm_config_user_agent (which bunx / pnpm dlx /
# yarn dlx set), so the install step and generated project must use that manager rather
# than hard-coding npm (#4390). pnpm and yarn are deliberately NOT pinned: their current
# releases are what users get, and policy changes in new majors (e.g. pnpm 11 ignoring
# the package.json pnpm field, yarn 4.14 disabling build scripts) must fail here (#4932).
test_package_managers:
needs: build_and_publish
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
strategy:
matrix:
include:
# $CREATE_VERSION is resolved from the ci dist-tag at run time. The tag
# itself is not passed to dlx because Verdaccio serves pnpm's abbreviated
# metadata requests a stale/merged tag map, resolving @ci to an upstream
# nightly instead of the locally published version.
- pm: bun
scaffold: bunx @vendure/create@$CREATE_VERSION test-app --ci --log-level info
- pm: pnpm
scaffold: pnpm dlx @vendure/create@$CREATE_VERSION test-app --ci --log-level info
- pm: yarn
scaffold: yarn dlx @vendure/create@$CREATE_VERSION test-app --ci --log-level info
fail-fast: false
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Use Node.js 22.x
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22.x
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: '1.3.10'
- name: Enable pnpm and yarn via corepack
run: |
# The runner's bundled corepack maps yarn@stable to an outdated release;
# updating corepack first gives the current one that users actually get.
npm install -g corepack@latest
corepack enable
corepack prepare pnpm@latest --activate
corepack prepare yarn@stable --activate
pnpm --version
yarn --version
- name: Download Verdaccio storage
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: verdaccio-storage
path: ~/verdaccio-download
- name: Setup Verdaccio with pre-built packages
run: |
npm install -g verdaccio
npm install -g wait-on
mkdir -p $HOME/.config/verdaccio
cp -v ./.github/workflows/verdaccio/config.yaml $HOME/.config/verdaccio/config.yaml
cd $HOME/.config/verdaccio
tar -xzf ~/verdaccio-download/verdaccio-storage.tar.gz
nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
wait-on http://localhost:4873
# Register a Verdaccio user and capture an auth token.
TOKEN_RES=$(curl -XPUT \
-H "Content-type: application/json" \
-d '{ "name": "test", "password": "test" }' \
'http://localhost:4873/-/user/org.couchdb.user:test')
TOKEN=$(echo "$TOKEN_RES" | jq -r '.token')
# npm + pnpm read ~/.npmrc; bun is pointed at Verdaccio via the
# npm_config_registry env var on the install step (see below).
npm set registry=http://localhost:4873
npm set //localhost:4873/:_authToken $TOKEN
- name: Install via @vendure/create using ${{ matrix.pm }}
# bun ignores the home-directory ~/.npmrc (oven-sh/bun#22971) and does not walk
# up to a parent .npmrc/bunfig, but it DOES honour the npm_config_registry env
# var — and so does the scaffolded project's nested install (it inherits the
# env). This is the one mechanism that reaches both bunx and the nested install.
# Yarn Berry reads neither ~/.npmrc nor npm_config_registry: it needs its own
# YARN_NPM_REGISTRY_SERVER env var, plus the http whitelist since Verdaccio
# is plain http.
env:
CI: true
npm_config_registry: http://localhost:4873/
YARN_NPM_REGISTRY_SERVER: http://localhost:4873/
YARN_UNSAFE_HTTP_WHITELIST: localhost
# Yarn quarantines versions published less than npmMinimalAgeGate ago
# (default 1d, YN0016) — the packages under test are seconds old.
YARN_NPM_MINIMAL_AGE_GATE: 0
run: |
mkdir -p $HOME/install
cd $HOME/install
npm dist-tag ls @vendure/create
CREATE_VERSION=$(npm view @vendure/create@ci version)
echo "Resolved @vendure/create@ci -> $CREATE_VERSION"
# No --use-npm: the manager is detected from the invoking bunx / pnpm dlx.
${{ matrix.scaffold }}
- name: Assert the generated project used ${{ matrix.pm }}
run: |
cd $HOME/install/test-app
case "${{ matrix.pm }}" in
bun) test -f bun.lock || { echo "Expected bun.lock to exist"; ls -la; exit 1; } ;;
pnpm) test -f pnpm-lock.yaml || { echo "Expected pnpm-lock.yaml to exist"; ls -la; exit 1; } ;;
yarn) test -f yarn.lock || { echo "Expected yarn.lock to exist"; ls -la; exit 1; } ;;
esac
echo "Lockfile for ${{ matrix.pm }} present ✓"
- name: Server smoke tests
run: |
cd $HOME/install/test-app
${{ matrix.pm }} run dev &
node $GITHUB_WORKSPACE/.github/workflows/scripts/smoke-tests
- name: Kill dev server after smoke tests
if: always()
run: |
lsof -ti:3000,5173 | xargs kill 2>/dev/null || true
# Job 4: Exercise the Quick Start PostgreSQL path (docker compose up + readiness wait
# + populate against postgres), which `--ci` alone never touches. Runs three creates on
# the same runner — two of them monorepo — because the compose project-name collision
# that motivated this job (#4932) only bites from the second monorepo project onwards.
test_quickstart_postgres:
needs: build_and_publish
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Use Node.js 22.x
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22.x
- name: Download Verdaccio storage
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: verdaccio-storage
path: ~/verdaccio-download
- name: Setup Verdaccio with pre-built packages
run: |
npm install -g verdaccio
npm install -g wait-on
mkdir -p $HOME/.config/verdaccio
cp -v ./.github/workflows/verdaccio/config.yaml $HOME/.config/verdaccio/config.yaml
cd $HOME/.config/verdaccio
tar -xzf ~/verdaccio-download/verdaccio-storage.tar.gz
nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
wait-on http://localhost:4873
TOKEN_RES=$(curl -XPUT \
-H "Content-type: application/json" \
-d '{ "name": "test", "password": "test" }' \
'http://localhost:4873/-/user/org.couchdb.user:test')
TOKEN=$(echo "$TOKEN_RES" | jq -r '.token')
npm set registry=http://localhost:4873
npm set //localhost:4873/:_authToken $TOKEN
# The install directory deliberately contains a space: file URLs percent-encode
# it (%20), which used to leak into filesystem paths in the dashboard's vite
# plugins and break `npm run dev` (#4931).
- name: Quick Start with Postgres (single project)
timeout-minutes: 15
run: |
mkdir -p "$HOME/install dir"
cd "$HOME/install dir"
npx @vendure/create@ci shop-pg --ci --db postgres --use-npm --log-level info
- name: Dev server smoke test from a path containing a space
timeout-minutes: 15
run: |
cd "$HOME/install dir/shop-pg"
npm run dev &
wait-on http://localhost:3000/health --timeout 120000
# The dashboard Vite server failed to start when the project path
# percent-encoded (#4931), so it becoming reachable is the regression check.
wait-on http://localhost:5173/dashboard --timeout 180000
lsof -ti:3000,5173 | xargs kill 2>/dev/null || true
- name: Stop containers so the next project can bind port 6543
run: docker ps -q | xargs -r docker stop
- name: Quick Start with Postgres (monorepo, first)
timeout-minutes: 15
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
cd "$HOME/install dir"
npx @vendure/create@ci shop-mono-a --ci --db postgres --with-storefront --use-npm --log-level info
- name: Stop containers so the next project can bind port 6543
run: docker ps -q | xargs -r docker stop
- name: Quick Start with Postgres (monorepo, second — compose collision regression)
timeout-minutes: 15
# Deliberately runs with the previous monorepo project's stopped containers and
# volumes still present: before the fix, Compose derived the project name from
# the apps/server directory for BOTH projects and hung forever on an
# unanswerable "Recreate volume?" prompt.
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
cd "$HOME/install dir"
npx @vendure/create@ci shop-mono-b --ci --db postgres --with-storefront --use-npm --log-level info
- name: Assert each project got its own Compose project
run: |
docker ps -a --format '{{.Names}}' | sort
for project in shop-pg shop-mono-a shop-mono-b; do
docker ps -a --format '{{.Names}}' | grep -q "^${project}-postgres_db-1$" \
|| { echo "Expected container ${project}-postgres_db-1 to exist"; exit 1; }
done