From 0db3624d962ae342c1c0aa7dfe2ea937d43ba83e Mon Sep 17 00:00:00 2001 From: charliewwdev Date: Tue, 1 Sep 2026 18:34:00 +0800 Subject: [PATCH] chore: make release.sh the only release entrypoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four scripts could cut a release, three of them able to bypass the correct flow. That contradicts the project's own rule ("ALWAYS use the release script. NEVER bump versions or commit/tag/push manually") and is the soil the v0.9.24 and v0.9.36 orphaned-tag incidents grew in. Removed, all untouched since February and referenced by no workflow: - publish.sh — hardcoded v0.2.24 against a current 0.9.36 and ran `flutter pub publish` directly, so it could ship the working tree to pub.dev with no tag, no version sync and no CI. Output was Chinese, against the English-only rule. - bump_version.dart — printed `git push origin main --tags` as step 4, reproducing the exact non-atomic push that stranded two releases. - bump_version.sh — updated 5 version files where release.sh updates 16, so using it left the versions inconsistent. The same push command was documented in CLAUDE.md and RELEASE_GUIDE.md, so following either by hand hit the bug even with the script fixed. Both now verify main matches origin/main first and push with --atomic naming the single tag. Remaining mentions in CHANGELOG.md and docs/CLEANUP_SUMMARY.md are historical records and are left as they are. --- .github/RELEASE_GUIDE.md | 15 +++- CLAUDE.md | 13 ++- scripts/bump_version.dart | 176 -------------------------------------- scripts/bump_version.sh | 56 ------------ scripts/publish.sh | 18 ---- 5 files changed, 24 insertions(+), 254 deletions(-) delete mode 100755 scripts/bump_version.dart delete mode 100755 scripts/bump_version.sh delete mode 100755 scripts/publish.sh diff --git a/.github/RELEASE_GUIDE.md b/.github/RELEASE_GUIDE.md index b100a736..2a8f10fa 100644 --- a/.github/RELEASE_GUIDE.md +++ b/.github/RELEASE_GUIDE.md @@ -47,13 +47,22 @@ # - intellij-plugin/plugin.xml # - README.md -# 3. Commit and tag +# 3. Confirm the release is cut from origin/main. +# The tag triggers the release workflow, so a tag that is not on main publishes +# from a commit nobody can see — this is how v0.9.24 and v0.9.36 shipped. +git fetch origin main +git rev-parse main origin/main # must print the same SHA twice + +# 4. Commit and tag git add -A git commit -m "chore: Release vX.Y.Z" git tag vX.Y.Z -git push origin main --tags -# 4. Wait for GitHub Actions +# --atomic, never `--tags`: without it a rejected branch update still lets the +# tag through, and CI then publishes from an orphaned commit. +git push --atomic origin main refs/tags/vX.Y.Z + +# 5. Wait for GitHub Actions # Auto-publishes to: pub.dev, npm, VSCode, JetBrains, Homebrew ``` diff --git a/CLAUDE.md b/CLAUDE.md index 1d752281..63de164c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,11 +114,22 @@ The script handles everything: version bumps across all files, CHANGELOG entry, - `README.md` - flutter_skill: ^X.Y.Z 3. **Commit and Tag** + + First confirm the release will be cut from `origin/main` — the tag is what + triggers the release workflow, so a tag that is not on `main` publishes from a + commit nobody can see. This is how v0.9.24 and v0.9.36 shipped the wrong tree. + ```bash + git fetch origin main + git rev-parse main origin/main # must print the same SHA twice + git add -A git commit -m "chore: Release vX.Y.Z\n\n" git tag vX.Y.Z - git push origin main --tags + + # --atomic, never `--tags`: without it a rejected branch update still lets + # the tag through, and CI then publishes from an orphaned commit. + git push --atomic origin main refs/tags/vX.Y.Z ``` 4. **Verify** diff --git a/scripts/bump_version.dart b/scripts/bump_version.dart deleted file mode 100755 index d9c3e9ae..00000000 --- a/scripts/bump_version.dart +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env dart - -/// Unified version bumping script for all distribution channels -/// -/// Usage: -/// dart scripts/bump_version.dart -/// dart scripts/bump_version.dart 0.2.22 -/// -/// Updates: -/// - pubspec.yaml -/// - packaging/npm/package.json -/// - vscode-extension/package.json -/// - intellij-plugin/build.gradle.kts - -import 'dart:io'; - -void main(List args) { - if (args.isEmpty) { - print('Error: Version number required'); - print('Usage: dart scripts/bump_version.dart '); - print('Example: dart scripts/bump_version.dart 0.2.22'); - exit(1); - } - - final version = args[0]; - - // Validate version format (semantic versioning) - final versionRegex = RegExp(r'^\d+\.\d+\.\d+(-[\w.]+)?$'); - if (!versionRegex.hasMatch(version)) { - print('Error: Invalid version format. Expected: x.y.z or x.y.z-suffix'); - print('Got: $version'); - exit(1); - } - - print('Updating all distribution channels to version $version\n'); - - final results = {}; - - // Update pubspec.yaml - results['pubspec.yaml'] = updatePubspec(version); - - // Update packaging/npm/package.json - results['packaging/npm/package.json'] = - updatePackageJson('packaging/npm/package.json', version); - - // Update vscode-extension/package.json - results['vscode-extension/package.json'] = - updatePackageJson('vscode-extension/package.json', version); - - // Update intellij-plugin/build.gradle.kts - results['intellij-plugin/build.gradle.kts'] = updateGradleKts(version); - - // Print summary - print('\n' + '═' * 60); - print('VERSION BUMP SUMMARY'); - print('═' * 60); - - var allSuccess = true; - results.forEach((file, success) { - final status = success ? '✅' : '❌'; - print('$status $file'); - if (!success) allSuccess = false; - }); - - print('═' * 60); - - if (allSuccess) { - print('\n✅ All files updated successfully!'); - print('\nNext steps:'); - print(' 1. Review changes: git diff'); - print( - ' 2. Commit: git add -A && git commit -m "chore: Bump version to $version"'); - print(' 3. Tag: git tag v$version'); - print(' 4. Push: git push origin main --tags'); - exit(0); - } else { - print('\n❌ Some files failed to update. Please check manually.'); - exit(1); - } -} - -bool updatePubspec(String version) { - try { - final file = File('pubspec.yaml'); - if (!file.existsSync()) { - print('❌ pubspec.yaml not found'); - return false; - } - - var content = file.readAsStringSync(); - final oldVersionMatch = - RegExp(r'^version:\s*(.+)$', multiLine: true).firstMatch(content); - - if (oldVersionMatch == null) { - print('❌ Could not find version in pubspec.yaml'); - return false; - } - - final oldVersion = oldVersionMatch.group(1)!.trim(); - content = content.replaceFirst( - RegExp(r'^version:.*$', multiLine: true), - 'version: $version', - ); - - file.writeAsStringSync(content); - print('✅ pubspec.yaml: $oldVersion → $version'); - return true; - } catch (e) { - print('❌ Error updating pubspec.yaml: $e'); - return false; - } -} - -bool updatePackageJson(String path, String version) { - try { - final file = File(path); - if (!file.existsSync()) { - print('❌ $path not found'); - return false; - } - - var content = file.readAsStringSync(); - final oldVersionMatch = - RegExp(r'"version":\s*"([^"]+)"').firstMatch(content); - - if (oldVersionMatch == null) { - print('❌ Could not find version in $path'); - return false; - } - - final oldVersion = oldVersionMatch.group(1)!; - content = content.replaceFirst( - RegExp(r'"version":\s*"[^"]+"'), - '"version": "$version"', - ); - - file.writeAsStringSync(content); - print('✅ $path: $oldVersion → $version'); - return true; - } catch (e) { - print('❌ Error updating $path: $e'); - return false; - } -} - -bool updateGradleKts(String version) { - try { - final file = File('intellij-plugin/build.gradle.kts'); - if (!file.existsSync()) { - print('❌ intellij-plugin/build.gradle.kts not found'); - return false; - } - - var content = file.readAsStringSync(); - final oldVersionMatch = RegExp(r'^version\s*=\s*"([^"]+)"', multiLine: true) - .firstMatch(content); - - if (oldVersionMatch == null) { - print('❌ Could not find version in build.gradle.kts'); - return false; - } - - final oldVersion = oldVersionMatch.group(1)!; - content = content.replaceFirst( - RegExp(r'^version\s*=\s*"[^"]+"', multiLine: true), - 'version = "$version"', - ); - - file.writeAsStringSync(content); - print('✅ intellij-plugin/build.gradle.kts: $oldVersion → $version'); - return true; - } catch (e) { - print('❌ Error updating build.gradle.kts: $e'); - return false; - } -} diff --git a/scripts/bump_version.sh b/scripts/bump_version.sh deleted file mode 100755 index c4952e89..00000000 --- a/scripts/bump_version.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -# Bump version across all packages - -set -e - -if [ -z "$1" ]; then - echo "Usage: $0 " - echo "Example: $0 0.4.0" - exit 1 -fi - -VERSION=$1 - -echo "🔄 Updating version to $VERSION across all packages..." -echo "" - -# 1. pubspec.yaml -echo "📦 Updating pubspec.yaml..." -sed -i.bak "s/^version: .*/version: $VERSION/" pubspec.yaml -rm -f pubspec.yaml.bak - -# 2. SKILL.md -echo "📦 Updating SKILL.md..." -sed -i.bak "s/^version: .*/version: $VERSION/" SKILL.md -rm -f SKILL.md.bak - -# 3. packaging/npm/package.json -if [ -f packaging/npm/package.json ]; then - echo "📦 Updating packaging/npm/package.json..." - sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" packaging/npm/package.json - rm -f packaging/npm/package.json.bak -fi - -# 4. vscode-extension/package.json -if [ -f vscode-extension/package.json ]; then - echo "📦 Updating vscode-extension/package.json..." - sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" vscode-extension/package.json - rm -f vscode-extension/package.json.bak -fi - -# 5. intellij-plugin/build.gradle.kts -if [ -f intellij-plugin/build.gradle.kts ]; then - echo "📦 Updating intellij-plugin/build.gradle.kts..." - sed -i.bak "s/^version = \".*\"/version = \"$VERSION\"/" intellij-plugin/build.gradle.kts - rm -f intellij-plugin/build.gradle.kts.bak -fi - -echo "" -echo "✅ Version updated to $VERSION in all packages!" -echo "" -echo "Next steps:" -echo " 1. Review changes: git diff" -echo " 2. Update CHANGELOG.md" -echo " 3. Commit: git commit -am 'chore: Release v$VERSION'" -echo " 4. Tag: git tag -a v$VERSION -m 'Release v$VERSION'" -echo " 5. Push: git push origin main && git push origin v$VERSION" diff --git a/scripts/publish.sh b/scripts/publish.sh deleted file mode 100755 index d9cb2ceb..00000000 --- a/scripts/publish.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -echo "🚀 flutter-skill v0.2.24 发布脚本" -echo "" -echo "正在发布到官方 pub.dev..." -echo "" - -# 设置环境变量指向官方 pub.dev -export PUB_HOSTED_URL=https://pub.dev -export FLUTTER_STORAGE_BASE_URL= - -# 发布 -flutter pub publish - -echo "" -echo "✅ 发布完成!" -echo "" -echo "访问 https://pub.dev/packages/flutter_skill 查看"