KeiSeiKit-1.0/.github/workflows/release.yml
Denis Parfionovich fc0758d2bb
Some checks are pending
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / preflight (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / vps-smoke (push) Waiting to run
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Blocked by required conditions
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Blocked by required conditions
chore: версия 0.38.0 единая + warning-fixes + mold для release-job
1. Версии npm-пакетов приведены к 0.38.0 (был зоопарк 0.14.0/0.14.6):
   _ts_packages/{,packages/{gmail,grok,mcp-server,recall,telegram,youtube}-adapter}

2. Rust warnings (cargo check workspace):
   - kei-cortex: deprecated validate_path → validate_path_lexical,
     удалён orphan-wrapper в read.rs, struct Input → pub(crate)
   - frustration-matrix: #[allow(dead_code)] на confusion_* поля
     EvalReport + train_from_dir (будущий CLI)

3. CI release.yml job 'release' падал на Build kei-changelog:
   clang invalid linker '-fuse-ld=mold' — в .cargo/config.toml
   жёстко прописан mold для linux. Добавлен Install mold шаг
   (как уже сделано в build-release matrix).
2026-05-18 13:41:37 +08:00

464 lines
20 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
build-release:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
# v0.22.3 fix: aarch64-linux moved from ubuntu-latest + cross-linker
# install (apt gcc-aarch64-linux-gnu consistently failed in CI) to
# ubuntu-24.04-arm NATIVE ARM runner. No cross-compile, rustc builds
# the target host-native. `experimental: false` — native path is
# reliable.
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
experimental: false
- os: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
experimental: false
- os: macos-latest
target: x86_64-apple-darwin
experimental: false
- os: macos-latest
target: aarch64-apple-darwin
experimental: false
steps:
# v0.19.1 supply-chain hardening (H5): all actions pinned by full
# commit SHA; a floating tag like @v4 can be re-pointed by a
# compromised maintainer (CVE-2025-30066 class). Version comment next
# to each SHA is for human readability only — the SHA is load-bearing.
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable # exception to SHA-pin: named-branch convention (validator V-2026-04-22)
with:
targets: ${{ matrix.target }}
- name: Install mold linker (Linux only)
if: contains(matrix.target, 'linux')
run: |
sudo apt-get update
sudo apt-get install -y mold clang
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: _primitives/_rust
# v0.22.3: cross-linker step removed — aarch64-linux now builds
# natively on ubuntu-24.04-arm. No cross-compile, no gcc-aarch64-linux-gnu.
- name: Build workspace (release)
working-directory: _primitives/_rust
run: cargo build --workspace --release --target ${{ matrix.target }}
- name: Package binaries
id: package
working-directory: _primitives/_rust/target/${{ matrix.target }}/release
shell: bash
run: |
set -euo pipefail
# Collect every Cargo-built executable (Linux + macOS: no ext, mode +x).
# Portable across GNU + BSD find: iterate, test executability in shell.
BINS=()
for f in *; do
[ -f "$f" ] || continue
case "$f" in
*.d|*.rlib|*.rmeta|*.so|*.dylib|*.dSYM) continue ;;
esac
if [ -x "$f" ]; then
BINS+=("$f")
fi
done
if [ "${#BINS[@]}" -eq 0 ]; then
echo "::error::no release binaries produced for ${{ matrix.target }}"
exit 1
fi
echo "Binaries found: ${BINS[*]}"
ARCHIVE="keisei-${{ matrix.target }}.tar.gz"
tar czf "$GITHUB_WORKSPACE/$ARCHIVE" "${BINS[@]}"
cd "$GITHUB_WORKSPACE"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$ARCHIVE" > "$ARCHIVE.sha256"
else
shasum -a 256 "$ARCHIVE" > "$ARCHIVE.sha256"
fi
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: binaries-${{ matrix.target }}
path: |
keisei-${{ matrix.target }}.tar.gz
keisei-${{ matrix.target }}.tar.gz.sha256
if-no-files-found: error
# v0.18 Phase 1 (exobrain): compile @keisei/mcp-server to a single static
# binary for 5 platforms via `bun build --compile`. Runs in parallel with
# build-release; the release job below `needs:` both. Linux arm64 is kept
# `continue-on-error` because the ubuntu arm runner pool is newer and
# occasionally flaky — a missing linux-arm64 asset must NOT block release.
build-mcp-binary:
# v0.22.2 fix: `macos-13` Intel runners were deprecated by GitHub and the
# pool is dry — `darwin-x64` jobs sit in queued for hours and block the
# final `release` job (needs: build-mcp-binary). bun supports
# cross-compile to every target from any host, so we consolidate every
# bun build onto ubuntu-latest. Faster, no macOS quota cost, no runner
# starvation. Binaries are still native per-target (bun produces the
# correct Mach-O / ELF / PE format via --target).
name: Build mcp-server ${{ matrix.target.platform }}-${{ matrix.target.arch }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- { platform: linux, arch: x64, bun_target: bun-linux-x64, ext: '' }
- { platform: linux, arch: arm64, bun_target: bun-linux-arm64, ext: '' }
- { platform: darwin, arch: x64, bun_target: bun-darwin-x64, ext: '' }
- { platform: darwin, arch: arm64, bun_target: bun-darwin-arm64, ext: '' }
- { platform: windows, arch: x64, bun_target: bun-windows-x64, ext: '.exe' }
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Install bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: latest
# v0.19.1 supply-chain hardening (H4): lockfile is REQUIRED — the
# `|| bun install` fallback was removed so a missing bun.lock fails
# the build instead of resolving deps fresh against the live npm
# registry (tainted-binary window). bun.lock lives at workspace
# root (_ts_packages/bun.lock) — bun is a monorepo tool and tracks
# all packages/* from one lockfile. See BUILD.md §Lockfile.
- name: Install mcp-server deps
shell: bash
working-directory: _ts_packages
run: bun install --frozen-lockfile
- name: Compile single-binary
shell: bash
env:
BIN_NAME: kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}${{ matrix.target.ext }}
run: |
set -euo pipefail
mkdir -p dist
bun build \
--compile \
--target=${{ matrix.target.bun_target }} \
_ts_packages/packages/mcp-server/src/index.ts \
--outfile "dist/${BIN_NAME}"
ls -la "dist/${BIN_NAME}"
- name: Compute sha256
shell: bash
env:
BIN_NAME: kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}${{ matrix.target.ext }}
run: |
set -euo pipefail
cd dist
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "${BIN_NAME}" > "${BIN_NAME}.sha256"
else
shasum -a 256 "${BIN_NAME}" > "${BIN_NAME}.sha256"
fi
cat "${BIN_NAME}.sha256"
- name: Upload artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}
path: |
dist/kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}${{ matrix.target.ext }}
dist/kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}${{ matrix.target.ext }}.sha256
if-no-files-found: error
release:
name: Publish GitHub Release
needs: [build-release, build-mcp-binary]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable # exception to SHA-pin: named-branch convention (validator V-2026-04-22)
# _primitives/_rust/.cargo/config.toml жёстко прописывает `-fuse-ld=mold`
# для linux targets — без этой установки `cargo build` падает с
# `clang: error: invalid linker name in argument '-fuse-ld=mold'`.
# CI run 26014724470 fix.
- name: Install mold linker (для linux target в .cargo/config.toml)
run: |
sudo apt-get update
sudo apt-get install -y mold clang
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: _primitives/_rust
- name: Build kei-changelog
working-directory: _primitives/_rust
run: cargo build --release -p kei-changelog
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: dist/
- name: Flatten artifacts
run: |
set -euo pipefail
mkdir -p release-assets
# Rust tarballs + sha256 sums from build-release matrix.
# MCP-server bare binaries (+ .exe on windows) + sha256 sums from
# build-mcp-binary matrix. Bare binaries need a stable name to stay
# USB-drive-droppable, so no archive — we ship them raw alongside
# the tarballs.
find dist -type f \( \
-name '*.tar.gz' \
-o -name '*.sha256' \
-o -name 'kei-mcp-server-*' \
\) -exec mv {} release-assets/ \;
ls -la release-assets
- name: Generate release notes (kei-changelog)
id: notes
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
PREV="$(git tag --sort=-creatordate | grep -v "^${TAG}$" | head -n1 || true)"
echo "Current tag: ${TAG}"
echo "Previous tag: ${PREV:-<none>}"
if [ -n "${PREV}" ]; then
NOTES="$(./_primitives/_rust/target/release/kei-changelog \
--from "${PREV}" --to "${TAG}" --version "${TAG}")"
else
NOTES="$(./_primitives/_rust/target/release/kei-changelog \
--to "${TAG}" --version "${TAG}")"
fi
if [ -z "${NOTES}" ]; then
NOTES="Release ${TAG}. No conventional-commit entries found in range."
fi
{
echo 'notes<<KEISEI_NOTES_EOF'
echo "${NOTES}"
echo 'KEISEI_NOTES_EOF'
} >> "$GITHUB_OUTPUT"
# v0.22.3 fix: softprops/action-gh-release v2.6.2 exited with failure
# on v0.22.2 due to a metadata-update race (asset uploaded to blob
# store but Releases metadata API returned 404 on the subsequent
# PATCH — eventual-consistency window). All 15 assets WERE uploaded,
# but the action exited 1 and left the Release in Draft state.
#
# Replaced with `gh release create` (bundled on all GitHub runners).
# CLI is idempotent: if the release already exists it updates it; if
# assets already exist `--clobber` replaces them. No metadata-PATCH
# race. Retry loop on transient upload failures.
- name: Publish GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.ref_name }}
NOTES: ${{ steps.notes.outputs.notes }}
shell: bash
run: |
set -euo pipefail
# Create the release if missing; `|| true` absorbs "already exists"
# on workflow re-run.
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 || \
gh release create "$TAG" \
--repo "$GITHUB_REPOSITORY" \
--title "$TAG" \
--notes "$NOTES"
# Upload all assets with --clobber so re-runs replace cleanly.
# Retry each asset up to 3 times on transient network errors.
shopt -s nullglob
for f in release-assets/*.tar.gz release-assets/*.sha256 release-assets/kei-mcp-server-*; do
[ -f "$f" ] || continue
for try in 1 2 3; do
if gh release upload "$TAG" --repo "$GITHUB_REPOSITORY" --clobber "$f"; then
break
elif [ "$try" -eq 3 ]; then
echo "::error::failed to upload $f after 3 tries" >&2
exit 1
else
echo "upload of $f failed (attempt $try/3), retrying in 5s..." >&2
sleep 5
fi
done
done
echo "✓ Release $TAG published with all assets"
# ─────────────────────────────────────────────────────────────────────
# npm publish — две независимые job'ы.
#
# PRIMARY: keigit.com (наш приватный Forgejo). Активируется когда
# установлен secret KEIGIT_NPM_TOKEN. Forgejo требует
# Basic-auth (`Authorization: Basic base64(user:token)`),
# поэтому публикация через прямой curl PUT с manual payload —
# npm CLI не умеет Basic для Forgejo packages API.
#
# FUTURE: registry.npmjs.org. Активируется когда установлен secret
# NPM_TOKEN. Сейчас не подключено (secret не задан) — job
# gracefully скипается. Оставлен для будущего публичного
# хостинга когда захотим.
# ─────────────────────────────────────────────────────────────────────
npm-publish-keigit:
name: Publish to keigit.com (primary)
needs: release
runs-on: ubuntu-latest
steps:
- name: Check KEIGIT_NPM_TOKEN presence
id: have_token
env:
KEIGIT_NPM_TOKEN: ${{ secrets.KEIGIT_NPM_TOKEN }}
run: |
if [ -n "${KEIGIT_NPM_TOKEN:-}" ]; then
echo "present=1" >> "$GITHUB_OUTPUT"
else
echo "present=0" >> "$GITHUB_OUTPUT"
echo "::notice::KEIGIT_NPM_TOKEN not set — skipping keigit publish gracefully"
fi
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
if: steps.have_token.outputs.present == '1'
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
if: steps.have_token.outputs.present == '1'
with:
node-version: '20'
- name: Install deps
if: steps.have_token.outputs.present == '1'
working-directory: _ts_packages
run: npm ci
- name: Build workspaces
if: steps.have_token.outputs.present == '1'
working-directory: _ts_packages
run: npm run build --workspaces --if-present
- name: Publish each package via curl PUT
if: steps.have_token.outputs.present == '1'
working-directory: _ts_packages
env:
KEIGIT_NPM_TOKEN: ${{ secrets.KEIGIT_NPM_TOKEN }}
KEIGIT_NPM_USER: ${{ secrets.KEIGIT_NPM_USER }}
run: |
set -euo pipefail
: "${KEIGIT_NPM_USER:?KEIGIT_NPM_USER secret required (e.g. 'Parfionovich')}"
B64_AUTH=$(printf '%s' "${KEIGIT_NPM_USER}:${KEIGIT_NPM_TOKEN}" | base64 -w0)
for pkg in packages/*/; do
[ -f "$pkg/package.json" ] || continue
pkgname=$(jq -r '.name' "$pkg/package.json")
version=$(jq -r '.version' "$pkg/package.json")
short=$(echo "$pkgname" | cut -d/ -f2)
echo "::group::publish $pkgname@$version → keigit"
(
cd "$pkg"
npm pack >/dev/null
tarball="keisei-${short}-${version}.tgz"
[ -f "$tarball" ] || { echo "::warning::tarball $tarball missing"; exit 0; }
data=$(base64 -w0 "$tarball")
shasum=$(sha1sum "$tarball" | awk '{print $1}')
integrity="sha512-$(sha512sum "$tarball" | awk '{print $1}' | xxd -r -p | base64 -w0)"
size=$(stat -c '%s' "$tarball")
jq -n \
--arg name "$pkgname" --arg version "$version" \
--arg tarball "https://keigit.com/api/packages/keisei/npm/%40keisei%2F${short}/-/${version}/${short}-${version}.tgz" \
--arg shasum "$shasum" --arg integrity "$integrity" \
--arg data "$data" --argjson length "$size" \
--arg attach "${short}-${version}.tgz" --slurpfile pkg package.json \
'{ _id: $name, name: $name, "dist-tags": {latest: $version},
versions: { ($version): ($pkg[0] + {_id: ($name + "@" + $version), dist: {tarball: $tarball, shasum: $shasum, integrity: $integrity}}) },
_attachments: ({} | .[$attach] = { content_type:"application/octet-stream", data:$data, length:$length }) }' > payload.json
http=$(curl -sS -X PUT "https://keigit.com/api/packages/keisei/npm/@keisei%2F${short}" \
-H "Authorization: Basic ${B64_AUTH}" -H "Content-Type: application/json" \
--data-binary @payload.json -o resp.txt -w "%{http_code}")
if [ "$http" = "201" ]; then
echo "$pkgname@$version → keigit OK"
elif [ "$http" = "409" ] || grep -q "already exists" resp.txt 2>/dev/null; then
echo "::warning::$pkgname@$version already published (skipping)"
else
echo "::error::$pkgname@$version → HTTP $http"
cat resp.txt
exit 1
fi
rm -f "$tarball" payload.json resp.txt
)
echo "::endgroup::"
done
npm-publish-npmjs:
name: Publish to registry.npmjs.org (future, gracefully skipped)
needs: release
runs-on: ubuntu-latest
# FUTURE: добавит публичный хостинг через npmjs параллельно keigit.
# Сейчас secret NPM_TOKEN не установлен → job просто скипается.
# Когда захотим подключить — добавить secret NPM_TOKEN с
# https://www.npmjs.com/settings/<user>/tokens, scope=Automation.
steps:
- name: Check NPM_TOKEN presence
id: have_token
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
if [ -n "${NPM_TOKEN:-}" ]; then
echo "present=1" >> "$GITHUB_OUTPUT"
else
echo "present=0" >> "$GITHUB_OUTPUT"
echo "::notice::NPM_TOKEN not set — skipping npmjs publish gracefully (keigit publish is primary)"
fi
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
if: steps.have_token.outputs.present == '1'
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
if: steps.have_token.outputs.present == '1'
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Install deps
if: steps.have_token.outputs.present == '1'
working-directory: _ts_packages
run: npm ci
- name: Build workspaces
if: steps.have_token.outputs.present == '1'
working-directory: _ts_packages
run: npm run build --workspaces --if-present
- name: Publish each package via npm CLI (override registry)
if: steps.have_token.outputs.present == '1'
working-directory: _ts_packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
for pkg in packages/*/; do
if [ -f "$pkg/package.json" ]; then
echo "::group::publish $pkg → npmjs"
# --registry overrides publishConfig.registry (keigit) for this run.
( cd "$pkg" && npm publish --access public --registry=https://registry.npmjs.org ) \
|| echo "::warning::npmjs publish failed for $pkg (continuing)"
echo "::endgroup::"
fi
done