From 7bf0e5bc35240d718a33bd1045f3ca369173fa5b Mon Sep 17 00:00:00 2001 From: weishu Date: Tue, 27 Oct 2026 08:32:28 +0800 Subject: [PATCH] Release version 0.31.2 --- android/app/build.gradle.kts | 2 +- bun.lock | 22 ++++---- cli/package.json | 12 ++-- cli/scripts/release-all.ts | 90 +++++++++++++++++++++++------- ios/Hapi.xcodeproj/project.pbxproj | 8 +-- shared/src/buildInfo.ts | 2 +- 6 files changed, 94 insertions(+), 42 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index a7292fb5..6dedff99 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -80,7 +80,7 @@ android { value.toInt().also { require(it > 0) { "hapiVersionCode must be positive" } } }.orElse(1).get() // Tracks the hapi CLI/hub release train. - versionName = providers.gradleProperty("hapiVersionName").orElse("0.30.7").get() + versionName = providers.gradleProperty("hapiVersionName").orElse("0.31.2").get() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/bun.lock b/bun.lock index 461b0c07..09c15627 100644 --- a/bun.lock +++ b/bun.lock @@ -13,7 +13,7 @@ }, "cli": { "name": "@twsxtd/hapi", - "version": "0.30.7", + "version": "0.31.2", "bin": { "hapi": "bin/hapi.cjs", }, @@ -48,11 +48,11 @@ "vitest": "^4.0.16", }, "optionalDependencies": { - "@twsxtd/hapi-darwin-arm64": "0.30.7", - "@twsxtd/hapi-darwin-x64": "0.30.7", - "@twsxtd/hapi-linux-arm64": "0.30.7", - "@twsxtd/hapi-linux-x64": "0.30.7", - "@twsxtd/hapi-win32-x64": "0.30.7", + "@twsxtd/hapi-darwin-arm64": "0.31.2", + "@twsxtd/hapi-darwin-x64": "0.31.2", + "@twsxtd/hapi-linux-arm64": "0.31.2", + "@twsxtd/hapi-linux-x64": "0.31.2", + "@twsxtd/hapi-win32-x64": "0.31.2", }, }, "docs": { @@ -1111,15 +1111,15 @@ "@twsxtd/hapi": ["@twsxtd/hapi@workspace:cli"], - "@twsxtd/hapi-darwin-arm64": ["@twsxtd/hapi-darwin-arm64@0.30.7", "", { "os": "darwin", "cpu": "arm64", "bin": { "hapi": "bin/hapi" } }, "sha512-liXzQiIdNrEobPqlVRmbdIcN8r7W9e/7Vcc5gO34keJwzr6PZ4JZO4BM2PnNuw57WD2EeXb4aBR5PSOFhYi6Xw=="], + "@twsxtd/hapi-darwin-arm64": ["@twsxtd/hapi-darwin-arm64@0.31.2", "", { "os": "darwin", "cpu": "arm64", "bin": { "hapi": "bin/hapi" } }, "sha512-vb0dhAIuoEm13sRE/PX7uW5ZThucJWw0eta5Y5gFNXtySqJGBGkZTMAzzrsRRzPKvxDO8etdO7LVO+mat9R90w=="], - "@twsxtd/hapi-darwin-x64": ["@twsxtd/hapi-darwin-x64@0.30.7", "", { "os": "darwin", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-GbakM5JycgyINRfsBfHwvUIqA/faq63Kzu28ARcFxXqyet4hrS8P0Xawrsbmawq6RibK3sI74kX5cRlFmtXpZw=="], + "@twsxtd/hapi-darwin-x64": ["@twsxtd/hapi-darwin-x64@0.31.2", "", { "os": "darwin", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-3LCrR+vdUkOie5JilNqh4g+K0lCsNRcLemof+sBeW0djqA+604taRu+CvYQO36Edj0KMnlxcrdtWGYQ1PQtHfg=="], - "@twsxtd/hapi-linux-arm64": ["@twsxtd/hapi-linux-arm64@0.30.7", "", { "os": "linux", "cpu": "arm64", "bin": { "hapi": "bin/hapi" } }, "sha512-v3OQ7ZrsLLwcCGIBd9lCCp+5vNoR0u+g+oWUV2iIAWvi2nmGF6zHvYT0Bl4+G9rjk29wdR8XemrItSfQrc3kbg=="], + "@twsxtd/hapi-linux-arm64": ["@twsxtd/hapi-linux-arm64@0.31.2", "", { "os": "linux", "cpu": "arm64", "bin": { "hapi": "bin/hapi" } }, "sha512-xI1NNKXjDXcIb2b1BdEBSa0ligiurstAs+QIRSUZo5m8eQ0uTXXCltkol/S2BLqQA9X9mSABZVieqaFob2PW2Q=="], - "@twsxtd/hapi-linux-x64": ["@twsxtd/hapi-linux-x64@0.30.7", "", { "os": "linux", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-/e712GKRzzeVD2SMO+ApIxd1/CkIHyKZRRzkEK1WWWvcie45Onres8z58V3liEA/JxbHUxaKaOPW7N0xQBVq1Q=="], + "@twsxtd/hapi-linux-x64": ["@twsxtd/hapi-linux-x64@0.31.2", "", { "os": "linux", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-yWoUQhPXC5Wyt7P984ewgimqrFsq2e2YNYrk4B/HzTpE5WctvbN0VgczHMtLPmnbg1hFQrS3xhDCz2LwiohJkA=="], - "@twsxtd/hapi-win32-x64": ["@twsxtd/hapi-win32-x64@0.30.7", "", { "os": "win32", "cpu": "x64", "bin": { "hapi": "bin/hapi.exe" } }, "sha512-oO43ncEFZVEUIUxQjzwTNdr70ae1pc8TEYBBa+lGhIOpU5NQNWu+Fc0tKqwvIcRKhXYqsHHPaVLqp+gg5uHxeQ=="], + "@twsxtd/hapi-win32-x64": ["@twsxtd/hapi-win32-x64@0.31.2", "", { "os": "win32", "cpu": "x64", "bin": { "hapi": "bin/hapi.exe" } }, "sha512-U4XZN0r/3v42FkXaJlcdQwzapxf+4DK13g5Xgu+S0fEpz26grgmBd8EInDhNKCZROB33DAQU0fU0g4pUXxNaRw=="], "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], diff --git a/cli/package.json b/cli/package.json index e7f9a084..51743db8 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@twsxtd/hapi", - "version": "0.30.7", + "version": "0.31.2", "description": "App for agentic coding - access coding agent anywhere", "author": "Kirill Dubovitskiy & weishu", "license": "AGPL-3.0-only", @@ -26,11 +26,11 @@ } }, "optionalDependencies": { - "@twsxtd/hapi-darwin-arm64": "0.30.7", - "@twsxtd/hapi-darwin-x64": "0.30.7", - "@twsxtd/hapi-linux-arm64": "0.30.7", - "@twsxtd/hapi-linux-x64": "0.30.7", - "@twsxtd/hapi-win32-x64": "0.30.7" + "@twsxtd/hapi-darwin-arm64": "0.31.2", + "@twsxtd/hapi-darwin-x64": "0.31.2", + "@twsxtd/hapi-linux-arm64": "0.31.2", + "@twsxtd/hapi-linux-x64": "0.31.2", + "@twsxtd/hapi-win32-x64": "0.31.2" }, "scripts": { "postinstall": "node -e \"try{require('fs').chmodSync(require('path').join(__dirname,'bin','hapi.cjs'),0o755)}catch(e){}\"", diff --git a/cli/scripts/release-all.ts b/cli/scripts/release-all.ts index 4837150f..ae3651a5 100644 --- a/cli/scripts/release-all.ts +++ b/cli/scripts/release-all.ts @@ -6,6 +6,9 @@ * 3. Publish platform packages first (so lockfile can resolve them) * 4. Verify all platform packages are live on npm * 5. Publish main package + * + * Re-running with the same version is safe: packages already on npm are skipped, + * so a release that failed midway can be resumed. * 6. bun install --lockfile-only --os=* --cpu=* (to lock all platform packages) * 7. Git commit + tag + push */ @@ -42,6 +45,51 @@ function run(cmd: string, cwd = projectRoot): void { } } +const platforms = ['darwin-arm64', 'darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64']; +const mainPackage = '@twsxtd/hapi'; + +function isPublished(name: string, expectedVersion: string): boolean { + try { + const published = execSync(`npm view ${name}@${expectedVersion} version`, { + encoding: 'utf-8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + return published === expectedVersion; + } catch { + return false; + } +} + +function assertNpmLogin(): string { + try { + return execSync('npm whoami', { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); + } catch { + console.error('❌ npm token is missing or invalid. Run `npm login` (or set a new token) first.'); + process.exit(1); + } +} + +// npm answers an unauthorized publish to a scoped package with a misleading 404, +// so verify write access to every package before publishing anything. +function assertWriteAccess(packages: string[]): void { + let access: Record; + try { + access = JSON.parse(execSync('npm access list packages --json', { + encoding: 'utf-8', + stdio: ['ignore', 'pipe', 'ignore'], + })); + } catch { + console.error('❌ Could not list npm package access. Check that your npm token is valid.'); + process.exit(1); + } + const missing = packages.filter(name => access[name] !== 'read-write'); + if (missing.length > 0) { + console.error(`❌ npm token lacks write access to: ${missing.join(', ')}`); + console.error(' Use a token with read-write permission on all @twsxtd/hapi* packages.'); + process.exit(1); + } +} + async function waitForPlatformPackages(platforms: string[], expectedVersion: string): Promise { const timeoutMs = 10 * 60 * 1000; const intervalMs = 15_000; @@ -50,14 +98,9 @@ async function waitForPlatformPackages(platforms: string[], expectedVersion: str while (pending.size > 0) { for (const name of [...pending]) { - try { - const published = execSync(`npm view ${name} version`, { encoding: 'utf-8' }).trim(); - if (published === expectedVersion) { - console.log(` ✓ ${name}@${expectedVersion} is live on npm`); - pending.delete(name); - } - } catch { - // Not published yet, keep waiting + if (isPublished(name, expectedVersion)) { + console.log(` ✓ ${name}@${expectedVersion} is live on npm`); + pending.delete(name); } } if (pending.size === 0) { @@ -105,15 +148,12 @@ async function main(version: string): Promise { // Pre-check: Ensure npm is logged in (skip in dry-run mode) if (!dryRun) { - try { - const npmUser = execSync('npm whoami', { encoding: 'utf-8' }).trim(); - console.log(` ✓ Logged in to npm as: ${npmUser}`); - } catch { - console.error('❌ Not logged in to npm. Run `npm login` first.'); - process.exit(1); - } + const npmUser = assertNpmLogin(); + console.log(` ✓ Logged in to npm as: ${npmUser}`); + assertWriteAccess([mainPackage, ...platforms.map(platform => `${mainPackage}-${platform}`)]); + console.log(' ✓ npm token can publish all packages'); } else { - console.log(' ✓ Skipping npm login check (dry-run)'); + console.log(' ✓ Skipping npm login and access checks (dry-run)'); } // Step 1: Update all release versions before building or publishing. @@ -132,8 +172,12 @@ async function main(version: string): Promise { // Step 3: Prepare and publish platform packages console.log('\n📤 Step 3: Publishing platform packages...'); run('bun run prepare-npm-packages'); - const platforms = ['darwin-arm64', 'darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64']; for (const platform of platforms) { + const name = `${mainPackage}-${platform}`; + if (!dryRun && isPublished(name, version)) { + console.log(` ✓ ${name}@${version} already published, skipping`); + continue; + } const npmDir = join(projectRoot, 'npm', platform); run(`npm publish --access public${dryRun ? ' --dry-run' : ''}`, npmDir); } @@ -150,8 +194,16 @@ async function main(version: string): Promise { // Step 5: Publish main package console.log('\n📤 Step 5: Publishing main package...'); - const mainNpmDir = join(projectRoot, 'npm', 'main'); - run(`npm publish --access public${dryRun ? ' --dry-run' : ''}`, mainNpmDir); + if (!dryRun && isPublished(mainPackage, version)) { + console.log(` ✓ ${mainPackage}@${version} already published, skipping`); + } else { + if (!dryRun) { + // The token may have expired while building or waiting for platform packages. + assertNpmLogin(); + } + const mainNpmDir = join(projectRoot, 'npm', 'main'); + run(`npm publish --access public${dryRun ? ' --dry-run' : ''}`, mainNpmDir); + } // --publish-npm 模式到此结束 if (publishNpm) { diff --git a/ios/Hapi.xcodeproj/project.pbxproj b/ios/Hapi.xcodeproj/project.pbxproj index 76a2a6bc..3c3fac74 100644 --- a/ios/Hapi.xcodeproj/project.pbxproj +++ b/ios/Hapi.xcodeproj/project.pbxproj @@ -408,7 +408,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.30.7; + MARKETING_VERSION = 0.31.2; PRODUCT_BUNDLE_IDENTIFIER = run.hapi.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -439,7 +439,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.30.7; + MARKETING_VERSION = 0.31.2; PRODUCT_BUNDLE_IDENTIFIER = run.hapi.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -463,7 +463,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.30.7; + MARKETING_VERSION = 0.31.2; PRODUCT_BUNDLE_IDENTIFIER = run.hapi.app.HapiNotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -488,7 +488,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 0.30.7; + MARKETING_VERSION = 0.31.2; PRODUCT_BUNDLE_IDENTIFIER = run.hapi.app.HapiNotificationService; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/shared/src/buildInfo.ts b/shared/src/buildInfo.ts index 2eaf2c38..321a2d35 100644 --- a/shared/src/buildInfo.ts +++ b/shared/src/buildInfo.ts @@ -1 +1 @@ -export const APP_VERSION = '0.30.7' +export const APP_VERSION = '0.31.2'