refactor(gitea): robust manifest-based publish pipeline

publish-outdated-packages.sh rewritten:
- Manifest-based change detection (no registry tarball downloads)
- Single pack per package (not double-pack for check+publish)
- Deterministic content hash: normalizes version, publishConfig,
  and @bytelyst/* dep versions (workspace:* resolution noise)
- Single metadata fetch per package (cached in-process)
- Fixed .npmrc overwrite bug that broke auth during publish phase
- npm_clean() helper strips all proxy env vars uniformly

release-packages.sh fixed:
- .npmrc now includes scoped registry + proxy=false for corp
- Unified corp/home publish path (no duplicated code)
- version_on_registry() uses proxy-stripped env
- Registry credential check uses proxy-stripped env

CI workflow: switched to publish-outdated-packages.sh --skip-build
This commit is contained in:
saravanakumardb1 2026-04-13 01:47:03 -07:00
parent 4e92634f62
commit deff216c7e
65 changed files with 627 additions and 323 deletions

View File

@ -71,5 +71,5 @@ jobs:
- name: Build all packages
run: pnpm -r --filter './packages/**' build
- name: Publish to local Gitea registry
run: bash ./scripts/gitea/publish-local-packages.sh
- name: Publish outdated packages to Gitea registry
run: bash ./scripts/gitea/publish-outdated-packages.sh --skip-build

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/accessibility",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/api-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/auth-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe auth API client for platform-service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/auth-ui",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Shared auth UI components for SmartAuth (LoginForm, MfaChallenge, SocialButtons)",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/auth",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/backend-config",
"version": "0.1.2",
"version": "0.1.5",
"description": "Shared Zod config schema base for Fastify product backends",
"type": "module",
"main": "dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/backend-flags",
"version": "0.1.2",
"version": "0.1.5",
"description": "In-memory feature flag registry for Fastify product backends",
"type": "module",
"main": "dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/backend-telemetry",
"version": "0.1.2",
"version": "0.1.5",
"description": "In-memory telemetry event buffer for Fastify product backends",
"type": "module",
"main": "dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/blob-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe blob storage client — SAS URL upload/download via platform-service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/blob",
"version": "0.2.2",
"version": "0.2.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/broadcast-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe broadcast messaging client for platform-service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/celebrations",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/client-encrypt",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/config",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/cosmos",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/create-app",
"version": "0.1.0",
"version": "0.1.3",
"description": "CLI tools for scaffolding ByteLyst product repos and code",
"type": "module",
"bin": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/dashboard-components",
"version": "0.1.2",
"version": "0.1.5",
"description": "Shared React components for ByteLyst dashboards",
"type": "module",
"main": "./dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/dashboard-shell",
"version": "0.1.2",
"version": "0.1.5",
"description": "Configurable Next.js dashboard layout with sidebar, profile, billing, and settings pages",
"type": "module",
"main": "./dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/datastore",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/design-tokens",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/diagnostics-client",
"version": "0.1.2",
"version": "0.1.5",
"description": "TypeScript client for remote diagnostics and debug tracing",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/errors",
"version": "0.1.3",
"version": "0.1.6",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/event-store",
"version": "0.1.2",
"version": "0.1.5",
"description": "Persistent event store with pluggable backends (in-memory, file, Cosmos) for ByteLyst product backends",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/events",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/extraction",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Shared types and client for the extraction service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/fastify-auth",
"version": "0.1.2",
"version": "0.1.5",
"description": "JWT auth middleware + request context for Fastify product backends",
"type": "module",
"main": "dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/fastify-core",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/fastify-sse",
"version": "0.3.2",
"version": "0.3.5",
"description": "Fastify plugin for Server-Sent Events (SSE) — real-time push for ByteLyst product backends",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/feature-flag-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe feature flag client for platform-service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/feedback-client",
"version": "0.1.2",
"version": "0.1.5",
"description": "TypeScript client for submitting user feedback with screenshots",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/field-encrypt",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/gentle-notifications",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/kill-switch-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe kill switch client for platform-service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/llm-router",
"version": "0.1.2",
"version": "0.1.5",
"description": "Pure-code LLM router for free-tier API providers with round-robin, fallback, and health tracking",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/llm",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/logger",
"version": "0.1.2",
"version": "0.1.5",
"description": "Structured logger factory for Next.js dashboards and Node.js services",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/marketplace-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/monitoring",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Health-check aggregation utilities for ByteLyst services and dashboards",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/offline-queue",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe persistent offline retry queue",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/ollama-client",
"version": "0.1.3",
"version": "0.1.6",
"description": "Shared Ollama API client — streaming chat, embeddings, model management, health checks",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/org-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/palace",
"version": "0.1.1",
"version": "0.1.4",
"description": "Shared MemPalace primitives — types, cosine similarity, dedup, relevance decay, extraction prompts, KG helpers, wake-up context builder",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/platform-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe typed fetch wrapper for platform-service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/push",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/queue",
"version": "0.1.2",
"version": "0.1.5",
"description": "Durable job queue with pluggable stores and worker runtime",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/quick-actions",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/react-auth",
"version": "0.1.3",
"version": "0.1.6",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/referral-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/secure-storage-web",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/speech",
"version": "0.1.2",
"version": "0.1.5",
"description": "Cloud-agnostic speech-to-text abstraction for the ByteLyst ecosystem",
"type": "module",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/storage",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/subscription-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/survey-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe survey client for platform-service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/sync",
"version": "0.1.2",
"version": "0.1.5",
"description": "Offline-first sync engine with configurable storage adapters and conflict resolution",
"type": "module",
"main": "./dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/telemetry-client",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"description": "Browser/React Native-safe telemetry client for platform-service",
"exports": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/testing",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/time-references",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/ui",
"version": "0.1.2",
"version": "0.1.5",
"type": "module",
"scripts": {
"storybook": "storybook dev -p 6006",

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/use-keyboard-shortcuts",
"version": "0.1.3",
"version": "0.1.6",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/use-theme",
"version": "0.1.3",
"version": "0.1.6",
"type": "module",
"exports": {
".": {

View File

@ -1,6 +1,6 @@
{
"name": "@bytelyst/webhook-dispatch",
"version": "0.1.2",
"version": "0.1.5",
"description": "Reusable webhook dispatch with HMAC-SHA256 signing, exponential backoff retry, and delivery tracking for ByteLyst product backends",
"type": "module",
"exports": {

View File

@ -0,0 +1,307 @@
{
"@bytelyst/accessibility": {
"version": "0.1.5",
"contentHash": "1298db3c803a8e338599967527b1af76d41e6ca60afb1566ef8346314845f094",
"publishedAt": "2026-04-13T08:44:02.743Z"
},
"@bytelyst/api-client": {
"version": "0.1.5",
"contentHash": "60958fa143ab6ff3aa6225779f162fe3a24744fb268f01bdff544b9a69ea0bdd",
"publishedAt": "2026-04-13T08:44:04.429Z"
},
"@bytelyst/auth": {
"version": "0.1.5",
"contentHash": "97f8e1ca0a609075e82751091a4a995740a5b06b513c14b55ee57ce6a9de7309",
"publishedAt": "2026-04-13T08:44:09.341Z"
},
"@bytelyst/auth-client": {
"version": "0.1.5",
"contentHash": "0155230525c853d8a18a6861078a5fecb2769962fd0f4102c759ead25c245296",
"publishedAt": "2026-04-13T08:44:06.085Z"
},
"@bytelyst/auth-ui": {
"version": "0.1.5",
"contentHash": "82bcd4ff0f8dea680c393b2f03470ce17bd1770baf3f9aca40dc1f18f2a82ad9",
"publishedAt": "2026-04-13T08:44:07.686Z"
},
"@bytelyst/backend-config": {
"version": "0.1.5",
"contentHash": "37808dd0b3280b730122ed0171811ac92dc7fd79e10a78f3c14daef911478149",
"publishedAt": "2026-04-13T08:44:11.057Z"
},
"@bytelyst/backend-flags": {
"version": "0.1.5",
"contentHash": "f79b89e172df28a296bd343883798bd761685599d27adfd94f88e09ef495df17",
"publishedAt": "2026-04-13T08:44:12.606Z"
},
"@bytelyst/backend-telemetry": {
"version": "0.1.5",
"contentHash": "34ead309860862742b7bafa60a85b95e5f1ccbd187cf7d849064e039ca1e336a",
"publishedAt": "2026-04-13T08:44:14.003Z"
},
"@bytelyst/blob": {
"version": "0.2.5",
"contentHash": "62b14ea355c625e102c533e9327f281b13c6031cfc3055fb4a6022d8d7512c5f",
"publishedAt": "2026-04-13T08:44:17.082Z"
},
"@bytelyst/blob-client": {
"version": "0.1.5",
"contentHash": "4b9e2a79ae68cba537026a861408afbd205f8d9da3f20bcab073d8f95881e6be",
"publishedAt": "2026-04-13T08:44:15.555Z"
},
"@bytelyst/broadcast-client": {
"version": "0.1.5",
"contentHash": "e819ffca743f96cdeb725134a1692378a2dbb0b40c843721290841e33fa731b7",
"publishedAt": "2026-04-13T08:44:18.467Z"
},
"@bytelyst/celebrations": {
"version": "0.1.5",
"contentHash": "84570ad46d99cada6b59512a81a5f1fbee2a74a982f9c8ffe1ca9577bc3e537b",
"publishedAt": "2026-04-13T08:44:20.012Z"
},
"@bytelyst/client-encrypt": {
"version": "0.1.5",
"contentHash": "4cfcfbe8b253dedaccfad21e680c43244449b60d441599a63031d8e27fa11ed1",
"publishedAt": "2026-04-13T08:44:21.557Z"
},
"@bytelyst/config": {
"version": "0.1.5",
"contentHash": "461d8646a67d0dfb4b27bbb62ee8751428d190c20ba9b55bea556806f55c0355",
"publishedAt": "2026-04-13T08:44:23.220Z"
},
"@bytelyst/cosmos": {
"version": "0.1.5",
"contentHash": "b0ddd5441f4b15ca28453b6e4cd089fcd92f7b504132d03c43a9b869c111c108",
"publishedAt": "2026-04-13T08:44:24.588Z"
},
"@bytelyst/create-app": {
"version": "0.1.3",
"contentHash": "e0439baca0c3850dc5714fcbb84b8b996549771740381fe5cf202a03b4e0c841",
"publishedAt": "2026-04-13T08:44:26.178Z"
},
"@bytelyst/dashboard-components": {
"version": "0.1.5",
"contentHash": "7d1ab88cb6fc6093e66c7be89c1cdc6ef7031bf20783b04d4f05bbb9d2e2dd5c",
"publishedAt": "2026-04-13T08:44:27.860Z"
},
"@bytelyst/dashboard-shell": {
"version": "0.1.5",
"contentHash": "e88743978d4d09a1a4047e5134469521ba8a9c0520ec04d0d3b7b5e4cb09f841",
"publishedAt": "2026-04-13T08:44:29.385Z"
},
"@bytelyst/datastore": {
"version": "0.1.5",
"contentHash": "482bef2452f47fe4ca553f3a1c67eb4dbd159ca9b791a274e713fbe0e64a7a2b",
"publishedAt": "2026-04-13T08:44:30.939Z"
},
"@bytelyst/design-tokens": {
"version": "0.1.5",
"contentHash": "46c2c7041807e52c965f08b8159b4cb17a4b752171f7d18cdb594f96d401792a",
"publishedAt": "2026-04-13T08:44:32.598Z"
},
"@bytelyst/diagnostics-client": {
"version": "0.1.5",
"contentHash": "c52ebcb6674e3cc468461741c86298f5923e80ca8fa4fbb08d55fdfd39517e7f",
"publishedAt": "2026-04-13T08:44:34.283Z"
},
"@bytelyst/errors": {
"version": "0.1.6",
"contentHash": "76443c39ff29478a483cb82439dcc44c5a25a1e87cf53eba3c1ba1e30b7947cf",
"publishedAt": "2026-04-13T08:44:35.843Z"
},
"@bytelyst/event-store": {
"version": "0.1.5",
"contentHash": "8ecf328682c6b886d0ee2db85094b7d2c6f589fe56d7666319b197383d8ca2ff",
"publishedAt": "2026-04-13T08:44:37.491Z"
},
"@bytelyst/events": {
"version": "0.1.5",
"contentHash": "ea584931f18cf682d3738545474ada10b67a775b7b549f4cc4862e145be8972f",
"publishedAt": "2026-04-13T08:44:39.134Z"
},
"@bytelyst/extraction": {
"version": "0.1.5",
"contentHash": "0edce9563443f5daea8d5efb2481fedaad9746f4ee1b7b9fb29e9095c0fe9305",
"publishedAt": "2026-04-13T08:44:40.758Z"
},
"@bytelyst/fastify-auth": {
"version": "0.1.5",
"contentHash": "d196ce76219aa420273d003781b83ecca11f11271dd2be9cfbe0c6b832d6d790",
"publishedAt": "2026-04-13T08:44:42.446Z"
},
"@bytelyst/fastify-core": {
"version": "0.1.5",
"contentHash": "0140953510ee4cf2f584c6f2745e33817832a7848f13e4a26b805038238cba77",
"publishedAt": "2026-04-13T08:44:44.114Z"
},
"@bytelyst/fastify-sse": {
"version": "0.3.5",
"contentHash": "1ef6fc1053ec6108af6faae1b6669fed4dc659660b6fbcc96474f7dff5c2d209",
"publishedAt": "2026-04-13T08:44:45.648Z"
},
"@bytelyst/feature-flag-client": {
"version": "0.1.5",
"contentHash": "81bbef1e411bdf9c38e59f82b6b7427b653aa7f36b304a32202332d260529ba3",
"publishedAt": "2026-04-13T08:44:47.385Z"
},
"@bytelyst/feedback-client": {
"version": "0.1.5",
"contentHash": "a9e11aa31ae7d576b0502fd6aefdfc93028863ae3e076e39c0accb4d2f78e524",
"publishedAt": "2026-04-13T08:44:49.075Z"
},
"@bytelyst/field-encrypt": {
"version": "0.1.5",
"contentHash": "3d8a01f2421fa077bf0f149b529f1841ec0f54a84b290b8535ca498e9c714c76",
"publishedAt": "2026-04-13T08:44:50.633Z"
},
"@bytelyst/gentle-notifications": {
"version": "0.1.5",
"contentHash": "00e49456bba81458c7d0129880207cc4e631ccd1037770ecddbfa9f7fbde8387",
"publishedAt": "2026-04-13T08:44:51.924Z"
},
"@bytelyst/kill-switch-client": {
"version": "0.1.5",
"contentHash": "0a6a46bcac5eb8d5723dd44458f533b8ef203b796939fb66cf66f2e6223c07fd",
"publishedAt": "2026-04-13T08:44:53.517Z"
},
"@bytelyst/llm": {
"version": "0.1.5",
"contentHash": "7985c82f98990adf7aa70aa55676c1fdd08e2100644d1b42a786758f7e0bb89c",
"publishedAt": "2026-04-13T08:44:56.327Z"
},
"@bytelyst/llm-router": {
"version": "0.1.5",
"contentHash": "5cc32aec6c0dd8bff4b4206ff55937663ba2995926d498510e49747a3fc12183",
"publishedAt": "2026-04-13T08:44:54.935Z"
},
"@bytelyst/logger": {
"version": "0.1.5",
"contentHash": "3ad702d6f0731621499916049049cf8de38a53e4c005095a2a14ca8b0aff1bbd",
"publishedAt": "2026-04-13T08:44:57.723Z"
},
"@bytelyst/marketplace-client": {
"version": "0.1.5",
"contentHash": "f44881256419432284b40dbde7d677a109579d94a3b9df9f3b9f40641ee30085",
"publishedAt": "2026-04-13T08:44:59.204Z"
},
"@bytelyst/monitoring": {
"version": "0.1.5",
"contentHash": "08840748c02e326e7cd2c410d80c17c1fcaeb5c48c57f3bbc5a5069926e660da",
"publishedAt": "2026-04-13T08:45:00.504Z"
},
"@bytelyst/offline-queue": {
"version": "0.1.5",
"contentHash": "1c25e355ec6a6f7c7e74e77b8c53aca94f0342eda049d535e31b56c6632d5101",
"publishedAt": "2026-04-13T08:45:02.179Z"
},
"@bytelyst/ollama-client": {
"version": "0.1.6",
"contentHash": "fa08340465aba0e866f8c5bb2839425ce46c694a5ad0839283fdbb8037a39121",
"publishedAt": "2026-04-13T08:45:03.855Z"
},
"@bytelyst/org-client": {
"version": "0.1.5",
"contentHash": "e0b8eb1bce5047e9bfcf11db8f77d8495f403cd08f74bb3d4a0dc37183a9ef29",
"publishedAt": "2026-04-13T08:45:05.581Z"
},
"@bytelyst/palace": {
"version": "0.1.4",
"contentHash": "8e47ae7ed300b1edd2f2058b5c3358f2d917d7b91ab4218357ad7cc3d20ed085",
"publishedAt": "2026-04-13T08:45:07.168Z"
},
"@bytelyst/platform-client": {
"version": "0.1.5",
"contentHash": "a5d90ea537a55ea295d5c9933f4ccab2b152d57666db623f7e6bcc11f403025a",
"publishedAt": "2026-04-13T08:45:08.836Z"
},
"@bytelyst/push": {
"version": "0.1.5",
"contentHash": "8fea33b73c0e4dc758a77b0dc80f49a1ea3a15b51a216dbc875b3f56c260685a",
"publishedAt": "2026-04-13T08:45:10.487Z"
},
"@bytelyst/queue": {
"version": "0.1.5",
"contentHash": "f3ea3cdf536c117e04f393bf1bfc5eda24117973aad3be15e687513b444f6bdb",
"publishedAt": "2026-04-13T08:45:12.106Z"
},
"@bytelyst/quick-actions": {
"version": "0.1.5",
"contentHash": "c1daf30dec2eda0bbec55a73e8c0cdb98d7c3c43dbcb5791bb9ef6097e3f4599",
"publishedAt": "2026-04-13T08:45:13.687Z"
},
"@bytelyst/react-auth": {
"version": "0.1.6",
"contentHash": "c4e4e86ed03b93ea6d1cb40e1a67315685cdb2a4e262858dfd29850a18a16a72",
"publishedAt": "2026-04-13T08:45:15.275Z"
},
"@bytelyst/referral-client": {
"version": "0.1.5",
"contentHash": "d519fab34d2e41c54951f02e38e1ce7f513aa4f4610f52947737bafb2b52dfae",
"publishedAt": "2026-04-13T08:45:16.758Z"
},
"@bytelyst/secure-storage-web": {
"version": "0.1.5",
"contentHash": "339d7122c0ec40bb1e84e99f74468b18cffde7600217fc7b0f91c89cf323f68c",
"publishedAt": "2026-04-13T08:45:18.264Z"
},
"@bytelyst/speech": {
"version": "0.1.5",
"contentHash": "5dc93cd396b7a61d24ad28d5e27d9873cea9d9dd086b2ba51c644863a5134938",
"publishedAt": "2026-04-13T08:45:19.842Z"
},
"@bytelyst/storage": {
"version": "0.1.5",
"contentHash": "65fb1ec119905bd862fb5ba1cc92cf88098067e162d4e8e058c565af9b7fae7b",
"publishedAt": "2026-04-13T08:45:21.411Z"
},
"@bytelyst/subscription-client": {
"version": "0.1.5",
"contentHash": "881ff18db9b61f715b0b995720d5defdb2ad5d7b44e17aa0f2bfd4e8ad6729fa",
"publishedAt": "2026-04-13T08:45:23.046Z"
},
"@bytelyst/survey-client": {
"version": "0.1.5",
"contentHash": "7d31d407924274dc4b1e4f6a06fce2b79b19f18e11270aa7a505a9bcefc2f8f9",
"publishedAt": "2026-04-13T08:45:24.602Z"
},
"@bytelyst/sync": {
"version": "0.1.5",
"contentHash": "7bcc6c6e7df33f966a5c4ee240b31dba8a286dbcfdebaad3dfdcefdb0fccfc2c",
"publishedAt": "2026-04-13T08:45:26.173Z"
},
"@bytelyst/telemetry-client": {
"version": "0.1.5",
"contentHash": "014acc903c05375e8decffcda698ba33a7993aed35916f48210546e8f4058138",
"publishedAt": "2026-04-13T08:45:27.699Z"
},
"@bytelyst/testing": {
"version": "0.1.5",
"contentHash": "478bdf04d9b2c54f8778d9a6987ba63b6f4f728bb2c7fc8b138cf8d3e5035c20",
"publishedAt": "2026-04-13T08:45:29.298Z"
},
"@bytelyst/time-references": {
"version": "0.1.5",
"contentHash": "70c04eeb491cf24ccb7cded95c4f213aff492c0f6b3b4d8be1af6395a266db59",
"publishedAt": "2026-04-13T08:45:30.916Z"
},
"@bytelyst/ui": {
"version": "0.1.5",
"contentHash": "3ed0e0c895a9fc86a016330b82f13df2c2ced4957e144a1a64c7d8c6e28e2282",
"publishedAt": "2026-04-13T08:45:32.412Z"
},
"@bytelyst/use-keyboard-shortcuts": {
"version": "0.1.6",
"contentHash": "04b917d4913f66247bfcfea53a871fac128e4b8d20bcdff6c2374e7f25aa8eee",
"publishedAt": "2026-04-13T08:45:33.972Z"
},
"@bytelyst/use-theme": {
"version": "0.1.6",
"contentHash": "1767e51d2352224ee2ef36d40e6261afcf70bb643b984d0378874a35a458cc26",
"publishedAt": "2026-04-13T08:45:35.655Z"
},
"@bytelyst/webhook-dispatch": {
"version": "0.1.5",
"contentHash": "e7afbed1158c657b1737716554cfc39f8d1263f03ee8c9774865e95622fadf8d",
"publishedAt": "2026-04-13T08:45:37.234Z"
}
}

View File

@ -2,25 +2,33 @@
set -euo pipefail
# ─────────────────────────────────────────────────────────────
# Publish only OUTDATED @bytelyst/* packages to Gitea registry
# Publish OUTDATED @bytelyst/* packages to the Gitea npm registry.
#
# Compares local built content against what's in the registry.
# Only publishes packages where the content has actually changed.
# Uses a local manifest (.publish-manifest.json) to track content hashes
# of the last-published version. Only packages whose built content has
# actually changed since the last publish are bumped and republished.
#
# Improvements over the previous version:
# - Manifest-based comparison (no registry tarball downloads)
# - Single pack per package (not double-pack)
# - Single metadata fetch per package (cached in-process)
# - No false-positive "OUTDATED" from pnpm-vs-npm pack differences
# - No .npmrc overwrite bug
#
# Usage:
# ./scripts/gitea/publish-outdated-packages.sh # detect + publish
# ./scripts/gitea/publish-outdated-packages.sh --dry-run # detect only
# ./scripts/gitea/publish-outdated-packages.sh --filter @bytelyst/errors
# scripts/gitea/publish-outdated-packages.sh # build + detect + publish
# scripts/gitea/publish-outdated-packages.sh --dry-run # build + detect only
# scripts/gitea/publish-outdated-packages.sh --skip-build # skip pnpm build
# scripts/gitea/publish-outdated-packages.sh --filter @bytelyst/errors
#
# Requires: GITEA_NPM_TOKEN env var (or ~/.gitea_npm_token)
# Requires: GITEA_NPM_TOKEN (env var or ~/.gitea_npm_token)
# ─────────────────────────────────────────────────────────────
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
PACKAGES_DIR="$REPO_ROOT/packages"
MANIFEST_FILE="$REPO_ROOT/scripts/gitea/.publish-manifest.json"
# ── Network-aware Gitea resolution ─────────────────────────
# NETWORK=corp -> localhost:3300 (SSH tunnel to Azure VM)
# NETWORK=home -> Azure VM directly (gitea.bytelyst.com or ~/.gitea_vm_host)
NETWORK_MODE="${NETWORK:-home}"
if [ "$NETWORK_MODE" = "corp" ]; then
@ -29,7 +37,6 @@ if [ "$NETWORK_MODE" = "corp" ]; then
GITEA_BASE="http://${GITEA_HOST}:${GITEA_PORT}"
IS_CORP=true
else
# Home network: use Azure VM host from ~/.gitea_vm_host or GITEA_NPM_HOST
if [ -n "${GITEA_NPM_HOST:-}" ] && [ "${GITEA_NPM_HOST}" != "localhost" ]; then
GITEA_HOST="$GITEA_NPM_HOST"
elif [ -f "$HOME/.gitea_vm_host" ]; then
@ -44,7 +51,7 @@ fi
REGISTRY_URL="${GITEA_NPM_REGISTRY_URL:-${GITEA_BASE}/api/packages/ByteLyst/npm/}"
TOKEN="${GITEA_NPM_TOKEN:-}"
WORK_DIR="${TMPDIR:-/tmp}/bytelyst-outdated-check-$$"
WORK_DIR="${TMPDIR:-/tmp}/bytelyst-publish-$$"
DRY_RUN=false
PACKAGE_FILTER=""
SKIP_BUILD=false
@ -67,35 +74,32 @@ while [[ $# -gt 0 ]]; do
esac
done
# Resolve token from file if not in env
# Resolve token
if [ -z "$TOKEN" ] && [ -f "$HOME/.gitea_npm_token" ]; then
TOKEN="$(cat "$HOME/.gitea_npm_token")"
fi
if [ -z "$TOKEN" ]; then
echo "ERROR: GITEA_NPM_TOKEN is required (env var or ~/.gitea_npm_token)"
exit 1
fi
# Auth target for npm publish
# Auth target for .npmrc
AUTH_TARGET="${REGISTRY_URL#http://}"
AUTH_TARGET="${AUTH_TARGET#https://}"
# Skip non-npm packages (native SDKs)
# Non-npm packages to skip
SKIP_DIRS="swift-platform-sdk swift-diagnostics kotlin-platform-sdk react-native-platform-sdk"
# Cleanup on exit
# Cleanup temp dir on exit
trap 'rm -rf "$WORK_DIR"' EXIT
mkdir -p "$WORK_DIR"
# Write .npmrc with auth token + scoped registry so npm publish bypasses publishConfig
# Single .npmrc for all npm operations (auth + scoped registry + proxy override)
NPMRC_FILE="$WORK_DIR/.npmrc"
{
printf '//%s:_authToken=%s\n' "$AUTH_TARGET" "$TOKEN"
# Override publishConfig.registry in package.json (npm uses scoped registry first)
printf '@bytelyst:registry=%s\n' "$REGISTRY_URL"
if [ "$IS_CORP" = true ]; then
# Disable proxy for localhost (global ~/.npmrc has corp proxy)
printf 'proxy=false\nhttps-proxy=false\n'
fi
} > "$NPMRC_FILE"
@ -103,184 +107,142 @@ NPMRC_FILE="$WORK_DIR/.npmrc"
# ── Helpers ────────────────────────────────────────────────
pkg_field() {
node -e "process.stdout.write(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).$1||'')" "$2"
node -e "process.stdout.write(String(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'))['$1']||''))" "$2"
}
# Compute a content fingerprint of all files under a directory.
# Sorts file paths, hashes each file, then hashes the combined result.
# This is metadata-independent (ignores tar headers, timestamps, etc.)
content_fingerprint() {
local dir="$1"
find "$dir" -type f -print0 | sort -z | xargs -0 shasum -a 256 | shasum -a 256 | cut -d' ' -f1
}
# Compute a deterministic content hash from an extracted tarball.
# Normalizes package.json (version→0.0.0, strips publishConfig) so that
# version bumps alone don't trigger re-publish.
# Uses relative paths and stdin hashing to ensure determinism across runs.
content_hash() {
local extracted_dir="$1" # contains package/ subdirectory
local pkg_json="$extracted_dir/package/package.json"
local norm; norm=$(mktemp)
# Check if a package version exists in the registry.
# Gitea npm API: GET /{encoded_name} returns all versions.
registry_version_exists() {
local name="$1" version="$2"
local encoded_name="${name/@/%40}"
encoded_name="${encoded_name//\//%2F}"
local meta
meta=$(curl -s -H "Authorization: token $TOKEN" \
"${REGISTRY_URL}${encoded_name}" 2>/dev/null) || return 1
echo "$meta" | node -e "
let d='';process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
try{const p=JSON.parse(d);process.exit(p.versions&&p.versions['$version']?0:1)}
catch(e){process.exit(1)}
})" 2>/dev/null
}
# Download a package tarball from the registry.
# Gitea npm API: GET /{encoded_name} → versions.{version}.dist.tarball
download_registry_tarball() {
local name="$1" version="$2" dest_dir="$3"
local encoded_name="${name/@/%40}"
encoded_name="${encoded_name//\//%2F}"
# Fetch the full package metadata (all versions)
local meta
meta=$(curl -s -H "Authorization: token $TOKEN" \
"${REGISTRY_URL}${encoded_name}" 2>/dev/null) || return 1
local tarball_url
tarball_url=$(echo "$meta" | node -e "
let d='';process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
try{
const p=JSON.parse(d);
const v=p.versions&&p.versions['$version'];
if(!v||!v.dist||!v.dist.tarball){process.exit(1)}
process.stdout.write(v.dist.tarball)
}catch(e){process.exit(1)}
})" 2>/dev/null) || return 1
if [ -z "$tarball_url" ]; then
return 1
fi
curl -s -H "Authorization: token $TOKEN" \
-o "$dest_dir/registry.tgz" \
"$tarball_url" 2>/dev/null || return 1
[ -f "$dest_dir/registry.tgz" ] && [ -s "$dest_dir/registry.tgz" ]
}
# Bump the patch version of a package.json (e.g., 0.1.0 → 0.1.1, 0.1.1 → 0.1.2).
# Also finds the next available version not yet in the registry.
bump_patch_version() {
local pkg_json="$1" pkg_name="$2"
local encoded_name="${pkg_name/@/%40}"
encoded_name="${encoded_name//\//%2F}"
# Get all published versions from registry
local published_versions
published_versions=$(curl -s -H "Authorization: token $TOKEN" \
"${REGISTRY_URL}${encoded_name}" 2>/dev/null | node -e "
let d='';process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
try{const p=JSON.parse(d);process.stdout.write(Object.keys(p.versions||{}).join(','))}
catch(e){process.stdout.write('')}
})" 2>/dev/null) || true
# Compute next available patch version
node -e "
const fs=require('fs');
const pkg=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));
const published=new Set((process.argv[2]||'').split(',').filter(Boolean));
const parts=pkg.version.split('.').map(Number);
// Start from current patch+1 and find first unpublished
parts[2]++;
while(published.has(parts.join('.'))){parts[2]++}
pkg.version=parts.join('.');
fs.writeFileSync(process.argv[1],JSON.stringify(pkg,null,2)+'\n');
process.stdout.write(pkg.version);
" "$pkg_json" "$published_versions"
const p=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));
p.version='0.0.0'; delete p.publishConfig;
// Normalize @bytelyst/* dep versions (pnpm resolves workspace:* to exact versions
// which change on every bump — these shouldn't trigger re-publish)
for(const s of ['dependencies','devDependencies','peerDependencies']){
if(!p[s])continue;
for(const [n]of Object.entries(p[s])){
if(n.startsWith('@bytelyst/'))p[s][n]='*';
}
}
fs.writeFileSync(process.argv[2],JSON.stringify(p));
" "$pkg_json" "$norm"
# Hash normalized package.json (via stdin — no path in output)
# + all other files using relative paths (cd into package/ first)
{
echo "PKG $(shasum -a 256 < "$norm" | cut -d' ' -f1)"
(cd "$extracted_dir/package" && find . -type f ! -name package.json -print0 \
| sort -z | xargs -0 shasum -a 256 2>/dev/null) || true
} | shasum -a 256 | cut -d' ' -f1
rm -f "$norm"
}
# Publish a package using the same double-pack pattern as publish-local-packages.sh.
publish_package() {
local pkg_dir="$1"
local pkg_name pkg_version safe_name
pkg_name="$(pkg_field name "$pkg_dir/package.json")"
pkg_version="$(pkg_field version "$pkg_dir/package.json")"
safe_name="${pkg_name//@/}"
safe_name="${safe_name//\//-}"
# ── Manifest Operations ───────────────────────────────────
local pub_dir="$WORK_DIR/publish-$safe_name"
rm -rf "$pub_dir"
mkdir -p "$pub_dir"
# Step 1: pnpm pack
(cd "$pkg_dir" && pnpm pack --pack-destination "$pub_dir" >/dev/null 2>&1)
local packed_tgz
packed_tgz="$(find "$pub_dir" -maxdepth 1 -name '*.tgz' | head -1)"
if [ -z "$packed_tgz" ]; then
echo " ERROR: pnpm pack failed for $pkg_name"
return 1
fi
# Step 2: extract, strip publishConfig (so --registry wins), then npm repack
mkdir -p "$pub_dir/unpacked"
tar -xzf "$packed_tgz" -C "$pub_dir/unpacked"
rm -f "$packed_tgz" # remove pnpm tgz so only npm-repacked tgz remains
manifest_get_hash() {
local name="$1"
[ -f "$MANIFEST_FILE" ] || { echo ""; return; }
node -e "
const fs=require('fs'),f=process.argv[1];
const p=JSON.parse(fs.readFileSync(f,'utf8'));
delete p.publishConfig;
fs.writeFileSync(f,JSON.stringify(p,null,2)+'\n');
" "$pub_dir/unpacked/package/package.json"
(cd "$pub_dir/unpacked/package" && npm pack --pack-destination "$pub_dir" >/dev/null 2>&1)
try{const m=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));
process.stdout.write(m[process.argv[2]]?.contentHash||'')}
catch(e){process.stdout.write('')}
" "$MANIFEST_FILE" "$name"
}
local final_tgz
final_tgz="$(find "$pub_dir" -maxdepth 1 -name '*.tgz' | head -1)"
if [ -z "$final_tgz" ]; then
echo " ERROR: npm repack failed for $pkg_name"
return 1
fi
manifest_set() {
local name="$1" version="$2" hash="$3"
node -e "
const fs=require('fs');
let m={};try{m=JSON.parse(fs.readFileSync(process.argv[1],'utf8'))}catch(e){}
m[process.argv[2]]={version:process.argv[3],contentHash:process.argv[4],
publishedAt:new Date().toISOString()};
const s=Object.fromEntries(Object.entries(m).sort(([a],[b])=>a.localeCompare(b)));
fs.writeFileSync(process.argv[1],JSON.stringify(s,null,2)+'\n');
" "$MANIFEST_FILE" "$name" "$version" "$hash"
}
# Step 3: publish to Gitea registry.
# Run from WORK_DIR (in /tmp with .npmrc for auth) so npm won't find
# the repo's .npmrc which has @bytelyst:registry pointing externally.
# Publish using shared .npmrc (has auth, scoped registry, and proxy=false on corp)
# Strip all proxy/registry env vars so only .npmrc settings apply
if ! (cd "$WORK_DIR" && env \
# ── Registry Helpers (single fetch, cached) ───────────────
# Cache file: one line per package = "name:ver1,ver2,ver3"
REGISTRY_CACHE_FILE="$WORK_DIR/.registry-cache"
: > "$REGISTRY_CACHE_FILE"
registry_versions() {
local name="$1"
# Check cache first
local cached
cached=$(grep "^${name}:" "$REGISTRY_CACHE_FILE" 2>/dev/null | cut -d: -f2-) || true
if [ -n "$cached" ]; then echo "$cached"; return; fi
local encoded="${name/@/%40}"; encoded="${encoded//\//%2F}"
local versions
versions=$(curl -s -H "Authorization: token $TOKEN" \
"${REGISTRY_URL}${encoded}" 2>/dev/null \
| node -e "let d='';process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{try{
process.stdout.write(Object.keys(JSON.parse(d).versions||{}).join(','))
}catch(e){process.stdout.write('')}})" 2>/dev/null) || true
echo "${name}:${versions}" >> "$REGISTRY_CACHE_FILE"
echo "$versions"
}
next_version() {
local name="$1" current="$2"
local versions; versions=$(registry_versions "$name")
node -e "
const pub=new Set((process.argv[1]||'').split(',').filter(Boolean));
const p=process.argv[2].split('.').map(Number);
p[2]++;while(pub.has(p.join('.'))){p[2]++}
process.stdout.write(p.join('.'));
" "$versions" "$current"
}
version_exists() {
local name="$1" version="$2"
local versions; versions=$(registry_versions "$name")
echo ",$versions," | grep -q ",$version,"
}
# Run npm commands with proxy/env stripping for reliable localhost access
npm_clean() {
(cd "$WORK_DIR" && env \
-u http_proxy -u https_proxy -u HTTP_PROXY -u HTTPS_PROXY \
-u npm_config_proxy -u npm_config_https_proxy \
-u NPM_CONFIG_PROXY -u NPM_CONFIG_HTTPS_PROXY \
-u NPM_CONFIG_REGISTRY -u NPM_CONFIG_STRICT_SSL \
-u NPM_CONFIG_NOPROXY \
-u NODE_TLS_REJECT_UNAUTHORIZED \
npm publish "$final_tgz" \
--registry "$REGISTRY_URL" \
--userconfig "$NPMRC_FILE" 2>&1); then
echo " ERROR: publish failed for $pkg_name@$pkg_version"
return 1
fi
-u NPM_CONFIG_NOPROXY -u NODE_TLS_REJECT_UNAUTHORIZED \
"$@")
}
# ── Main ───────────────────────────────────────────────────
# Show resolved config
echo "==> Network: $NETWORK_MODE ($( [ "$IS_CORP" = true ] && echo "corp — localhost tunnel" || echo "home — Azure VM" ))"
echo " Registry: $REGISTRY_URL"
echo ""
# Step 1: Build all packages
# Phase 1: Build
if [ "$SKIP_BUILD" = false ]; then
echo "==> Building all packages..."
(cd "$REPO_ROOT" && pnpm build 2>&1 | tail -5)
echo ""
fi
# Step 2: Check each package
echo "==> Checking packages against registry..."
# Phase 2: Check each package against manifest
echo "==> Checking packages (manifest-based)..."
echo ""
# Arrays for tracking results
outdated_dirs=()
outdated_names=()
outdated_hashes=() # content hash per outdated package (for manifest update after publish)
up_to_date=0
not_found=0
changed=0
skipped=0
errors=0
@ -291,76 +253,66 @@ for pkg_json in "$PACKAGES_DIR"/*/package.json; do
# Skip native SDKs
if echo "$SKIP_DIRS" | grep -qw "$dir_name"; then
((skipped++))
continue
((skipped++)); continue
fi
pkg_name="$(pkg_field name "$pkg_json")"
pkg_version="$(pkg_field version "$pkg_json")"
# Skip private packages
private_flag="$(pkg_field private "$pkg_json")"
if [ "$private_flag" = "true" ]; then
((skipped++)); continue
fi
# Apply filter
if [ -n "$PACKAGE_FILTER" ] && [ "$pkg_name" != "$PACKAGE_FILTER" ]; then
continue
fi
# Check if dist/ exists (package must be built)
# Must have built output
if [ ! -d "$pkg_dir/dist" ] && [ ! -d "$pkg_dir/generated" ]; then
echo " SKIP (no dist/): $pkg_name"
((skipped++))
continue
((skipped++)); continue
fi
# Prepare work dirs
local_work="$WORK_DIR/check-$dir_name/local"
registry_work="$WORK_DIR/check-$dir_name/registry"
rm -rf "$WORK_DIR/check-$dir_name"
mkdir -p "$local_work" "$registry_work"
# Pack locally (single pack — reused for publish if needed)
safe_name="${pkg_name//@/}"; safe_name="${safe_name//\//-}"
pack_dir="$WORK_DIR/$safe_name"
rm -rf "$pack_dir"; mkdir -p "$pack_dir/extracted"
# Pack locally
if ! (cd "$pkg_dir" && pnpm pack --pack-destination "$local_work" >/dev/null 2>&1); then
echo " ERROR (pack failed): $pkg_name"
((errors++))
continue
if ! (cd "$pkg_dir" && pnpm pack --pack-destination "$pack_dir" >/dev/null 2>&1); then
echo " ERROR (pack): $pkg_name"
((errors++)); continue
fi
local_tgz="$(find "$local_work" -maxdepth 1 -name '*.tgz' | head -1)"
local_tgz="$(find "$pack_dir" -maxdepth 1 -name '*.tgz' | head -1)"
if [ -z "$local_tgz" ]; then
echo " ERROR (no tgz): $pkg_name"
((errors++))
continue
((errors++)); continue
fi
# Extract local tarball
mkdir -p "$local_work/extracted"
tar xzf "$local_tgz" -C "$local_work/extracted" 2>/dev/null
# Extract for content hashing (keep around for publish)
tar xzf "$local_tgz" -C "$pack_dir/extracted" 2>/dev/null
# Try to download registry tarball
if download_registry_tarball "$pkg_name" "$pkg_version" "$registry_work"; then
# Extract registry tarball
mkdir -p "$registry_work/extracted"
tar xzf "$registry_work/registry.tgz" -C "$registry_work/extracted" 2>/dev/null
# Compute content hash
local_hash="$(content_hash "$pack_dir/extracted")"
manifest_hash="$(manifest_get_hash "$pkg_name")"
# Compare content fingerprints
local_fp="$(content_fingerprint "$local_work/extracted")"
registry_fp="$(content_fingerprint "$registry_work/extracted")"
if [ "$local_fp" = "$registry_fp" ]; then
if [ "$local_hash" = "$manifest_hash" ]; then
echo " UP-TO-DATE: $pkg_name@$pkg_version"
((up_to_date++))
rm -rf "$pack_dir" # free disk
else
echo " OUTDATED: $pkg_name@$pkg_version"
if [ -z "$manifest_hash" ]; then
echo " NEW: $pkg_name@$pkg_version"
else
echo " CHANGED: $pkg_name@$pkg_version"
fi
outdated_dirs+=("$pkg_dir")
outdated_names+=("$pkg_name@$pkg_version")
outdated_names+=("$pkg_name")
outdated_hashes+=("$local_hash")
((changed++))
fi
else
echo " NOT FOUND: $pkg_name@$pkg_version (will publish)"
outdated_dirs+=("$pkg_dir")
outdated_names+=("$pkg_name@$pkg_version")
((not_found++))
fi
# Cleanup check dir to save disk space
rm -rf "$WORK_DIR/check-$dir_name"
done
# ── Summary ────────────────────────────────────────────────
@ -368,8 +320,7 @@ done
echo ""
echo "==> Summary"
echo " Up-to-date: $up_to_date"
echo " Outdated: $changed"
echo " Not found: $not_found"
echo " Changed: $changed"
echo " Skipped: $skipped"
echo " Errors: $errors"
echo ""
@ -390,59 +341,97 @@ if [ "$DRY_RUN" = true ]; then
exit 0
fi
# Step 3: Publish outdated packages
# Phase 3: Publish changed packages
echo "==> Publishing ${#outdated_dirs[@]} package(s)..."
echo ""
# Write .npmrc into WORK_DIR for auth (npm reads .npmrc from cwd)
cat > "$WORK_DIR/.npmrc" <<EOF
//${AUTH_TARGET}:_authToken=${TOKEN}
EOF
published=0
pub_errors=0
bumped_packages=()
for i in "${!outdated_dirs[@]}"; do
pkg_dir="${outdated_dirs[$i]}"
pkg_name="$(pkg_field name "$pkg_dir/package.json")"
pkg_name="${outdated_names[$i]}"
local_hash="${outdated_hashes[$i]}"
pkg_version="$(pkg_field version "$pkg_dir/package.json")"
safe_name="${pkg_name//@/}"; safe_name="${safe_name//\//-}"
pack_dir="$WORK_DIR/$safe_name"
echo " [$((i+1))/${#outdated_dirs[@]}] $pkg_name@$pkg_version"
# If version already exists in registry, bump patch version
if registry_version_exists "$pkg_name" "$pkg_version"; then
echo " Version $pkg_version exists in registry, bumping patch..."
new_version="$(bump_patch_version "$pkg_dir/package.json" "$pkg_name")"
echo " Bumped to $new_version"
bumped_packages+=("$pkg_dir")
# Find next available version if current already exists
if version_exists "$pkg_name" "$pkg_version"; then
new_version="$(next_version "$pkg_name" "$pkg_version")"
echo " Version $pkg_version exists, bumping to $new_version"
# Update source package.json
node -e "
const fs=require('fs'),f=process.argv[1];
const p=JSON.parse(fs.readFileSync(f,'utf8'));
p.version=process.argv[2];
fs.writeFileSync(f,JSON.stringify(p,null,2)+'\n');
" "$pkg_dir/package.json" "$new_version"
bumped_packages+=("$pkg_dir/package.json")
pkg_version="$new_version"
# Also update the extracted tarball's package.json (for repack)
node -e "
const fs=require('fs'),f=process.argv[1];
const p=JSON.parse(fs.readFileSync(f,'utf8'));
p.version=process.argv[2];
fs.writeFileSync(f,JSON.stringify(p,null,2)+'\n');
" "$pack_dir/extracted/package/package.json" "$new_version"
fi
# Re-read version after potential bump
pkg_version="$(pkg_field version "$pkg_dir/package.json")"
# Strip publishConfig from extracted package.json (for npm repack)
node -e "
const fs=require('fs'),f=process.argv[1];
const p=JSON.parse(fs.readFileSync(f,'utf8'));
delete p.publishConfig;
fs.writeFileSync(f,JSON.stringify(p,null,2)+'\n');
" "$pack_dir/extracted/package/package.json"
# Repack with npm (from already-extracted content — no second pnpm pack)
rm -f "$pack_dir"/*.tgz
if ! (cd "$pack_dir/extracted/package" && npm pack --pack-destination "$pack_dir" >/dev/null 2>&1); then
echo " ERROR: npm repack failed"
((pub_errors++)); continue
fi
final_tgz="$(find "$pack_dir" -maxdepth 1 -name '*.tgz' | head -1)"
if [ -z "$final_tgz" ]; then
echo " ERROR: no tarball after repack"
((pub_errors++)); continue
fi
# Publish
echo " Publishing $pkg_name@$pkg_version..."
if publish_package "$pkg_dir"; then
if npm_clean npm publish "$final_tgz" \
--registry "$REGISTRY_URL" \
--userconfig "$NPMRC_FILE" 2>&1; then
echo " Published $pkg_name@$pkg_version"
manifest_set "$pkg_name" "$pkg_version" "$local_hash"
((published++))
else
echo " FAILED to publish $pkg_name@$pkg_version"
((pub_errors++))
fi
# Free disk
rm -rf "$pack_dir"
echo ""
done
echo "==> Done: $published published, $pub_errors failed"
# Show bumped packages so the user knows which package.json files changed
if [ ${#bumped_packages[@]} -gt 0 ]; then
echo ""
echo "NOTE: ${#bumped_packages[@]} package(s) had their patch version bumped."
echo " These package.json changes are local — commit them when ready:"
for d in "${bumped_packages[@]}"; do
echo " $d/package.json"
echo "NOTE: ${#bumped_packages[@]} package(s) had their version bumped."
echo " Commit these + the manifest when ready:"
for f in "${bumped_packages[@]}"; do
echo " $f"
done
echo " $MANIFEST_FILE"
fi
exit $((pub_errors > 0 ? 1 : 0))

View File

@ -97,14 +97,20 @@ is_skip_package() {
return 1
}
# Check registry using shared npmrc (reliable auth for both GET and PUT)
# Check registry using shared npmrc (reliable auth, proxy stripped)
version_on_registry() {
local name="$1" version="$2"
(cd "$TMP_DIR" && env \
-u http_proxy -u https_proxy -u HTTP_PROXY -u HTTPS_PROXY \
-u npm_config_proxy -u npm_config_https_proxy \
-u NPM_CONFIG_PROXY -u NPM_CONFIG_HTTPS_PROXY \
-u NPM_CONFIG_REGISTRY -u NPM_CONFIG_STRICT_SSL \
-u NPM_CONFIG_NOPROXY -u NODE_TLS_REJECT_UNAUTHORIZED \
npm view "${name}@${version}" version \
--registry "$REGISTRY_URL" \
--userconfig "$NPMRC_FILE" \
--silent \
2>/dev/null || true
2>/dev/null) || true
}
# ── Preflight ──────────────────────────────────────────────────────────────────
@ -123,7 +129,13 @@ command -v node >/dev/null 2>&1 || fail "node not found in PATH"
# Create shared tmp dir + npmrc early so version checks work before publishing
mkdir -p "$TMP_DIR"
trap 'rm -rf "$TMP_DIR"' EXIT
printf '//%s:_authToken=%s\n' "$AUTH_TARGET" "$TOKEN" > "$NPMRC_FILE"
{
printf '//%s:_authToken=%s\n' "$AUTH_TARGET" "$TOKEN"
printf '@bytelyst:registry=%s\n' "$REGISTRY_URL"
if [ "$IS_CORP" = true ]; then
printf 'proxy=false\nhttps-proxy=false\n'
fi
} > "$NPMRC_FILE"
# Show resolved config
log "Network: $NETWORK_MODE ($( [ "$IS_CORP" = true ] && echo "corp — localhost tunnel" || echo "home — Azure VM" ))"
@ -131,11 +143,17 @@ info "Registry: $REGISTRY_URL"
# Verify token can read from registry
log "Verifying registry credentials..."
if ! npm view "@bytelyst/errors" version \
if ! (cd "$TMP_DIR" && env \
-u http_proxy -u https_proxy -u HTTP_PROXY -u HTTPS_PROXY \
-u npm_config_proxy -u npm_config_https_proxy \
-u NPM_CONFIG_PROXY -u NPM_CONFIG_HTTPS_PROXY \
-u NPM_CONFIG_REGISTRY -u NPM_CONFIG_STRICT_SSL \
-u NPM_CONFIG_NOPROXY -u NODE_TLS_REJECT_UNAUTHORIZED \
npm view "@bytelyst/errors" version \
--registry "$REGISTRY_URL" \
--userconfig "$NPMRC_FILE" \
--silent 2>/dev/null; then
fail "Registry auth failed — check GITEA_NPM_TOKEN has read:package scope"
--silent 2>/dev/null); then
fail "Registry auth failed — check GITEA_NPM_TOKEN has read+write:package scope"
fi
ok "Registry credentials verified"
@ -324,31 +342,21 @@ publish_package() {
return
fi
# Publish using shared npmrc (corp: strip proxy env so npm reaches localhost directly)
# Publish using shared npmrc (strip proxy env so npm reaches localhost/VM directly)
local publish_log="$work_dir/publish.log"
local publish_ok=false
if [ "$IS_CORP" = true ]; then
if (cd "$work_dir" && env \
if (cd "$TMP_DIR" && env \
-u http_proxy -u https_proxy -u HTTP_PROXY -u HTTPS_PROXY \
-u npm_config_proxy -u npm_config_https_proxy \
-u NPM_CONFIG_PROXY -u NPM_CONFIG_HTTPS_PROXY \
-u npm_config_noproxy -u NPM_CONFIG_NOPROXY \
-u NODE_TLS_REJECT_UNAUTHORIZED \
-u NPM_CONFIG_REGISTRY -u NPM_CONFIG_STRICT_SSL \
-u NPM_CONFIG_NOPROXY -u NODE_TLS_REJECT_UNAUTHORIZED \
npm publish "$final_tgz" \
--registry "$REGISTRY_URL" \
--userconfig "$NPMRC_FILE" \
--silent 2>"$publish_log"); then
publish_ok=true
fi
else
if npm publish "$final_tgz" \
--registry "$REGISTRY_URL" \
--userconfig "$NPMRC_FILE" \
--silent \
2>"$publish_log"; then
publish_ok=true
fi
fi
if [ "$publish_ok" = true ]; then
ok "$name@$version published"
PUBLISHED+=("$name@$version")