cherrypick-interactive 1.6.1 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,107 @@
1
+ name: Release & Publish
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: write
9
+
10
+ jobs:
11
+ release:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - uses: actions/setup-node@v4
19
+ with:
20
+ node-version: 20
21
+ registry-url: https://registry.npmjs.org
22
+
23
+ - name: Get current version
24
+ id: current
25
+ run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
26
+
27
+ - name: Determine version bump from commits
28
+ id: bump
29
+ run: |
30
+ # Get commits since last tag (or all commits if no tags)
31
+ LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
32
+
33
+ if [ -z "$LAST_TAG" ]; then
34
+ COMMITS=$(git log --pretty=format:"%s" HEAD)
35
+ else
36
+ COMMITS=$(git log --pretty=format:"%s" "${LAST_TAG}..HEAD")
37
+ fi
38
+
39
+ if [ -z "$COMMITS" ]; then
40
+ echo "bump=none" >> $GITHUB_OUTPUT
41
+ exit 0
42
+ fi
43
+
44
+ BUMP="none"
45
+
46
+ # Check for breaking changes (major)
47
+ if echo "$COMMITS" | grep -qE "^[a-z]+(\(.+\))?!:"; then
48
+ BUMP="major"
49
+ # Check for feat (minor)
50
+ elif echo "$COMMITS" | grep -qE "^feat(\(.+\))?:"; then
51
+ BUMP="minor"
52
+ # Check for fix, refactor, perf, etc. (patch)
53
+ elif echo "$COMMITS" | grep -qE "^(fix|refactor|perf|chore|docs|style|test|ci)(\(.+\))?:"; then
54
+ BUMP="patch"
55
+ fi
56
+
57
+ echo "bump=$BUMP" >> $GITHUB_OUTPUT
58
+
59
+ - name: Bump version
60
+ if: steps.bump.outputs.bump != 'none'
61
+ id: version
62
+ run: |
63
+ BUMP=${{ steps.bump.outputs.bump }}
64
+ CURRENT=${{ steps.current.outputs.version }}
65
+
66
+ IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
67
+
68
+ case "$BUMP" in
69
+ major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
70
+ minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
71
+ patch) PATCH=$((PATCH + 1)) ;;
72
+ esac
73
+
74
+ NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
75
+ echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
76
+
77
+ # Update package.json
78
+ node -e "
79
+ const pkg = require('./package.json');
80
+ pkg.version = '${NEW_VERSION}';
81
+ require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
82
+ "
83
+
84
+ echo "Version: $CURRENT → $NEW_VERSION ($BUMP)"
85
+
86
+ - name: Publish to npm
87
+ if: steps.bump.outputs.bump != 'none'
88
+ run: npm publish --access public
89
+ env:
90
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
91
+
92
+ - name: Commit version bump and tag
93
+ if: steps.bump.outputs.bump != 'none'
94
+ run: |
95
+ git config user.name "github-actions[bot]"
96
+ git config user.email "github-actions[bot]@users.noreply.github.com"
97
+ git add package.json
98
+ git commit -m "chore(release): v${{ steps.version.outputs.new_version }}"
99
+ git tag "v${{ steps.version.outputs.new_version }}"
100
+ git push
101
+ git push --tags
102
+
103
+ - name: Create GitHub Release
104
+ if: steps.bump.outputs.bump != 'none'
105
+ run: gh release create "v${{ steps.version.outputs.new_version }}" --generate-notes --title "v${{ steps.version.outputs.new_version }}"
106
+ env:
107
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/cli.js CHANGED
@@ -959,8 +959,13 @@ async function runGh(args) {
959
959
  });
960
960
  }
961
961
  async function readJson(filePath) {
962
- const raw = await fsPromises.readFile(filePath, 'utf8');
963
- return JSON.parse(raw);
962
+ try {
963
+ const raw = await fsPromises.readFile(filePath, 'utf8');
964
+ return JSON.parse(raw);
965
+ } catch (e) {
966
+ if (e.code === 'ENOENT') return null;
967
+ throw e;
968
+ }
964
969
  }
965
970
 
966
971
  async function writeJson(filePath, data) {
@@ -968,19 +973,39 @@ async function writeJson(filePath, data) {
968
973
  await fsPromises.writeFile(filePath, text, 'utf8');
969
974
  }
970
975
 
971
- /** Read package.json version; throw if missing */
976
+ /** Read package.json version; prompt to create file with 0.0.0 if it does not exist */
972
977
  async function getPkgVersion(pkgPath) {
973
- const pkg = await readJson(pkgPath);
974
- const v = pkg?.version;
978
+ let pkg = await readJson(pkgPath);
979
+ if (!pkg) {
980
+ log(chalk.yellow(`⚠ ${pkgPath} not found.`));
981
+ const { shouldCreate } = await inquirer.prompt([
982
+ {
983
+ type: 'confirm',
984
+ name: 'shouldCreate',
985
+ message: `Create ${pkgPath} with version 0.0.0?`,
986
+ default: true,
987
+ },
988
+ ]);
989
+ if (!shouldCreate) {
990
+ throw new Error(`Version file ${pkgPath} does not exist. Aborting.`);
991
+ }
992
+ pkg = { "version": "0.0.0" };
993
+ await writeJson(pkgPath, pkg);
994
+ log(chalk.green(`✓ Created ${pkgPath} with version 0.0.0`));
995
+ }
996
+ const v = pkg.version;
975
997
  if (!v) {
976
998
  throw new Error(`No "version" field found in ${pkgPath}`);
977
999
  }
978
1000
  return v;
979
1001
  }
980
1002
 
981
- /** Update package.json version in-place */
1003
+ /** Update package.json version in-place; create file if it does not exist */
982
1004
  async function setPkgVersion(pkgPath, nextVersion) {
983
- const pkg = await readJson(pkgPath);
1005
+ let pkg = await readJson(pkgPath);
1006
+ if (!pkg) {
1007
+ pkg = {};
1008
+ }
984
1009
  pkg.version = nextVersion;
985
1010
  await writeJson(pkgPath, pkg);
986
1011
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cherrypick-interactive",
3
- "version": "1.6.1",
3
+ "version": "1.7.1",
4
4
  "description": "Interactively cherry-pick commits that are in dev but not in main, using subject-based comparison.",
5
5
  "main": "cli.js",
6
6
  "bin": "cli.js",