From eb25d4352c4eb5249613bd21c107247589b3987b Mon Sep 17 00:00:00 2001 From: unknownproperty Date: Fri, 22 May 2026 04:11:45 +0300 Subject: [PATCH 1/8] feat(xi.calls): add publish jobs for calls packages --- .github/filters.yml | 34 +++++ .github/workflows/common-prepare.yml | 33 ++++ .github/workflows/front-development.yml | 21 +++ .github/workflows/front-production.yml | 93 ++++++++++++ .github/workflows/front-staging.yml | 11 ++ .github/workflows/generate-package-filters.sh | 17 +++ .github/workflows/publish-calls-packages.sh | 47 ++++++ docs/migrations/calls-build.md | 10 ++ scripts/calls-publish-lib.sh | 40 +++++ scripts/publish-calls-all.sh | 142 ++++++++++++++++++ 10 files changed, 448 insertions(+) create mode 100644 .github/filters.yml create mode 100644 .github/workflows/common-prepare.yml create mode 100644 .github/workflows/front-development.yml create mode 100644 .github/workflows/front-production.yml create mode 100644 .github/workflows/front-staging.yml create mode 100755 .github/workflows/generate-package-filters.sh create mode 100755 .github/workflows/publish-calls-packages.sh create mode 100644 scripts/calls-publish-lib.sh create mode 100755 scripts/publish-calls-all.sh diff --git a/.github/filters.yml b/.github/filters.yml new file mode 100644 index 0000000..05fecea --- /dev/null +++ b/.github/filters.yml @@ -0,0 +1,34 @@ +# Основная документация: https://github.com/dorny/paths-filter (см. всё к полю `filters`) + +# Пути прописываются в виде picomatch-паттернов: +# https://github.com/micromatch/picomatch#basic-globbing + +# Можно использовать все фишки YAML-я: +# https://ru.wikipedia.org/wiki/YAML#%D0%A1%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%81 + +# Имена последних объектов (packages, lintable) лучше не менять + +configs: &configs # изменения тут обязательно запускают все пайплайны + - ".github/workflows/*" + - "package.json" + - ".npmrc" + +app_common: &app_common # тригерят все билды (и депой в main-ветке) + - *configs + - "packages/**" + - "Dockerfile" + - "turbo.json" + +lintable: # запускает прогон линтера + - *configs + - ".eslintrc.js" + - "prettier.config.js" + - "**.[jt]s" + - "**.[jt]sx" + - "**.md" + +packages: # запускает деплой @xipkg/calls-* в main-ветке (только обновлённых) + - *configs + - "packages/calls/**" + - "packages/calls.*/**" + - "tsup.calls.base.ts" diff --git a/.github/workflows/common-prepare.yml b/.github/workflows/common-prepare.yml new file mode 100644 index 0000000..c942660 --- /dev/null +++ b/.github/workflows/common-prepare.yml @@ -0,0 +1,33 @@ +name: Common Preparation Steps + +on: + workflow_call: + outputs: + packages: + description: Will be 'true' if any package files have changed + value: ${{ jobs.analyze_files.outputs.packages }} + +jobs: + analyze_files: + runs-on: ubuntu-latest + + outputs: + lintable: ${{ steps.path_filter.outputs.lintable }} + packages: ${{ steps.path_filter.outputs.packages }} + + steps: + - uses: actions/checkout@v2 + name: Checkout repository + + - name: Filter paths + uses: dorny/paths-filter@v2 + id: path_filter + with: + filters: .github/filters.yml + + lint_and_format: + needs: analyze_files + if: needs.analyze_files.outputs.lintable == 'true' + uses: xi-effect/xi.actions/.github/workflows/front-lint-and-format.yml@main + with: + node-version: 22.22.1 diff --git a/.github/workflows/front-development.yml b/.github/workflows/front-development.yml new file mode 100644 index 0000000..629e2ae --- /dev/null +++ b/.github/workflows/front-development.yml @@ -0,0 +1,21 @@ +name: Development + +on: + push: + branches-ignore: + - main + - staging + workflow_dispatch: + +jobs: + prepare: + uses: ./.github/workflows/common-prepare.yml + + # build-frodo: + # needs: prepare + # if: needs.prepare.outputs.xi-frodo == 'true' + # uses: xi-effect/xi.actions/.github/workflows/docker-build-nocache.yml@main + # with: + # tag: frodo-${{ github.run_id }} + # arguments: "APP_NAME=xi.frodo" + # secrets: inherit diff --git a/.github/workflows/front-production.yml b/.github/workflows/front-production.yml new file mode 100644 index 0000000..4d07baa --- /dev/null +++ b/.github/workflows/front-production.yml @@ -0,0 +1,93 @@ +name: Production + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + id-token: write + +jobs: + prepare: + uses: ./.github/workflows/common-prepare.yml + + analyze-packages: + needs: prepare + if: needs.prepare.outputs.packages == 'true' + runs-on: ubuntu-latest + + env: + filename: .github/workflows/generated-filters.yml + + outputs: + packages: ${{ steps.package_filter.outputs.changes }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Generate package filters + run: ./.github/workflows/generate-package-filters.sh + shell: bash + + - name: Filter packages + uses: dorny/paths-filter@v2 + id: package_filter + with: + filters: ${{ env.filename }} + + deploy-packages: + needs: analyze-packages + if: needs.analyze-packages.outputs.packages != '[]' + runs-on: ubuntu-latest + + permissions: + contents: read + id-token: write + + environment: >- + ${{ + contains(fromJSON('["niqzart", "unknownproperty"]'), github.triggering_actor) + && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + && 'xi-production' + || 'xi-production-manual' + }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.22.1 + registry-url: https://registry.npmjs.org + cache: pnpm + + - name: Upgrade npm (required for trusted publishing) + run: npm install -g npm@latest + + - name: Verify Node/npm/pnpm versions + run: | + node -v + npm -v + pnpm -v + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Build calls packages + run: pnpm exec turbo run build --filter='./packages/calls...' + + - name: Publish changed packages + env: + CHANGED_PACKAGES: ${{ needs.analyze-packages.outputs.packages }} + run: ./.github/workflows/publish-calls-packages.sh diff --git a/.github/workflows/front-staging.yml b/.github/workflows/front-staging.yml new file mode 100644 index 0000000..3f63ee8 --- /dev/null +++ b/.github/workflows/front-staging.yml @@ -0,0 +1,11 @@ +name: Staging + +on: + push: + branches: + - staging + workflow_dispatch: + +jobs: + prepare: + uses: ./.github/workflows/common-prepare.yml diff --git a/.github/workflows/generate-package-filters.sh b/.github/workflows/generate-package-filters.sh new file mode 100755 index 0000000..01e8d9a --- /dev/null +++ b/.github/workflows/generate-package-filters.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -euo pipefail + +# Only @xipkg/calls-* packages (publishable) +packages=$(ls -d packages/calls*/ 2>/dev/null | sort) || exit 0 + +echo "# autogenerated packages data" > "$filename" + +for package in $packages; do + dirname=${package%/*} + dirname=${dirname#*/} + key=${dirname//\//-} + + printf '%s:\n - %s**\n' "$key" "$package" >> "$filename" +done + +echo "saved filters to $filename" diff --git a/.github/workflows/publish-calls-packages.sh b/.github/workflows/publish-calls-packages.sh new file mode 100755 index 0000000..e3840bf --- /dev/null +++ b/.github/workflows/publish-calls-packages.sh @@ -0,0 +1,47 @@ +#!/bin/bash +set -euo pipefail + +# Publish changed calls packages in dependency order (npm trusted publishing / OIDC). +# CHANGED_PACKAGES — JSON array from dorny/paths-filter, e.g. ["calls.types","calls.hooks"] + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +# shellcheck source=../../scripts/calls-publish-lib.sh +source "${ROOT}/scripts/calls-publish-lib.sh" + +CHANGED_PACKAGES="${CHANGED_PACKAGES:-[]}" + +is_changed() { + node -e " + const list = JSON.parse(process.argv[1]); + process.exit(list.includes(process.argv[2]) ? 0 : 1); + " "$CHANGED_PACKAGES" "$1" +} + +published=0 + +for pkg in "${CALLS_PUBLISH_ORDER[@]}"; do + if ! is_changed "$pkg"; then + continue + fi + + echo "::group::Publish packages/${pkg}" + cd "${ROOT}/packages/${pkg}" + + if npm publish --access public --provenance; then + echo "Published @xipkg package from packages/${pkg}" + published=$((published + 1)) + else + exit_code=$? + echo "npm publish failed for packages/${pkg} (exit ${exit_code})" + exit "$exit_code" + fi + + cd - > /dev/null + echo "::endgroup::" +done + +if [ "$published" -eq 0 ]; then + echo "No packages to publish." +else + echo "Published ${published} package(s)." +fi diff --git a/docs/migrations/calls-build.md b/docs/migrations/calls-build.md index 7e8c219..6e3e56c 100644 --- a/docs/migrations/calls-build.md +++ b/docs/migrations/calls-build.md @@ -73,6 +73,16 @@ Trusted publishing: workflow `front-production.yml`, environment `xi-production` Перед publish нужно **поднять version** в `package.json` — npm вернёт `409`, если версия уже есть. +## Первый ручной publish + +```bash +npm login +./scripts/publish-calls-all.sh --dry-run # проверка без загрузки +./scripts/publish-calls-all.sh # build + publish всех 11 пакетов +``` + +Опции: `--skip-build`, `--yes` (без подтверждения). + ## Замечания - Зависимости (`@xipkg/*`, `livekit-client`, `react` и т.д.) **не бандлятся** — `esbuildOptions.packages = 'external'` diff --git a/scripts/calls-publish-lib.sh b/scripts/calls-publish-lib.sh new file mode 100644 index 0000000..af6253c --- /dev/null +++ b/scripts/calls-publish-lib.sh @@ -0,0 +1,40 @@ +# Shared publish order for @xipkg/calls-* (dependencies first). +CALLS_PUBLISH_ORDER=( + calls.types + calls.config + calls.utils + calls.store + calls.providers + calls.hooks + calls.ui + calls.risehand + calls.chat + calls.compactview + calls +) + +get_package_field() { + local pkg_dir=$1 + local field=$2 + node -e " + const pkg = JSON.parse(require('fs').readFileSync('${pkg_dir}/package.json', 'utf8')); + const value = pkg['${field}']; + if (value === undefined) process.exit(1); + console.log(value); + " +} + +assert_dist_built() { + local pkg_dir=$1 + local name=$2 + + if [[ ! -f "${pkg_dir}/dist/index.mjs" ]]; then + echo "ERROR: ${name}: dist/index.mjs not found. Run build first." >&2 + exit 1 + fi + + if [[ ! -f "${pkg_dir}/dist/index.d.ts" ]]; then + echo "ERROR: ${name}: dist/index.d.ts not found. Run build first." >&2 + exit 1 + fi +} diff --git a/scripts/publish-calls-all.sh b/scripts/publish-calls-all.sh new file mode 100755 index 0000000..9cc4f7b --- /dev/null +++ b/scripts/publish-calls-all.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +set -euo pipefail + +# First-time (or full) manual publish of all @xipkg/calls-* packages to npm. +# +# Prerequisites: +# 1. npm login (or valid NODE_AUTH_TOKEN with publish access to @xipkg) +# 2. Versions bumped in package.json (npm rejects duplicate versions with 409) +# 3. pnpm install completed +# +# Usage: +# ./scripts/publish-calls-all.sh # build + publish all +# ./scripts/publish-calls-all.sh --dry-run # build + npm publish --dry-run +# ./scripts/publish-calls-all.sh --skip-build # publish only (dist must exist) +# ./scripts/publish-calls-all.sh --yes # skip confirmation prompt + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# shellcheck source=calls-publish-lib.sh +source "${ROOT}/scripts/calls-publish-lib.sh" + +DRY_RUN=false +SKIP_BUILD=false +ASSUME_YES=false + +usage() { + cat <<'EOF' +Usage: ./scripts/publish-calls-all.sh [options] + +Options: + --dry-run Run npm publish --dry-run (no upload) + --skip-build Skip install/build (requires existing dist/ in each package) + --yes Publish without confirmation prompt + -h, --help Show this help +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) + DRY_RUN=true + shift + ;; + --skip-build) + SKIP_BUILD=true + shift + ;; + --yes) + ASSUME_YES=true + shift + ;; + -h | --help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +cd "$ROOT" + +echo "==> Checking npm authentication" +if ! npm whoami; then + echo "ERROR: Not logged in to npm. Run: npm login" >&2 + exit 1 +fi + +if [[ "$SKIP_BUILD" == false ]]; then + echo "==> Installing dependencies (without lifecycle scripts)" + # --ignore-scripts: avoids broken optional postinstalls (e.g. unrs-resolver/napi-postinstall) + pnpm install --frozen-lockfile --ignore-scripts + + echo "==> Building all calls packages" + pnpm exec turbo run build --filter='./packages/calls...' +else + echo "==> Skipping build (--skip-build)" +fi + +echo +echo "Packages to publish (in order):" +for pkg in "${CALLS_PUBLISH_ORDER[@]}"; do + pkg_dir="${ROOT}/packages/${pkg}" + name="$(get_package_field "$pkg_dir" name)" + version="$(get_package_field "$pkg_dir" version)" + assert_dist_built "$pkg_dir" "$name" + echo " - ${name}@${version}" +done + +if [[ "$DRY_RUN" == true ]]; then + echo + echo "DRY RUN — nothing will be uploaded to npm." +fi + +if [[ "$ASSUME_YES" == false ]]; then + echo + read -r -p "Continue? [y/N] " reply + if [[ ! "$reply" =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 + fi +fi + +published=0 + +for pkg in "${CALLS_PUBLISH_ORDER[@]}"; do + pkg_dir="${ROOT}/packages/${pkg}" + name="$(get_package_field "$pkg_dir" name)" + version="$(get_package_field "$pkg_dir" version)" + + echo + echo "==> Publishing ${name}@${version} (packages/${pkg})" + + pushd "$pkg_dir" > /dev/null + + publish_args=(publish --access public) + if [[ "$DRY_RUN" == true ]]; then + publish_args+=(--dry-run) + fi + + if npm "${publish_args[@]}"; then + published=$((published + 1)) + echo "OK: ${name}@${version}" + else + exit_code=$? + echo "FAILED: ${name}@${version} (exit ${exit_code})" >&2 + popd > /dev/null + exit "$exit_code" + fi + + popd > /dev/null +done + +echo +if [[ "$DRY_RUN" == true ]]; then + echo "Dry run complete for ${published} package(s)." +else + echo "Published ${published} package(s) to npm." + echo "Next: configure Trusted Publisher on npmjs.com for each package (workflow: front-production.yml)." +fi From 30b59f615dfce651f94f90eea912a4ecc43bb25a Mon Sep 17 00:00:00 2001 From: unknownproperty Date: Fri, 22 May 2026 19:14:55 +0300 Subject: [PATCH 2/8] fix: folder registry --- packages/{calls.riseHand => calls.risehand}/README.md | 0 packages/{calls.riseHand => calls.risehand}/eslint.config.js | 0 packages/{calls.riseHand => calls.risehand}/index.ts | 0 packages/{calls.riseHand => calls.risehand}/package.json | 0 packages/{calls.riseHand => calls.risehand}/src/hooks/index.ts | 0 .../{calls.riseHand => calls.risehand}/src/hooks/useHandFocus.ts | 0 .../src/hooks/useRaisedHands.ts | 0 packages/{calls.riseHand => calls.risehand}/src/index.ts | 0 .../{calls.riseHand => calls.risehand}/src/ui/RaiseHandButton.tsx | 0 packages/{calls.riseHand => calls.risehand}/src/ui/index.ts | 0 packages/{calls.riseHand => calls.risehand}/tsconfig.json | 0 packages/{calls.riseHand => calls.risehand}/tsup.config.ts | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename packages/{calls.riseHand => calls.risehand}/README.md (100%) rename packages/{calls.riseHand => calls.risehand}/eslint.config.js (100%) rename packages/{calls.riseHand => calls.risehand}/index.ts (100%) rename packages/{calls.riseHand => calls.risehand}/package.json (100%) rename packages/{calls.riseHand => calls.risehand}/src/hooks/index.ts (100%) rename packages/{calls.riseHand => calls.risehand}/src/hooks/useHandFocus.ts (100%) rename packages/{calls.riseHand => calls.risehand}/src/hooks/useRaisedHands.ts (100%) rename packages/{calls.riseHand => calls.risehand}/src/index.ts (100%) rename packages/{calls.riseHand => calls.risehand}/src/ui/RaiseHandButton.tsx (100%) rename packages/{calls.riseHand => calls.risehand}/src/ui/index.ts (100%) rename packages/{calls.riseHand => calls.risehand}/tsconfig.json (100%) rename packages/{calls.riseHand => calls.risehand}/tsup.config.ts (100%) diff --git a/packages/calls.riseHand/README.md b/packages/calls.risehand/README.md similarity index 100% rename from packages/calls.riseHand/README.md rename to packages/calls.risehand/README.md diff --git a/packages/calls.riseHand/eslint.config.js b/packages/calls.risehand/eslint.config.js similarity index 100% rename from packages/calls.riseHand/eslint.config.js rename to packages/calls.risehand/eslint.config.js diff --git a/packages/calls.riseHand/index.ts b/packages/calls.risehand/index.ts similarity index 100% rename from packages/calls.riseHand/index.ts rename to packages/calls.risehand/index.ts diff --git a/packages/calls.riseHand/package.json b/packages/calls.risehand/package.json similarity index 100% rename from packages/calls.riseHand/package.json rename to packages/calls.risehand/package.json diff --git a/packages/calls.riseHand/src/hooks/index.ts b/packages/calls.risehand/src/hooks/index.ts similarity index 100% rename from packages/calls.riseHand/src/hooks/index.ts rename to packages/calls.risehand/src/hooks/index.ts diff --git a/packages/calls.riseHand/src/hooks/useHandFocus.ts b/packages/calls.risehand/src/hooks/useHandFocus.ts similarity index 100% rename from packages/calls.riseHand/src/hooks/useHandFocus.ts rename to packages/calls.risehand/src/hooks/useHandFocus.ts diff --git a/packages/calls.riseHand/src/hooks/useRaisedHands.ts b/packages/calls.risehand/src/hooks/useRaisedHands.ts similarity index 100% rename from packages/calls.riseHand/src/hooks/useRaisedHands.ts rename to packages/calls.risehand/src/hooks/useRaisedHands.ts diff --git a/packages/calls.riseHand/src/index.ts b/packages/calls.risehand/src/index.ts similarity index 100% rename from packages/calls.riseHand/src/index.ts rename to packages/calls.risehand/src/index.ts diff --git a/packages/calls.riseHand/src/ui/RaiseHandButton.tsx b/packages/calls.risehand/src/ui/RaiseHandButton.tsx similarity index 100% rename from packages/calls.riseHand/src/ui/RaiseHandButton.tsx rename to packages/calls.risehand/src/ui/RaiseHandButton.tsx diff --git a/packages/calls.riseHand/src/ui/index.ts b/packages/calls.risehand/src/ui/index.ts similarity index 100% rename from packages/calls.riseHand/src/ui/index.ts rename to packages/calls.risehand/src/ui/index.ts diff --git a/packages/calls.riseHand/tsconfig.json b/packages/calls.risehand/tsconfig.json similarity index 100% rename from packages/calls.riseHand/tsconfig.json rename to packages/calls.risehand/tsconfig.json diff --git a/packages/calls.riseHand/tsup.config.ts b/packages/calls.risehand/tsup.config.ts similarity index 100% rename from packages/calls.riseHand/tsup.config.ts rename to packages/calls.risehand/tsup.config.ts From 5c973f426dc4bbbd4b816a78ffc385c4af87939c Mon Sep 17 00:00:00 2001 From: unknownproperty Date: Fri, 22 May 2026 19:20:13 +0300 Subject: [PATCH 3/8] feat: change to pnpm lint job --- .github/workflows/common-prepare.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/common-prepare.yml b/.github/workflows/common-prepare.yml index c942660..eefc5c5 100644 --- a/.github/workflows/common-prepare.yml +++ b/.github/workflows/common-prepare.yml @@ -28,6 +28,28 @@ jobs: lint_and_format: needs: analyze_files if: needs.analyze_files.outputs.lintable == 'true' - uses: xi-effect/xi.actions/.github/workflows/front-lint-and-format.yml@main - with: - node-version: 22.22.1 + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.22.1 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Check format + run: pnpm run format-check + + - name: Lint + run: pnpm run lint From 67e651eb06f11bd784fc6d90652dd7af6da059c2 Mon Sep 17 00:00:00 2001 From: unknownproperty Date: Sat, 23 May 2026 00:39:42 +0300 Subject: [PATCH 4/8] feat: move sh scripts to node and actions --- .../generate-package-filters/action.yml | 20 ++ .../actions/publish-calls-packages/action.yml | 14 ++ .github/actions/setup-pnpm/action.yml | 40 ++++ .github/filters.yml | 13 +- .github/workflows/common-prepare.yml | 21 +- .github/workflows/front-production.yml | 31 +-- .github/workflows/generate-package-filters.sh | 17 -- .github/workflows/publish-calls-packages.sh | 47 ---- .gitignore | 1 + docs/migrations/calls-build.md | 6 +- package.json | 2 + scripts/calls-publish-lib.mjs | 41 ++++ scripts/calls-publish-lib.sh | 40 ---- scripts/calls-publish-order.json | 13 ++ scripts/generate-package-filters.mjs | 59 +++++ scripts/publish-calls-all.sh | 142 ----------- scripts/publish-calls-packages.mjs | 221 ++++++++++++++++++ 17 files changed, 434 insertions(+), 294 deletions(-) create mode 100644 .github/actions/generate-package-filters/action.yml create mode 100644 .github/actions/publish-calls-packages/action.yml create mode 100644 .github/actions/setup-pnpm/action.yml delete mode 100755 .github/workflows/generate-package-filters.sh delete mode 100755 .github/workflows/publish-calls-packages.sh create mode 100644 scripts/calls-publish-lib.mjs delete mode 100644 scripts/calls-publish-lib.sh create mode 100644 scripts/calls-publish-order.json create mode 100644 scripts/generate-package-filters.mjs delete mode 100755 scripts/publish-calls-all.sh create mode 100644 scripts/publish-calls-packages.mjs diff --git a/.github/actions/generate-package-filters/action.yml b/.github/actions/generate-package-filters/action.yml new file mode 100644 index 0000000..280414a --- /dev/null +++ b/.github/actions/generate-package-filters/action.yml @@ -0,0 +1,20 @@ +name: Generate package filters +description: Generate dorny/paths-filter config for publishable @xipkg/calls-* packages + +inputs: + output-file: + description: Path to write the generated filters YAML + required: false + default: .github/generated/package-filters.yml + +outputs: + output-file: + description: Path to the generated filters file + value: ${{ inputs.output-file }} + +runs: + using: composite + steps: + - name: Generate package filters + shell: bash + run: node scripts/generate-package-filters.mjs --output "${{ inputs.output-file }}" diff --git a/.github/actions/publish-calls-packages/action.yml b/.github/actions/publish-calls-packages/action.yml new file mode 100644 index 0000000..3db7fd4 --- /dev/null +++ b/.github/actions/publish-calls-packages/action.yml @@ -0,0 +1,14 @@ +name: Publish changed calls packages +description: Publish changed @xipkg/calls-* packages in dependency order (npm trusted publishing / OIDC) + +inputs: + changed-packages: + description: JSON array from dorny/paths-filter, e.g. ["calls.types","calls.hooks"] + required: true + +runs: + using: composite + steps: + - name: Publish changed packages + shell: bash + run: node scripts/publish-calls-packages.mjs --changed-packages '${{ inputs.changed-packages }}' --provenance diff --git a/.github/actions/setup-pnpm/action.yml b/.github/actions/setup-pnpm/action.yml new file mode 100644 index 0000000..9fac72d --- /dev/null +++ b/.github/actions/setup-pnpm/action.yml @@ -0,0 +1,40 @@ +name: Setup pnpm +description: Install pnpm, Node.js, and workspace dependencies + +inputs: + node-version: + description: Node.js version + required: false + default: "22.22.1" + pnpm-version: + description: pnpm version + required: false + default: "8.15.4" + registry-url: + description: npm registry URL (optional, for publish jobs) + required: false + default: "" + install: + description: Whether to run pnpm install + required: false + default: "true" + +runs: + using: composite + steps: + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ inputs.pnpm-version }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + cache: pnpm + registry-url: ${{ inputs.registry-url }} + + - name: Install dependencies + if: inputs.install == 'true' + shell: bash + run: pnpm install --frozen-lockfile --ignore-scripts diff --git a/.github/filters.yml b/.github/filters.yml index 05fecea..03e2a60 100644 --- a/.github/filters.yml +++ b/.github/filters.yml @@ -10,18 +10,17 @@ configs: &configs # изменения тут обязательно запускают все пайплайны - ".github/workflows/*" + - ".github/actions/**" - "package.json" - ".npmrc" - -app_common: &app_common # тригерят все билды (и депой в main-ветке) - - *configs - - "packages/**" - - "Dockerfile" - - "turbo.json" + - "scripts/calls-publish-order.json" + - "scripts/calls-publish-lib.mjs" + - "scripts/generate-package-filters.mjs" + - "scripts/publish-calls-packages.mjs" lintable: # запускает прогон линтера - *configs - - ".eslintrc.js" + - "**/eslint.config.js" - "prettier.config.js" - "**.[jt]s" - "**.[jt]sx" diff --git a/.github/workflows/common-prepare.yml b/.github/workflows/common-prepare.yml index eefc5c5..44c087f 100644 --- a/.github/workflows/common-prepare.yml +++ b/.github/workflows/common-prepare.yml @@ -16,11 +16,11 @@ jobs: packages: ${{ steps.path_filter.outputs.packages }} steps: - - uses: actions/checkout@v2 - name: Checkout repository + - name: Checkout repository + uses: actions/checkout@v4 - name: Filter paths - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v4 id: path_filter with: filters: .github/filters.yml @@ -34,19 +34,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 8.15.4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.22.1 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile --ignore-scripts + - name: Setup pnpm + uses: ./.github/actions/setup-pnpm - name: Check format run: pnpm run format-check diff --git a/.github/workflows/front-production.yml b/.github/workflows/front-production.yml index 4d07baa..aef8978 100644 --- a/.github/workflows/front-production.yml +++ b/.github/workflows/front-production.yml @@ -19,9 +19,6 @@ jobs: if: needs.prepare.outputs.packages == 'true' runs-on: ubuntu-latest - env: - filename: .github/workflows/generated-filters.yml - outputs: packages: ${{ steps.package_filter.outputs.changes }} @@ -30,14 +27,14 @@ jobs: uses: actions/checkout@v4 - name: Generate package filters - run: ./.github/workflows/generate-package-filters.sh - shell: bash + id: generate_filters + uses: ./.github/actions/generate-package-filters - name: Filter packages - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v4 id: package_filter with: - filters: ${{ env.filename }} + filters: ${{ steps.generate_filters.outputs.output-file }} deploy-packages: needs: analyze-packages @@ -60,17 +57,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 8.15.4 - - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup pnpm + uses: ./.github/actions/setup-pnpm with: - node-version: 22.22.1 registry-url: https://registry.npmjs.org - cache: pnpm - name: Upgrade npm (required for trusted publishing) run: npm install -g npm@latest @@ -81,13 +71,10 @@ jobs: npm -v pnpm -v - - name: Install dependencies - run: pnpm install --frozen-lockfile --ignore-scripts - - name: Build calls packages run: pnpm exec turbo run build --filter='./packages/calls...' - name: Publish changed packages - env: - CHANGED_PACKAGES: ${{ needs.analyze-packages.outputs.packages }} - run: ./.github/workflows/publish-calls-packages.sh + uses: ./.github/actions/publish-calls-packages + with: + changed-packages: ${{ needs.analyze-packages.outputs.packages }} diff --git a/.github/workflows/generate-package-filters.sh b/.github/workflows/generate-package-filters.sh deleted file mode 100755 index 01e8d9a..0000000 --- a/.github/workflows/generate-package-filters.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Only @xipkg/calls-* packages (publishable) -packages=$(ls -d packages/calls*/ 2>/dev/null | sort) || exit 0 - -echo "# autogenerated packages data" > "$filename" - -for package in $packages; do - dirname=${package%/*} - dirname=${dirname#*/} - key=${dirname//\//-} - - printf '%s:\n - %s**\n' "$key" "$package" >> "$filename" -done - -echo "saved filters to $filename" diff --git a/.github/workflows/publish-calls-packages.sh b/.github/workflows/publish-calls-packages.sh deleted file mode 100755 index e3840bf..0000000 --- a/.github/workflows/publish-calls-packages.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Publish changed calls packages in dependency order (npm trusted publishing / OIDC). -# CHANGED_PACKAGES — JSON array from dorny/paths-filter, e.g. ["calls.types","calls.hooks"] - -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -# shellcheck source=../../scripts/calls-publish-lib.sh -source "${ROOT}/scripts/calls-publish-lib.sh" - -CHANGED_PACKAGES="${CHANGED_PACKAGES:-[]}" - -is_changed() { - node -e " - const list = JSON.parse(process.argv[1]); - process.exit(list.includes(process.argv[2]) ? 0 : 1); - " "$CHANGED_PACKAGES" "$1" -} - -published=0 - -for pkg in "${CALLS_PUBLISH_ORDER[@]}"; do - if ! is_changed "$pkg"; then - continue - fi - - echo "::group::Publish packages/${pkg}" - cd "${ROOT}/packages/${pkg}" - - if npm publish --access public --provenance; then - echo "Published @xipkg package from packages/${pkg}" - published=$((published + 1)) - else - exit_code=$? - echo "npm publish failed for packages/${pkg} (exit ${exit_code})" - exit "$exit_code" - fi - - cd - > /dev/null - echo "::endgroup::" -done - -if [ "$published" -eq 0 ]; then - echo "No packages to publish." -else - echo "Published ${published} package(s)." -fi diff --git a/.gitignore b/.gitignore index e722be1..04dae2f 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ yarn-error.log* # Misc .DS_Store *.pem +.github/generated/ /.idea/.gitignore /.idea/codeStyles/codeStyleConfig.xml /.idea/dbnavigator.xml diff --git a/docs/migrations/calls-build.md b/docs/migrations/calls-build.md index 6e3e56c..cdcd395 100644 --- a/docs/migrations/calls-build.md +++ b/docs/migrations/calls-build.md @@ -77,11 +77,11 @@ Trusted publishing: workflow `front-production.yml`, environment `xi-production` ```bash npm login -./scripts/publish-calls-all.sh --dry-run # проверка без загрузки -./scripts/publish-calls-all.sh # build + publish всех 11 пакетов +pnpm run publish:calls:dry-run # проверка без загрузки +pnpm run publish:calls # build + publish всех 11 пакетов ``` -Опции: `--skip-build`, `--yes` (без подтверждения). +Дополнительные флаги: `node scripts/publish-calls-packages.mjs --all --skip-build --yes`. ## Замечания diff --git a/package.json b/package.json index f68f83d..1566ce8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "lint": "turbo run lint", "format": "prettier --write \"**/*.{ts,tsx,md}\"", "format-check": "prettier --check \"**/*.{ts,tsx,md}\"", + "publish:calls": "node scripts/publish-calls-packages.mjs --all", + "publish:calls:dry-run": "node scripts/publish-calls-packages.mjs --all --dry-run --yes", "prepare": "husky" }, "devDependencies": { diff --git a/scripts/calls-publish-lib.mjs b/scripts/calls-publish-lib.mjs new file mode 100644 index 0000000..3b25f79 --- /dev/null +++ b/scripts/calls-publish-lib.mjs @@ -0,0 +1,41 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptsDir = path.dirname(fileURLToPath(import.meta.url)); + +export const CALLS_PUBLISH_ORDER = JSON.parse( + fs.readFileSync(path.join(scriptsDir, 'calls-publish-order.json'), 'utf8'), +); + +export function getRepoRoot(fromDir = scriptsDir) { + return path.resolve(fromDir, '..'); +} + +export function readPackageField(pkgDir, field) { + const pkgPath = path.join(pkgDir, 'package.json'); + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + + if (pkg[field] === undefined) { + throw new Error(`Missing "${field}" in ${pkgPath}`); + } + + return pkg[field]; +} + +export function assertDistBuilt(pkgDir, name) { + for (const file of ['dist/index.mjs', 'dist/index.d.ts']) { + const filePath = path.join(pkgDir, file); + + if (!fs.existsSync(filePath)) { + throw new Error(`${name}: ${file} not found. Run build first.`); + } + } +} + +export function listCallsPackageNames(packagesDir) { + return fs + .readdirSync(packagesDir) + .filter((name) => name === 'calls' || name.startsWith('calls.')) + .sort(); +} diff --git a/scripts/calls-publish-lib.sh b/scripts/calls-publish-lib.sh deleted file mode 100644 index af6253c..0000000 --- a/scripts/calls-publish-lib.sh +++ /dev/null @@ -1,40 +0,0 @@ -# Shared publish order for @xipkg/calls-* (dependencies first). -CALLS_PUBLISH_ORDER=( - calls.types - calls.config - calls.utils - calls.store - calls.providers - calls.hooks - calls.ui - calls.risehand - calls.chat - calls.compactview - calls -) - -get_package_field() { - local pkg_dir=$1 - local field=$2 - node -e " - const pkg = JSON.parse(require('fs').readFileSync('${pkg_dir}/package.json', 'utf8')); - const value = pkg['${field}']; - if (value === undefined) process.exit(1); - console.log(value); - " -} - -assert_dist_built() { - local pkg_dir=$1 - local name=$2 - - if [[ ! -f "${pkg_dir}/dist/index.mjs" ]]; then - echo "ERROR: ${name}: dist/index.mjs not found. Run build first." >&2 - exit 1 - fi - - if [[ ! -f "${pkg_dir}/dist/index.d.ts" ]]; then - echo "ERROR: ${name}: dist/index.d.ts not found. Run build first." >&2 - exit 1 - fi -} diff --git a/scripts/calls-publish-order.json b/scripts/calls-publish-order.json new file mode 100644 index 0000000..e4a5b9a --- /dev/null +++ b/scripts/calls-publish-order.json @@ -0,0 +1,13 @@ +[ + "calls.types", + "calls.config", + "calls.utils", + "calls.store", + "calls.providers", + "calls.hooks", + "calls.ui", + "calls.risehand", + "calls.chat", + "calls.compactview", + "calls" +] diff --git a/scripts/generate-package-filters.mjs b/scripts/generate-package-filters.mjs new file mode 100644 index 0000000..b13dd36 --- /dev/null +++ b/scripts/generate-package-filters.mjs @@ -0,0 +1,59 @@ +#!/usr/bin/env node + +import fs from 'node:fs'; +import path from 'node:path'; +import { parseArgs } from 'node:util'; +import { pathToFileURL } from 'node:url'; +import { getRepoRoot, listCallsPackageNames } from './calls-publish-lib.mjs'; + +export function generatePackageFilters({ rootDir, outputFile }) { + const packagesDir = path.join(rootDir, 'packages'); + const packageNames = listCallsPackageNames(packagesDir); + + const lines = ['# autogenerated packages data']; + + for (const name of packageNames) { + const key = name.replaceAll('/', '-'); + lines.push(`${key}:`, ` - packages/${name}/**`); + } + + fs.mkdirSync(path.dirname(outputFile), { recursive: true }); + fs.writeFileSync(outputFile, `${lines.join('\n')}\n`, 'utf8'); + + return outputFile; +} + +function parseCliArgs(argv) { + const { values } = parseArgs({ + args: argv, + options: { + output: { type: 'string', short: 'o' }, + help: { type: 'boolean', short: 'h', default: false }, + }, + allowPositionals: false, + }); + + if (values.help) { + console.log(`Usage: node scripts/generate-package-filters.mjs [--output ] + +Options: + --output, -o Output file path (default: .github/generated/package-filters.yml) + --help, -h Show this help +`); + process.exit(0); + } + + return values; +} + +if (import.meta.url === pathToFileURL(process.argv[1]).href) { + const rootDir = getRepoRoot(); + const args = parseCliArgs(process.argv.slice(2)); + const outputFile = path.resolve( + rootDir, + args.output ?? '.github/generated/package-filters.yml', + ); + + generatePackageFilters({ rootDir, outputFile }); + console.log(`saved filters to ${path.relative(rootDir, outputFile)}`); +} diff --git a/scripts/publish-calls-all.sh b/scripts/publish-calls-all.sh deleted file mode 100755 index 9cc4f7b..0000000 --- a/scripts/publish-calls-all.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# First-time (or full) manual publish of all @xipkg/calls-* packages to npm. -# -# Prerequisites: -# 1. npm login (or valid NODE_AUTH_TOKEN with publish access to @xipkg) -# 2. Versions bumped in package.json (npm rejects duplicate versions with 409) -# 3. pnpm install completed -# -# Usage: -# ./scripts/publish-calls-all.sh # build + publish all -# ./scripts/publish-calls-all.sh --dry-run # build + npm publish --dry-run -# ./scripts/publish-calls-all.sh --skip-build # publish only (dist must exist) -# ./scripts/publish-calls-all.sh --yes # skip confirmation prompt - -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -# shellcheck source=calls-publish-lib.sh -source "${ROOT}/scripts/calls-publish-lib.sh" - -DRY_RUN=false -SKIP_BUILD=false -ASSUME_YES=false - -usage() { - cat <<'EOF' -Usage: ./scripts/publish-calls-all.sh [options] - -Options: - --dry-run Run npm publish --dry-run (no upload) - --skip-build Skip install/build (requires existing dist/ in each package) - --yes Publish without confirmation prompt - -h, --help Show this help -EOF -} - -while [[ $# -gt 0 ]]; do - case "$1" in - --dry-run) - DRY_RUN=true - shift - ;; - --skip-build) - SKIP_BUILD=true - shift - ;; - --yes) - ASSUME_YES=true - shift - ;; - -h | --help) - usage - exit 0 - ;; - *) - echo "Unknown option: $1" >&2 - usage >&2 - exit 1 - ;; - esac -done - -cd "$ROOT" - -echo "==> Checking npm authentication" -if ! npm whoami; then - echo "ERROR: Not logged in to npm. Run: npm login" >&2 - exit 1 -fi - -if [[ "$SKIP_BUILD" == false ]]; then - echo "==> Installing dependencies (without lifecycle scripts)" - # --ignore-scripts: avoids broken optional postinstalls (e.g. unrs-resolver/napi-postinstall) - pnpm install --frozen-lockfile --ignore-scripts - - echo "==> Building all calls packages" - pnpm exec turbo run build --filter='./packages/calls...' -else - echo "==> Skipping build (--skip-build)" -fi - -echo -echo "Packages to publish (in order):" -for pkg in "${CALLS_PUBLISH_ORDER[@]}"; do - pkg_dir="${ROOT}/packages/${pkg}" - name="$(get_package_field "$pkg_dir" name)" - version="$(get_package_field "$pkg_dir" version)" - assert_dist_built "$pkg_dir" "$name" - echo " - ${name}@${version}" -done - -if [[ "$DRY_RUN" == true ]]; then - echo - echo "DRY RUN — nothing will be uploaded to npm." -fi - -if [[ "$ASSUME_YES" == false ]]; then - echo - read -r -p "Continue? [y/N] " reply - if [[ ! "$reply" =~ ^[Yy]$ ]]; then - echo "Aborted." - exit 0 - fi -fi - -published=0 - -for pkg in "${CALLS_PUBLISH_ORDER[@]}"; do - pkg_dir="${ROOT}/packages/${pkg}" - name="$(get_package_field "$pkg_dir" name)" - version="$(get_package_field "$pkg_dir" version)" - - echo - echo "==> Publishing ${name}@${version} (packages/${pkg})" - - pushd "$pkg_dir" > /dev/null - - publish_args=(publish --access public) - if [[ "$DRY_RUN" == true ]]; then - publish_args+=(--dry-run) - fi - - if npm "${publish_args[@]}"; then - published=$((published + 1)) - echo "OK: ${name}@${version}" - else - exit_code=$? - echo "FAILED: ${name}@${version} (exit ${exit_code})" >&2 - popd > /dev/null - exit "$exit_code" - fi - - popd > /dev/null -done - -echo -if [[ "$DRY_RUN" == true ]]; then - echo "Dry run complete for ${published} package(s)." -else - echo "Published ${published} package(s) to npm." - echo "Next: configure Trusted Publisher on npmjs.com for each package (workflow: front-production.yml)." -fi diff --git a/scripts/publish-calls-packages.mjs b/scripts/publish-calls-packages.mjs new file mode 100644 index 0000000..8ac4eab --- /dev/null +++ b/scripts/publish-calls-packages.mjs @@ -0,0 +1,221 @@ +#!/usr/bin/env node + +import readline from 'node:readline/promises'; +import { spawnSync } from 'node:child_process'; +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { stdin as input, stdout as output } from 'node:process'; +import { parseArgs } from 'node:util'; +import { + assertDistBuilt, + CALLS_PUBLISH_ORDER, + getRepoRoot, + readPackageField, +} from './calls-publish-lib.mjs'; + +function isCi() { + return process.env.GITHUB_ACTIONS === 'true'; +} + +function group(title, fn) { + if (isCi()) { + console.log(`::group::${title}`); + } else { + console.log(title); + } + + try { + return fn(); + } finally { + if (isCi()) { + console.log('::endgroup::'); + } + } +} + +function run(command, args, options = {}) { + const result = spawnSync(command, args, { + stdio: 'inherit', + ...options, + }); + + if (result.error) { + throw result.error; + } + + if (result.status !== 0) { + throw new Error(`${command} ${args.join(' ')} failed with exit code ${result.status}`); + } +} + +function parseChangedPackages(raw) { + const parsed = JSON.parse(raw); + + if (!Array.isArray(parsed)) { + throw new Error('changed-packages must be a JSON array'); + } + + return parsed; +} + +function resolvePackagesToPublish(changedPackages) { + if (changedPackages) { + return CALLS_PUBLISH_ORDER.filter((pkg) => changedPackages.includes(pkg)); + } + + return [...CALLS_PUBLISH_ORDER]; +} + +function publishPackage(pkgDir, { dryRun, provenance }) { + const args = ['publish', '--access', 'public']; + + if (dryRun) { + args.push('--dry-run'); + } + + if (provenance) { + args.push('--provenance'); + } + + run('npm', args, { cwd: pkgDir }); +} + +export async function publishCallsPackages(options) { + const rootDir = options.rootDir ?? getRepoRoot(); + const changedPackages = options.changedPackages + ? parseChangedPackages(options.changedPackages) + : null; + const packagesToPublish = resolvePackagesToPublish(changedPackages); + + if (packagesToPublish.length === 0) { + console.log('No packages to publish.'); + return 0; + } + + if (!changedPackages) { + run('npm', ['whoami']); + } + + if (!changedPackages && !options.skipBuild) { + run('pnpm', ['install', '--frozen-lockfile', '--ignore-scripts'], { cwd: rootDir }); + run('pnpm', ['exec', 'turbo', 'run', 'build', '--filter=./packages/calls...'], { + cwd: rootDir, + }); + } else if (!changedPackages && options.skipBuild) { + console.log('Skipping build (--skip-build)'); + } + + console.log('\nPackages to publish (in order):'); + for (const pkg of packagesToPublish) { + const pkgDir = path.join(rootDir, 'packages', pkg); + const name = readPackageField(pkgDir, 'name'); + const version = readPackageField(pkgDir, 'version'); + assertDistBuilt(pkgDir, name); + console.log(` - ${name}@${version}`); + } + + if (options.dryRun) { + console.log('\nDRY RUN — nothing will be uploaded to npm.'); + } + + if (!changedPackages && !options.yes) { + const rl = readline.createInterface({ input, output }); + const reply = await rl.question('\nContinue? [y/N] '); + rl.close(); + + if (!/^y$/i.test(reply.trim())) { + console.log('Aborted.'); + return 0; + } + } + + let published = 0; + + for (const pkg of packagesToPublish) { + const pkgDir = path.join(rootDir, 'packages', pkg); + const name = readPackageField(pkgDir, 'name'); + const version = readPackageField(pkgDir, 'version'); + + group(`Publish packages/${pkg}`, () => { + console.log(`Publishing ${name}@${version}`); + publishPackage(pkgDir, { + dryRun: options.dryRun, + provenance: options.provenance, + }); + published += 1; + console.log(`Published ${name}@${version}`); + }); + } + + if (options.dryRun) { + console.log(`\nDry run complete for ${published} package(s).`); + } else if (published > 0) { + console.log(`\nPublished ${published} package(s).`); + + if (!changedPackages) { + console.log( + 'Next: configure Trusted Publisher on npmjs.com for each package (workflow: front-production.yml).', + ); + } + } + + return published; +} + +function parseCliArgs(argv) { + const { values } = parseArgs({ + args: argv, + options: { + all: { type: 'boolean', default: false }, + 'changed-packages': { type: 'string' }, + 'dry-run': { type: 'boolean', default: false }, + 'skip-build': { type: 'boolean', default: false }, + yes: { type: 'boolean', default: false }, + provenance: { type: 'boolean', default: false }, + help: { type: 'boolean', short: 'h', default: false }, + }, + allowPositionals: false, + }); + + if (values.help) { + console.log(`Usage: node scripts/publish-calls-packages.mjs [options] + +Options: + --changed-packages Publish only changed packages (CI mode) + --all Publish all calls packages in dependency order + --dry-run Run npm publish --dry-run + --skip-build Skip install/build before publishing all packages + --yes Skip confirmation prompt + --provenance Pass --provenance to npm publish (CI trusted publishing) + --help, -h Show this help +`); + process.exit(0); + } + + return values; +} + +if (import.meta.url === pathToFileURL(process.argv[1]).href) { + const args = parseCliArgs(process.argv.slice(2)); + + if (!args.all && !args['changed-packages']) { + console.error('Specify --all or --changed-packages.'); + process.exit(1); + } + + if (args.all && args['changed-packages']) { + console.error('Use either --all or --changed-packages, not both.'); + process.exit(1); + } + + publishCallsPackages({ + changedPackages: args['changed-packages'], + dryRun: args['dry-run'], + skipBuild: args['skip-build'], + yes: args.yes, + provenance: args.provenance, + }).catch((error) => { + console.error(error.message); + process.exit(1); + }); +} From ecd2c46e657e8fd6ab2b315b8b618b132a93ad1e Mon Sep 17 00:00:00 2001 From: unknownproperty Date: Sat, 23 May 2026 01:03:50 +0300 Subject: [PATCH 5/8] feat: delete scripts, move to workflows --- .../generate-package-filters/action.yml | 20 -- .../actions/publish-calls-packages/action.yml | 14 -- .github/actions/setup-pnpm/action.yml | 40 ---- .github/filters.yml | 6 +- .github/package-filters.yml | 25 ++ .github/workflows/common-prepare.yml | 15 +- .github/workflows/front-production.yml | 88 +++++-- .gitignore | 1 - docs/migrations/calls-build.md | 13 +- package.json | 2 - scripts/calls-publish-lib.mjs | 41 ---- scripts/calls-publish-order.json | 13 -- scripts/generate-package-filters.mjs | 59 ----- scripts/publish-calls-packages.mjs | 221 ------------------ 14 files changed, 113 insertions(+), 445 deletions(-) delete mode 100644 .github/actions/generate-package-filters/action.yml delete mode 100644 .github/actions/publish-calls-packages/action.yml delete mode 100644 .github/actions/setup-pnpm/action.yml create mode 100644 .github/package-filters.yml delete mode 100644 scripts/calls-publish-lib.mjs delete mode 100644 scripts/calls-publish-order.json delete mode 100644 scripts/generate-package-filters.mjs delete mode 100644 scripts/publish-calls-packages.mjs diff --git a/.github/actions/generate-package-filters/action.yml b/.github/actions/generate-package-filters/action.yml deleted file mode 100644 index 280414a..0000000 --- a/.github/actions/generate-package-filters/action.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Generate package filters -description: Generate dorny/paths-filter config for publishable @xipkg/calls-* packages - -inputs: - output-file: - description: Path to write the generated filters YAML - required: false - default: .github/generated/package-filters.yml - -outputs: - output-file: - description: Path to the generated filters file - value: ${{ inputs.output-file }} - -runs: - using: composite - steps: - - name: Generate package filters - shell: bash - run: node scripts/generate-package-filters.mjs --output "${{ inputs.output-file }}" diff --git a/.github/actions/publish-calls-packages/action.yml b/.github/actions/publish-calls-packages/action.yml deleted file mode 100644 index 3db7fd4..0000000 --- a/.github/actions/publish-calls-packages/action.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Publish changed calls packages -description: Publish changed @xipkg/calls-* packages in dependency order (npm trusted publishing / OIDC) - -inputs: - changed-packages: - description: JSON array from dorny/paths-filter, e.g. ["calls.types","calls.hooks"] - required: true - -runs: - using: composite - steps: - - name: Publish changed packages - shell: bash - run: node scripts/publish-calls-packages.mjs --changed-packages '${{ inputs.changed-packages }}' --provenance diff --git a/.github/actions/setup-pnpm/action.yml b/.github/actions/setup-pnpm/action.yml deleted file mode 100644 index 9fac72d..0000000 --- a/.github/actions/setup-pnpm/action.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Setup pnpm -description: Install pnpm, Node.js, and workspace dependencies - -inputs: - node-version: - description: Node.js version - required: false - default: "22.22.1" - pnpm-version: - description: pnpm version - required: false - default: "8.15.4" - registry-url: - description: npm registry URL (optional, for publish jobs) - required: false - default: "" - install: - description: Whether to run pnpm install - required: false - default: "true" - -runs: - using: composite - steps: - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: ${{ inputs.pnpm-version }} - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ inputs.node-version }} - cache: pnpm - registry-url: ${{ inputs.registry-url }} - - - name: Install dependencies - if: inputs.install == 'true' - shell: bash - run: pnpm install --frozen-lockfile --ignore-scripts diff --git a/.github/filters.yml b/.github/filters.yml index 03e2a60..1c9ab81 100644 --- a/.github/filters.yml +++ b/.github/filters.yml @@ -10,13 +10,9 @@ configs: &configs # изменения тут обязательно запускают все пайплайны - ".github/workflows/*" - - ".github/actions/**" + - ".github/package-filters.yml" - "package.json" - ".npmrc" - - "scripts/calls-publish-order.json" - - "scripts/calls-publish-lib.mjs" - - "scripts/generate-package-filters.mjs" - - "scripts/publish-calls-packages.mjs" lintable: # запускает прогон линтера - *configs diff --git a/.github/package-filters.yml b/.github/package-filters.yml new file mode 100644 index 0000000..30a37ee --- /dev/null +++ b/.github/package-filters.yml @@ -0,0 +1,25 @@ +# Per-package path filters for selective npm publish (dorny/paths-filter). +# Update this file when adding a new packages/calls.* package. + +calls.types: + - packages/calls.types/** +calls.config: + - packages/calls.config/** +calls.utils: + - packages/calls.utils/** +calls.store: + - packages/calls.store/** +calls.providers: + - packages/calls.providers/** +calls.hooks: + - packages/calls.hooks/** +calls.ui: + - packages/calls.ui/** +calls.risehand: + - packages/calls.risehand/** +calls.chat: + - packages/calls.chat/** +calls.compactview: + - packages/calls.compactview/** +calls: + - packages/calls/** diff --git a/.github/workflows/common-prepare.yml b/.github/workflows/common-prepare.yml index 44c087f..d51e439 100644 --- a/.github/workflows/common-prepare.yml +++ b/.github/workflows/common-prepare.yml @@ -34,8 +34,19 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup pnpm - uses: ./.github/actions/setup-pnpm + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.22.1 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts - name: Check format run: pnpm run format-check diff --git a/.github/workflows/front-production.yml b/.github/workflows/front-production.yml index aef8978..c03cfd0 100644 --- a/.github/workflows/front-production.yml +++ b/.github/workflows/front-production.yml @@ -26,21 +26,52 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Generate package filters - id: generate_filters - uses: ./.github/actions/generate-package-filters - - name: Filter packages uses: dorny/paths-filter@v4 id: package_filter with: - filters: ${{ steps.generate_filters.outputs.output-file }} + filters: .github/package-filters.yml - deploy-packages: + build-packages: needs: analyze-packages if: needs.analyze-packages.outputs.packages != '[]' runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 8.15.4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.22.1 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile --ignore-scripts + + - name: Build calls packages + run: pnpm exec turbo run build --filter='./packages/calls...' + + - name: Upload built packages + uses: actions/upload-artifact@v4 + with: + name: calls-packages + path: packages/ + retention-days: 1 + + publish-packages: + needs: [analyze-packages, build-packages] + if: | + needs.analyze-packages.outputs.packages != '[]' && + contains(fromJSON(needs.analyze-packages.outputs.packages), matrix.package) + runs-on: ubuntu-latest + permissions: contents: read id-token: write @@ -53,28 +84,39 @@ jobs: || 'xi-production-manual' }} + strategy: + fail-fast: true + max-parallel: 1 + matrix: + package: + - calls.types + - calls.config + - calls.utils + - calls.store + - calls.providers + - calls.hooks + - calls.ui + - calls.risehand + - calls.chat + - calls.compactview + - calls + steps: - - name: Checkout repository - uses: actions/checkout@v4 + - name: Download built packages + uses: actions/download-artifact@v4 + with: + name: calls-packages + path: packages - - name: Setup pnpm - uses: ./.github/actions/setup-pnpm + - name: Setup Node.js + uses: actions/setup-node@v4 with: + node-version: 22.22.1 registry-url: https://registry.npmjs.org - name: Upgrade npm (required for trusted publishing) run: npm install -g npm@latest - - name: Verify Node/npm/pnpm versions - run: | - node -v - npm -v - pnpm -v - - - name: Build calls packages - run: pnpm exec turbo run build --filter='./packages/calls...' - - - name: Publish changed packages - uses: ./.github/actions/publish-calls-packages - with: - changed-packages: ${{ needs.analyze-packages.outputs.packages }} + - name: Publish package + working-directory: packages/${{ matrix.package }} + run: npm publish --access public --provenance diff --git a/.gitignore b/.gitignore index 04dae2f..e722be1 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,6 @@ yarn-error.log* # Misc .DS_Store *.pem -.github/generated/ /.idea/.gitignore /.idea/codeStyles/codeStyleConfig.xml /.idea/dbnavigator.xml diff --git a/docs/migrations/calls-build.md b/docs/migrations/calls-build.md index cdcd395..5e1a5eb 100644 --- a/docs/migrations/calls-build.md +++ b/docs/migrations/calls-build.md @@ -73,15 +73,20 @@ Trusted publishing: workflow `front-production.yml`, environment `xi-production` Перед publish нужно **поднять version** в `package.json` — npm вернёт `409`, если версия уже есть. -## Первый ручной publish +## Первый publish + +Обычный путь — merge в `main`: CI сам соберёт и опубликует пакеты с изменёнными версиями. + +Для разовой ручной проверки до merge: ```bash npm login -pnpm run publish:calls:dry-run # проверка без загрузки -pnpm run publish:calls # build + publish всех 11 пакетов +pnpm install --frozen-lockfile --ignore-scripts +pnpm exec turbo run build --filter='./packages/calls...' +cd packages/calls.types && npm publish --access public --dry-run ``` -Дополнительные флаги: `node scripts/publish-calls-packages.mjs --all --skip-build --yes`. +При добавлении нового `packages/calls.*` обновите `.github/package-filters.yml` и `matrix.package` в `front-production.yml`. ## Замечания diff --git a/package.json b/package.json index 1566ce8..f68f83d 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,6 @@ "lint": "turbo run lint", "format": "prettier --write \"**/*.{ts,tsx,md}\"", "format-check": "prettier --check \"**/*.{ts,tsx,md}\"", - "publish:calls": "node scripts/publish-calls-packages.mjs --all", - "publish:calls:dry-run": "node scripts/publish-calls-packages.mjs --all --dry-run --yes", "prepare": "husky" }, "devDependencies": { diff --git a/scripts/calls-publish-lib.mjs b/scripts/calls-publish-lib.mjs deleted file mode 100644 index 3b25f79..0000000 --- a/scripts/calls-publish-lib.mjs +++ /dev/null @@ -1,41 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const scriptsDir = path.dirname(fileURLToPath(import.meta.url)); - -export const CALLS_PUBLISH_ORDER = JSON.parse( - fs.readFileSync(path.join(scriptsDir, 'calls-publish-order.json'), 'utf8'), -); - -export function getRepoRoot(fromDir = scriptsDir) { - return path.resolve(fromDir, '..'); -} - -export function readPackageField(pkgDir, field) { - const pkgPath = path.join(pkgDir, 'package.json'); - const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); - - if (pkg[field] === undefined) { - throw new Error(`Missing "${field}" in ${pkgPath}`); - } - - return pkg[field]; -} - -export function assertDistBuilt(pkgDir, name) { - for (const file of ['dist/index.mjs', 'dist/index.d.ts']) { - const filePath = path.join(pkgDir, file); - - if (!fs.existsSync(filePath)) { - throw new Error(`${name}: ${file} not found. Run build first.`); - } - } -} - -export function listCallsPackageNames(packagesDir) { - return fs - .readdirSync(packagesDir) - .filter((name) => name === 'calls' || name.startsWith('calls.')) - .sort(); -} diff --git a/scripts/calls-publish-order.json b/scripts/calls-publish-order.json deleted file mode 100644 index e4a5b9a..0000000 --- a/scripts/calls-publish-order.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - "calls.types", - "calls.config", - "calls.utils", - "calls.store", - "calls.providers", - "calls.hooks", - "calls.ui", - "calls.risehand", - "calls.chat", - "calls.compactview", - "calls" -] diff --git a/scripts/generate-package-filters.mjs b/scripts/generate-package-filters.mjs deleted file mode 100644 index b13dd36..0000000 --- a/scripts/generate-package-filters.mjs +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env node - -import fs from 'node:fs'; -import path from 'node:path'; -import { parseArgs } from 'node:util'; -import { pathToFileURL } from 'node:url'; -import { getRepoRoot, listCallsPackageNames } from './calls-publish-lib.mjs'; - -export function generatePackageFilters({ rootDir, outputFile }) { - const packagesDir = path.join(rootDir, 'packages'); - const packageNames = listCallsPackageNames(packagesDir); - - const lines = ['# autogenerated packages data']; - - for (const name of packageNames) { - const key = name.replaceAll('/', '-'); - lines.push(`${key}:`, ` - packages/${name}/**`); - } - - fs.mkdirSync(path.dirname(outputFile), { recursive: true }); - fs.writeFileSync(outputFile, `${lines.join('\n')}\n`, 'utf8'); - - return outputFile; -} - -function parseCliArgs(argv) { - const { values } = parseArgs({ - args: argv, - options: { - output: { type: 'string', short: 'o' }, - help: { type: 'boolean', short: 'h', default: false }, - }, - allowPositionals: false, - }); - - if (values.help) { - console.log(`Usage: node scripts/generate-package-filters.mjs [--output ] - -Options: - --output, -o Output file path (default: .github/generated/package-filters.yml) - --help, -h Show this help -`); - process.exit(0); - } - - return values; -} - -if (import.meta.url === pathToFileURL(process.argv[1]).href) { - const rootDir = getRepoRoot(); - const args = parseCliArgs(process.argv.slice(2)); - const outputFile = path.resolve( - rootDir, - args.output ?? '.github/generated/package-filters.yml', - ); - - generatePackageFilters({ rootDir, outputFile }); - console.log(`saved filters to ${path.relative(rootDir, outputFile)}`); -} diff --git a/scripts/publish-calls-packages.mjs b/scripts/publish-calls-packages.mjs deleted file mode 100644 index 8ac4eab..0000000 --- a/scripts/publish-calls-packages.mjs +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/env node - -import readline from 'node:readline/promises'; -import { spawnSync } from 'node:child_process'; -import path from 'node:path'; -import { pathToFileURL } from 'node:url'; -import { stdin as input, stdout as output } from 'node:process'; -import { parseArgs } from 'node:util'; -import { - assertDistBuilt, - CALLS_PUBLISH_ORDER, - getRepoRoot, - readPackageField, -} from './calls-publish-lib.mjs'; - -function isCi() { - return process.env.GITHUB_ACTIONS === 'true'; -} - -function group(title, fn) { - if (isCi()) { - console.log(`::group::${title}`); - } else { - console.log(title); - } - - try { - return fn(); - } finally { - if (isCi()) { - console.log('::endgroup::'); - } - } -} - -function run(command, args, options = {}) { - const result = spawnSync(command, args, { - stdio: 'inherit', - ...options, - }); - - if (result.error) { - throw result.error; - } - - if (result.status !== 0) { - throw new Error(`${command} ${args.join(' ')} failed with exit code ${result.status}`); - } -} - -function parseChangedPackages(raw) { - const parsed = JSON.parse(raw); - - if (!Array.isArray(parsed)) { - throw new Error('changed-packages must be a JSON array'); - } - - return parsed; -} - -function resolvePackagesToPublish(changedPackages) { - if (changedPackages) { - return CALLS_PUBLISH_ORDER.filter((pkg) => changedPackages.includes(pkg)); - } - - return [...CALLS_PUBLISH_ORDER]; -} - -function publishPackage(pkgDir, { dryRun, provenance }) { - const args = ['publish', '--access', 'public']; - - if (dryRun) { - args.push('--dry-run'); - } - - if (provenance) { - args.push('--provenance'); - } - - run('npm', args, { cwd: pkgDir }); -} - -export async function publishCallsPackages(options) { - const rootDir = options.rootDir ?? getRepoRoot(); - const changedPackages = options.changedPackages - ? parseChangedPackages(options.changedPackages) - : null; - const packagesToPublish = resolvePackagesToPublish(changedPackages); - - if (packagesToPublish.length === 0) { - console.log('No packages to publish.'); - return 0; - } - - if (!changedPackages) { - run('npm', ['whoami']); - } - - if (!changedPackages && !options.skipBuild) { - run('pnpm', ['install', '--frozen-lockfile', '--ignore-scripts'], { cwd: rootDir }); - run('pnpm', ['exec', 'turbo', 'run', 'build', '--filter=./packages/calls...'], { - cwd: rootDir, - }); - } else if (!changedPackages && options.skipBuild) { - console.log('Skipping build (--skip-build)'); - } - - console.log('\nPackages to publish (in order):'); - for (const pkg of packagesToPublish) { - const pkgDir = path.join(rootDir, 'packages', pkg); - const name = readPackageField(pkgDir, 'name'); - const version = readPackageField(pkgDir, 'version'); - assertDistBuilt(pkgDir, name); - console.log(` - ${name}@${version}`); - } - - if (options.dryRun) { - console.log('\nDRY RUN — nothing will be uploaded to npm.'); - } - - if (!changedPackages && !options.yes) { - const rl = readline.createInterface({ input, output }); - const reply = await rl.question('\nContinue? [y/N] '); - rl.close(); - - if (!/^y$/i.test(reply.trim())) { - console.log('Aborted.'); - return 0; - } - } - - let published = 0; - - for (const pkg of packagesToPublish) { - const pkgDir = path.join(rootDir, 'packages', pkg); - const name = readPackageField(pkgDir, 'name'); - const version = readPackageField(pkgDir, 'version'); - - group(`Publish packages/${pkg}`, () => { - console.log(`Publishing ${name}@${version}`); - publishPackage(pkgDir, { - dryRun: options.dryRun, - provenance: options.provenance, - }); - published += 1; - console.log(`Published ${name}@${version}`); - }); - } - - if (options.dryRun) { - console.log(`\nDry run complete for ${published} package(s).`); - } else if (published > 0) { - console.log(`\nPublished ${published} package(s).`); - - if (!changedPackages) { - console.log( - 'Next: configure Trusted Publisher on npmjs.com for each package (workflow: front-production.yml).', - ); - } - } - - return published; -} - -function parseCliArgs(argv) { - const { values } = parseArgs({ - args: argv, - options: { - all: { type: 'boolean', default: false }, - 'changed-packages': { type: 'string' }, - 'dry-run': { type: 'boolean', default: false }, - 'skip-build': { type: 'boolean', default: false }, - yes: { type: 'boolean', default: false }, - provenance: { type: 'boolean', default: false }, - help: { type: 'boolean', short: 'h', default: false }, - }, - allowPositionals: false, - }); - - if (values.help) { - console.log(`Usage: node scripts/publish-calls-packages.mjs [options] - -Options: - --changed-packages Publish only changed packages (CI mode) - --all Publish all calls packages in dependency order - --dry-run Run npm publish --dry-run - --skip-build Skip install/build before publishing all packages - --yes Skip confirmation prompt - --provenance Pass --provenance to npm publish (CI trusted publishing) - --help, -h Show this help -`); - process.exit(0); - } - - return values; -} - -if (import.meta.url === pathToFileURL(process.argv[1]).href) { - const args = parseCliArgs(process.argv.slice(2)); - - if (!args.all && !args['changed-packages']) { - console.error('Specify --all or --changed-packages.'); - process.exit(1); - } - - if (args.all && args['changed-packages']) { - console.error('Use either --all or --changed-packages, not both.'); - process.exit(1); - } - - publishCallsPackages({ - changedPackages: args['changed-packages'], - dryRun: args['dry-run'], - skipBuild: args['skip-build'], - yes: args.yes, - provenance: args.provenance, - }).catch((error) => { - console.error(error.message); - process.exit(1); - }); -} From e754dd1a4a8149ec8094faaca756bc44d6cc83ba Mon Sep 17 00:00:00 2001 From: unknownproperty Date: Sat, 23 May 2026 01:18:22 +0300 Subject: [PATCH 6/8] feat: remove publish --- .github/workflows/front-production.yml | 118 +++++++++++++------------ docs/migrations/calls-build.md | 17 +--- 2 files changed, 65 insertions(+), 70 deletions(-) diff --git a/.github/workflows/front-production.yml b/.github/workflows/front-production.yml index c03cfd0..9fe6633 100644 --- a/.github/workflows/front-production.yml +++ b/.github/workflows/front-production.yml @@ -32,11 +32,23 @@ jobs: with: filters: .github/package-filters.yml - build-packages: + deploy-packages: needs: analyze-packages if: needs.analyze-packages.outputs.packages != '[]' runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + environment: >- + ${{ + contains(fromJSON('["niqzart", "unknownproperty"]'), github.triggering_actor) + && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + && 'xi-production' + || 'xi-production-manual' + }} + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -50,73 +62,69 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22.22.1 + registry-url: https://registry.npmjs.org cache: pnpm - name: Install dependencies run: pnpm install --frozen-lockfile --ignore-scripts + - name: Upgrade npm (required for trusted publishing) + run: npm install -g npm@latest + - name: Build calls packages run: pnpm exec turbo run build --filter='./packages/calls...' - - name: Upload built packages - uses: actions/upload-artifact@v4 - with: - name: calls-packages - path: packages/ - retention-days: 1 - - publish-packages: - needs: [analyze-packages, build-packages] - if: | - needs.analyze-packages.outputs.packages != '[]' && - contains(fromJSON(needs.analyze-packages.outputs.packages), matrix.package) - runs-on: ubuntu-latest + - name: Publish calls.types + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.types') + working-directory: packages/calls.types + run: npm publish --access public --provenance - permissions: - contents: read - id-token: write + - name: Publish calls.config + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.config') + working-directory: packages/calls.config + run: npm publish --access public --provenance - environment: >- - ${{ - contains(fromJSON('["niqzart", "unknownproperty"]'), github.triggering_actor) - && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) - && 'xi-production' - || 'xi-production-manual' - }} + - name: Publish calls.utils + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.utils') + working-directory: packages/calls.utils + run: npm publish --access public --provenance - strategy: - fail-fast: true - max-parallel: 1 - matrix: - package: - - calls.types - - calls.config - - calls.utils - - calls.store - - calls.providers - - calls.hooks - - calls.ui - - calls.risehand - - calls.chat - - calls.compactview - - calls + - name: Publish calls.store + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.store') + working-directory: packages/calls.store + run: npm publish --access public --provenance - steps: - - name: Download built packages - uses: actions/download-artifact@v4 - with: - name: calls-packages - path: packages + - name: Publish calls.providers + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.providers') + working-directory: packages/calls.providers + run: npm publish --access public --provenance - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.22.1 - registry-url: https://registry.npmjs.org + - name: Publish calls.hooks + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.hooks') + working-directory: packages/calls.hooks + run: npm publish --access public --provenance - - name: Upgrade npm (required for trusted publishing) - run: npm install -g npm@latest + - name: Publish calls.ui + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.ui') + working-directory: packages/calls.ui + run: npm publish --access public --provenance + + - name: Publish calls.risehand + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.risehand') + working-directory: packages/calls.risehand + run: npm publish --access public --provenance + + - name: Publish calls.chat + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.chat') + working-directory: packages/calls.chat + run: npm publish --access public --provenance + + - name: Publish calls.compactview + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls.compactview') + working-directory: packages/calls.compactview + run: npm publish --access public --provenance - - name: Publish package - working-directory: packages/${{ matrix.package }} + - name: Publish calls + if: contains(fromJSON(needs.analyze-packages.outputs.packages), 'calls') + working-directory: packages/calls run: npm publish --access public --provenance diff --git a/docs/migrations/calls-build.md b/docs/migrations/calls-build.md index 5e1a5eb..904e34b 100644 --- a/docs/migrations/calls-build.md +++ b/docs/migrations/calls-build.md @@ -56,7 +56,7 @@ pnpm --filter @xipkg/calls-hooks dev ## npm publish -Перед публикацией CI (или локально) запускает `pnpm run build`. В npm уходит только `dist/` (+ CSS для ui). +CI при merge в `main` сам запускает build и публикует изменённые пакеты. В npm уходит только `dist/` (+ CSS для ui). `publishConfig.access: public` уже прописан во всех calls-пакетах. @@ -73,20 +73,7 @@ Trusted publishing: workflow `front-production.yml`, environment `xi-production` Перед publish нужно **поднять version** в `package.json` — npm вернёт `409`, если версия уже есть. -## Первый publish - -Обычный путь — merge в `main`: CI сам соберёт и опубликует пакеты с изменёнными версиями. - -Для разовой ручной проверки до merge: - -```bash -npm login -pnpm install --frozen-lockfile --ignore-scripts -pnpm exec turbo run build --filter='./packages/calls...' -cd packages/calls.types && npm publish --access public --dry-run -``` - -При добавлении нового `packages/calls.*` обновите `.github/package-filters.yml` и `matrix.package` в `front-production.yml`. +При добавлении нового `packages/calls.*` обновите `.github/package-filters.yml` и шаг publish в `front-production.yml`. ## Замечания From ba96338781611be2327dd573620e0d7a6b80639d Mon Sep 17 00:00:00 2001 From: unknownproperty Date: Sat, 23 May 2026 01:42:44 +0300 Subject: [PATCH 7/8] feat: change case --- .github/workflows/front-production.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/front-production.yml b/.github/workflows/front-production.yml index 9fe6633..a26de48 100644 --- a/.github/workflows/front-production.yml +++ b/.github/workflows/front-production.yml @@ -42,12 +42,12 @@ jobs: id-token: write environment: >- - ${{ + ${{ case( contains(fromJSON('["niqzart", "unknownproperty"]'), github.triggering_actor) - && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) - && 'xi-production' - || 'xi-production-manual' - }} + && github.ref == format('refs/heads/{0}', github.event.repository.default_branch), + 'xi-production', + 'xi-production-manual' + ) }} steps: - name: Checkout repository From 67d8e79c882db8f9980d5895afd1db2632e5ec40 Mon Sep 17 00:00:00 2001 From: unknownproperty Date: Sun, 24 May 2026 21:24:49 +0300 Subject: [PATCH 8/8] fix(cicd): remove staging --- .github/workflows/front-development.yml | 1 - .github/workflows/front-staging.yml | 11 ----------- 2 files changed, 12 deletions(-) delete mode 100644 .github/workflows/front-staging.yml diff --git a/.github/workflows/front-development.yml b/.github/workflows/front-development.yml index 629e2ae..f662c56 100644 --- a/.github/workflows/front-development.yml +++ b/.github/workflows/front-development.yml @@ -4,7 +4,6 @@ on: push: branches-ignore: - main - - staging workflow_dispatch: jobs: diff --git a/.github/workflows/front-staging.yml b/.github/workflows/front-staging.yml deleted file mode 100644 index 3f63ee8..0000000 --- a/.github/workflows/front-staging.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: Staging - -on: - push: - branches: - - staging - workflow_dispatch: - -jobs: - prepare: - uses: ./.github/workflows/common-prepare.yml