git-chopstick-core 0.1.7 → 0.1.9
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.
- package/CHANGELOG.md +23 -6
- package/README.md +12 -9
- package/package.json +4 -3
- package/src/__tests__/fixture-helpers.ts +105 -0
- package/src/__tests__/fixtures/test-repo.bundle +0 -0
- package/src/__tests__/integration.test.ts +158 -0
- package/docs/skills/release.md +0 -81
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.9] — 2026-06-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Test fixtures**: Created `src/__tests__/fixtures/test-repo.bundle` — a pre-built git bundle with 6 commits, 3 branches, and 1 tag for faster test setup. Added `src/__tests__/fixture-helpers.ts` with `setupFixtureRepo()` to clone the bundle in ~10ms instead of building a repo from scratch. Refactored integration tests to use the fixture.
|
|
7
|
+
- **Release skill moved**: Moved from `docs/skills/release.md` to `.agents/skills/release-npm.md` and renamed to `release-npm`.
|
|
8
|
+
- **Global default branch**: Set `git config --global init.defaultBranch main`.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Fixture rebuild instructions**: Updated to use `git init -q` (relies on global `init.defaultBranch`).
|
|
12
|
+
- **`prepublishOnly` now runs tests**: Integration tests run during `npm publish` to catch regressions before they ship.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## [0.1.8] — 2026-06-13
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **Test files included in package**: Added `src/__tests__/` to the `files` array in `package.json`. Integration tests ship with the installed package so consumers can run `npm test` to verify the package works for their environment.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
3
23
|
## [0.1.7] — 2026-06-13
|
|
4
24
|
|
|
5
25
|
### Added
|
|
@@ -122,11 +142,8 @@
|
|
|
122
142
|
|
|
123
143
|
---
|
|
124
144
|
|
|
125
|
-
[0.1.
|
|
126
|
-
[0.1.
|
|
127
|
-
[0.1.2]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.2
|
|
128
|
-
[0.1.1]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.1
|
|
129
|
-
[0.1.0]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.0
|
|
145
|
+
[0.1.9]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.9
|
|
146
|
+
[0.1.8]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.8
|
|
130
147
|
[0.1.7]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.7
|
|
131
148
|
[0.1.6]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.6
|
|
132
149
|
[0.1.5]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.5
|
|
@@ -135,4 +152,4 @@
|
|
|
135
152
|
[0.1.2]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.2
|
|
136
153
|
[0.1.1]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.1
|
|
137
154
|
[0.1.0]: https://github.com/parkiyong/git-chopstick-core/releases/tag/v0.1.0
|
|
138
|
-
[Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.
|
|
155
|
+
[Unreleased]: https://github.com/parkiyong/git-chopstick-core/compare/v0.1.9...HEAD
|
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ npm install file:../path/to/git-chopstick-core
|
|
|
26
26
|
```typescript
|
|
27
27
|
import { Repository, getStatus, GitError } from 'git-chopstick-core'
|
|
28
28
|
|
|
29
|
-
const repo = new Repository('/path/to/repo'
|
|
29
|
+
const repo = new Repository('/path/to/repo')
|
|
30
30
|
|
|
31
31
|
try {
|
|
32
32
|
const status = await getStatus(repo)
|
|
@@ -46,7 +46,7 @@ try {
|
|
|
46
46
|
```typescript
|
|
47
47
|
import { Repository, getStatus } from 'git-chopstick-core'
|
|
48
48
|
|
|
49
|
-
const repo = new Repository('/path/to/repo'
|
|
49
|
+
const repo = new Repository('/path/to/repo')
|
|
50
50
|
const status = await getStatus(repo)
|
|
51
51
|
|
|
52
52
|
if (!status) {
|
|
@@ -81,7 +81,7 @@ if (status.rebaseInternalState) {
|
|
|
81
81
|
```typescript
|
|
82
82
|
import { Repository, getStatus, createCommit } from 'git-chopstick-core'
|
|
83
83
|
|
|
84
|
-
const repo = new Repository('/path/to/repo'
|
|
84
|
+
const repo = new Repository('/path/to/repo')
|
|
85
85
|
const status = await getStatus(repo)
|
|
86
86
|
|
|
87
87
|
// Stage and commit all tracked files
|
|
@@ -98,7 +98,7 @@ import {
|
|
|
98
98
|
deleteLocalBranch, renameBranch, getBranches
|
|
99
99
|
} from 'git-chopstick-core'
|
|
100
100
|
|
|
101
|
-
const repo = new Repository('/path/to/repo'
|
|
101
|
+
const repo = new Repository('/path/to/repo')
|
|
102
102
|
|
|
103
103
|
// Create a branch
|
|
104
104
|
await createBranch(repo, 'feature/new-feature', 'main')
|
|
@@ -125,7 +125,7 @@ import {
|
|
|
125
125
|
abortMerge, getMergeBase, isMergeHeadSet
|
|
126
126
|
} from 'git-chopstick-core'
|
|
127
127
|
|
|
128
|
-
const repo = new Repository('/path/to/repo'
|
|
128
|
+
const repo = new Repository('/path/to/repo')
|
|
129
129
|
|
|
130
130
|
const result = await merge(repo, 'feature/new-feature')
|
|
131
131
|
|
|
@@ -149,7 +149,7 @@ import {
|
|
|
149
149
|
Repository, push, pull, getRemotes, getStatus
|
|
150
150
|
} from 'git-chopstick-core'
|
|
151
151
|
|
|
152
|
-
const repo = new Repository('/path/to/repo'
|
|
152
|
+
const repo = new Repository('/path/to/repo')
|
|
153
153
|
const [remote] = await getRemotes(repo)
|
|
154
154
|
const status = await getStatus(repo)
|
|
155
155
|
|
|
@@ -167,7 +167,7 @@ import {
|
|
|
167
167
|
Repository, merge, GitError, GitErrorCodes
|
|
168
168
|
} from 'git-chopstick-core'
|
|
169
169
|
|
|
170
|
-
const repo = new Repository('/path/to/repo'
|
|
170
|
+
const repo = new Repository('/path/to/repo')
|
|
171
171
|
|
|
172
172
|
try {
|
|
173
173
|
await merge(repo, 'other-branch')
|
|
@@ -328,11 +328,14 @@ src/
|
|
|
328
328
|
# Type-check
|
|
329
329
|
npm run typecheck
|
|
330
330
|
|
|
331
|
+
# Run integration tests (uses vitest, creates temp git repos)
|
|
332
|
+
npm test
|
|
333
|
+
|
|
331
334
|
# Build (compile TypeScript → dist/)
|
|
332
335
|
npm run build
|
|
333
336
|
|
|
334
|
-
#
|
|
335
|
-
npm run
|
|
337
|
+
# Package contents dry-run (verify files before publish)
|
|
338
|
+
npm pack --dry-run
|
|
336
339
|
|
|
337
340
|
# Run examples (uses tsx for source-level execution)
|
|
338
341
|
npx tsx examples/get-status.ts /path/to/repo
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-chopstick-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"files": [
|
|
34
34
|
"dist",
|
|
35
35
|
"CHANGELOG.md",
|
|
36
|
-
"docs"
|
|
36
|
+
"docs",
|
|
37
|
+
"src/__tests__"
|
|
37
38
|
],
|
|
38
39
|
"description": "Git backend library extracted from GitHub Desktop",
|
|
39
40
|
"scripts": {
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"build": "npm run clean && tsc",
|
|
42
43
|
"typecheck": "tsc --noEmit",
|
|
43
44
|
"test": "vitest run",
|
|
44
|
-
"prepublishOnly": "npm run typecheck && npm run build"
|
|
45
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
48
|
"byline": "^5.0.0",
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* To rebuild the fixture bundle:
|
|
3
|
+
*
|
|
4
|
+
* cd /tmp && rm -rf fixture-builder && mkdir fixture-builder && cd fixture-builder
|
|
5
|
+
* git init -q && git config user.email f@t.t && git config user.name F
|
|
6
|
+
* echo '# Test Repo' > README.md && echo 'node_modules/' > .gitignore
|
|
7
|
+
* git add .gitignore README.md && git commit -m 'initial: add README and gitignore'
|
|
8
|
+
* echo 'hello' > hello.txt && mkdir -p src && echo 'console.log("hi")' > src/index.js
|
|
9
|
+
* git add hello.txt src/index.js && git commit -m 'feat: add hello.txt and src/index.js'
|
|
10
|
+
* echo 'world' > hello.txt && echo 'console.log("hello world")' > src/index.js
|
|
11
|
+
* git add -A && git commit -m 'feat: update hello and src/index.js'
|
|
12
|
+
* git checkout -b feature/one && echo 'feature one content' > feature-one.txt
|
|
13
|
+
* git add feature-one.txt && git commit -m 'feat: feature one'
|
|
14
|
+
* git checkout main && echo 'another change' > another.txt
|
|
15
|
+
* git add another.txt && git commit -m 'feat: add another.txt'
|
|
16
|
+
* git merge feature/one --no-ff --no-edit -m 'merge: feature/one into main'
|
|
17
|
+
* git checkout -b feature/two && echo 'feature two content' > feature-two.txt
|
|
18
|
+
* git add feature-two.txt && git commit -m 'feat: feature two'
|
|
19
|
+
* git checkout main && git tag -a v0.1.0 -m 'v0.1.0'
|
|
20
|
+
* git bundle create test-repo.bundle --all
|
|
21
|
+
* cp test-repo.bundle <this-dir>/fixtures/
|
|
22
|
+
*
|
|
23
|
+
* This creates a bundle with 6 commits on main, 3 branches, and 1 annotated tag.
|
|
24
|
+
*/
|
|
25
|
+
import { mkdtempSync } from 'fs'
|
|
26
|
+
import { execSync } from 'child_process'
|
|
27
|
+
import { tmpdir } from 'os'
|
|
28
|
+
import { dirname, join } from 'path'
|
|
29
|
+
import { fileURLToPath } from 'url'
|
|
30
|
+
import { Repository } from '../models/repository.js'
|
|
31
|
+
|
|
32
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Path to the pre-built fixture repo bundle.
|
|
36
|
+
*/
|
|
37
|
+
export const FIXTURE_BUNDLE_PATH = join(__dirname, 'fixtures', 'test-repo.bundle')
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Clone the fixture bundle to a temp directory, configure git identity,
|
|
41
|
+
* create local branches for all remote-tracking branches, and return
|
|
42
|
+
* the path and a Repository object.
|
|
43
|
+
*
|
|
44
|
+
* Use this in `beforeAll` for faster test setup — avoids creating a repo
|
|
45
|
+
* from scratch for every test suite.
|
|
46
|
+
*/
|
|
47
|
+
export function setupFixtureRepo(): { repoPath: string; repo: Repository } {
|
|
48
|
+
const repoPath = mkdtempSync(join(tmpdir(), 'gcctest-fixture-'))
|
|
49
|
+
|
|
50
|
+
execSync(`git clone "${FIXTURE_BUNDLE_PATH}" "${repoPath}"`, {
|
|
51
|
+
stdio: 'pipe',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
execSync('git config user.email test@test.test', {
|
|
55
|
+
cwd: repoPath,
|
|
56
|
+
stdio: 'pipe',
|
|
57
|
+
})
|
|
58
|
+
execSync('git config user.name Test', {
|
|
59
|
+
cwd: repoPath,
|
|
60
|
+
stdio: 'pipe',
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// Create local branches for all remote-tracking branches (bundle clones
|
|
64
|
+
// only create a local branch for HEAD — everything else becomes origin/*)
|
|
65
|
+
const localBranches = execSync(
|
|
66
|
+
'git branch --format="%(refname:short)"',
|
|
67
|
+
{ cwd: repoPath, encoding: 'utf-8', stdio: 'pipe' }
|
|
68
|
+
)
|
|
69
|
+
.trim()
|
|
70
|
+
.split('\n')
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
|
|
73
|
+
const remoteBranches = execSync(
|
|
74
|
+
'git branch -r --format="%(refname:lstrip=3)"',
|
|
75
|
+
{ cwd: repoPath, encoding: 'utf-8', stdio: 'pipe' }
|
|
76
|
+
)
|
|
77
|
+
.trim()
|
|
78
|
+
.split('\n')
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.filter(b => b !== 'HEAD' && !localBranches.includes(b))
|
|
81
|
+
|
|
82
|
+
for (const branch of remoteBranches) {
|
|
83
|
+
execSync(`git branch --track "${branch}" "origin/${branch}"`, {
|
|
84
|
+
cwd: repoPath,
|
|
85
|
+
stdio: 'pipe',
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const repo = new Repository(repoPath)
|
|
90
|
+
return { repoPath, repo }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Run a git command in the fixture repo.
|
|
95
|
+
*/
|
|
96
|
+
export function git(repoPath: string, args: string) {
|
|
97
|
+
execSync(`git ${args}`, { cwd: repoPath, stdio: 'pipe' })
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Clean up a fixture repo directory.
|
|
102
|
+
*/
|
|
103
|
+
export function cleanupFixtureRepo(repoPath: string) {
|
|
104
|
+
execSync(`rm -rf ${repoPath}`)
|
|
105
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
2
|
+
import { mkdtempSync, writeFileSync } from 'fs'
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
import { tmpdir } from 'os'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
import {
|
|
7
|
+
Repository, getStatus, getCommits, getBranches,
|
|
8
|
+
createCommit, createBranch, deleteLocalBranch, renameBranch,
|
|
9
|
+
getCurrentBranch, getRepositoryType, getAllTags,
|
|
10
|
+
} from '../index.js'
|
|
11
|
+
import {
|
|
12
|
+
setupFixtureRepo, cleanupFixtureRepo, git,
|
|
13
|
+
} from './fixture-helpers.js'
|
|
14
|
+
|
|
15
|
+
let repoPath: string
|
|
16
|
+
let repo: Repository
|
|
17
|
+
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
const fixture = setupFixtureRepo()
|
|
20
|
+
repoPath = fixture.repoPath
|
|
21
|
+
repo = fixture.repo
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('getRepositoryType', () => {
|
|
25
|
+
it('returns regular for a valid repo', async () => {
|
|
26
|
+
const type = await getRepositoryType(repoPath)
|
|
27
|
+
expect(type.kind).toBe('regular')
|
|
28
|
+
if (type.kind === 'regular') {
|
|
29
|
+
expect(type.topLevelWorkingDirectory).toBe(repoPath)
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('returns missing for a non-existent path', async () => {
|
|
34
|
+
const type = await getRepositoryType('/nonexistent/path')
|
|
35
|
+
expect(type.kind).toBe('missing')
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('getCurrentBranch', () => {
|
|
40
|
+
it('returns the current branch name', async () => {
|
|
41
|
+
const branch = await getCurrentBranch(repoPath)
|
|
42
|
+
expect(branch).toBe('main')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('returns undefined when HEAD is detached', async () => {
|
|
46
|
+
git(repoPath, 'checkout --detach')
|
|
47
|
+
const branch = await getCurrentBranch(repoPath)
|
|
48
|
+
expect(branch).toBeUndefined()
|
|
49
|
+
// Reset back to a branch for subsequent tests
|
|
50
|
+
git(repoPath, 'checkout main')
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('getStatus', () => {
|
|
55
|
+
it('returns a status with branch info', async () => {
|
|
56
|
+
const status = await getStatus(repo)
|
|
57
|
+
expect(status).toBeTruthy()
|
|
58
|
+
expect(status!.currentBranch).toBe('main')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('detects untracked files', async () => {
|
|
62
|
+
writeFileSync(join(repoPath, 'untracked.txt'), 'hello')
|
|
63
|
+
const status = await getStatus(repo)
|
|
64
|
+
expect(status!.workingDirectory.files.length).toBeGreaterThanOrEqual(1)
|
|
65
|
+
const untracked = status!.workingDirectory.files.find(
|
|
66
|
+
f => f.path === 'untracked.txt'
|
|
67
|
+
)
|
|
68
|
+
expect(untracked).toBeTruthy()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('returns null for a path that exists but is not a git repo', async () => {
|
|
72
|
+
const nonRepoPath = mkdtempSync(join(tmpdir(), 'gcctest-nonrepo-'))
|
|
73
|
+
const result = await getStatus(new Repository(nonRepoPath))
|
|
74
|
+
expect(result).toBeNull()
|
|
75
|
+
execSync(`rm -rf ${nonRepoPath}`)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
describe('getCommits', () => {
|
|
80
|
+
it('returns commits in order (fixture has 6 commits reachable from main)', async () => {
|
|
81
|
+
const commits = await getCommits(repo, 'HEAD', 10)
|
|
82
|
+
expect(commits.length).toBe(6)
|
|
83
|
+
expect(commits[0].sha).toBeTruthy()
|
|
84
|
+
expect(commits[0].author).toBeTruthy()
|
|
85
|
+
// The most recent commit should be the merge
|
|
86
|
+
expect(commits[0].summary).toBe('merge: feature/one into main')
|
|
87
|
+
// The initial commit should be the last one
|
|
88
|
+
expect(commits[5].summary).toBe('initial: add README and gitignore')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('returns commits for feature/two branch', async () => {
|
|
92
|
+
const commits = await getCommits(repo, 'feature/two', 10)
|
|
93
|
+
expect(commits.length).toBeGreaterThanOrEqual(1)
|
|
94
|
+
expect(commits[0].summary).toBe('feat: feature two')
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe('Branch operations', () => {
|
|
99
|
+
it('includes feature/one and feature/two', async () => {
|
|
100
|
+
const branches = await getBranches(repo)
|
|
101
|
+
const names = branches.map(b => b.nameWithoutRemote)
|
|
102
|
+
expect(names).toContain('main')
|
|
103
|
+
expect(names).toContain('feature/one')
|
|
104
|
+
expect(names).toContain('feature/two')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('creates, renames, and deletes a branch', async () => {
|
|
108
|
+
await createBranch(repo, 'test-feature', 'HEAD')
|
|
109
|
+
let branches = await getBranches(repo)
|
|
110
|
+
const names = branches.map(b => b.nameWithoutRemote)
|
|
111
|
+
expect(names).toContain('test-feature')
|
|
112
|
+
|
|
113
|
+
// Rename the branch
|
|
114
|
+
const featureBranch = branches.find(b => b.nameWithoutRemote === 'test-feature')!
|
|
115
|
+
await renameBranch(repo, featureBranch, 'test-feature-renamed')
|
|
116
|
+
branches = await getBranches(repo)
|
|
117
|
+
expect(branches.map(b => b.nameWithoutRemote)).toContain('test-feature-renamed')
|
|
118
|
+
expect(branches.map(b => b.nameWithoutRemote)).not.toContain('test-feature')
|
|
119
|
+
|
|
120
|
+
// Delete the branch
|
|
121
|
+
await deleteLocalBranch(repo, 'test-feature-renamed')
|
|
122
|
+
branches = await getBranches(repo)
|
|
123
|
+
expect(branches.map(b => b.nameWithoutRemote)).not.toContain('test-feature-renamed')
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
describe('createCommit', () => {
|
|
128
|
+
it('creates a commit with files', async () => {
|
|
129
|
+
writeFileSync(join(repoPath, 'commit-test.txt'), 'commit data')
|
|
130
|
+
git(repoPath, 'add commit-test.txt')
|
|
131
|
+
|
|
132
|
+
const status = await getStatus(repo)
|
|
133
|
+
const files = status!.workingDirectory.files.filter(
|
|
134
|
+
f => f.path === 'commit-test.txt'
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
const sha = await createCommit(repo, 'feat: add commit-test.txt', files)
|
|
138
|
+
expect(sha).toBeTruthy()
|
|
139
|
+
expect(sha.length).toBeGreaterThanOrEqual(7)
|
|
140
|
+
|
|
141
|
+
// Verify the commit exists
|
|
142
|
+
const commits = await getCommits(repo, 'HEAD', 5)
|
|
143
|
+
expect(commits[0].sha.startsWith(sha)).toBe(true)
|
|
144
|
+
expect(commits[0].summary).toBe('feat: add commit-test.txt')
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe('Tag operations', () => {
|
|
149
|
+
it('lists tags from the fixture', async () => {
|
|
150
|
+
const tags = await getAllTags(repo)
|
|
151
|
+
expect(tags.size).toBeGreaterThanOrEqual(1)
|
|
152
|
+
expect(tags.has('v0.1.0')).toBe(true)
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
afterAll(() => {
|
|
157
|
+
cleanupFixtureRepo(repoPath)
|
|
158
|
+
})
|
package/docs/skills/release.md
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: release
|
|
3
|
-
description: Automated release workflow for git-chopstick-core — version bump, changelog, build, validation, and npm publish.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Release: git-chopstick-core
|
|
7
|
-
|
|
8
|
-
> Loaded via `skill("release")`. Run this whenever you need to publish a new release.
|
|
9
|
-
|
|
10
|
-
## Process
|
|
11
|
-
|
|
12
|
-
### 1. Read current state
|
|
13
|
-
|
|
14
|
-
Read `package.json`, `CHANGELOG.md`, and run `git log --oneline -5` to understand what has changed since the last release.
|
|
15
|
-
|
|
16
|
-
### 2. Determine version bump
|
|
17
|
-
|
|
18
|
-
Check what has changed:
|
|
19
|
-
|
|
20
|
-
- **Breaking API changes** (removed exports, changed signatures) → minor bump (0.x.0)
|
|
21
|
-
- **New features** (new functions, new exports) → patch bump (0.0.x)
|
|
22
|
-
- **Bug fixes, docs, internal refactoring** → patch bump (0.0.x)
|
|
23
|
-
|
|
24
|
-
Current version: read from `package.json`. Bump accordingly.
|
|
25
|
-
|
|
26
|
-
### 3. Update `CHANGELOG.md`
|
|
27
|
-
|
|
28
|
-
Insert a new `## [VERSION] — YYYY-MM-DD` section at the top (below the `# Changelog` heading) with:
|
|
29
|
-
|
|
30
|
-
- `### Added` — new features, exports, helpers
|
|
31
|
-
- `### Changed` — behavioral changes, renames, config changes
|
|
32
|
-
- `### Fixed` — bug fixes
|
|
33
|
-
|
|
34
|
-
Then update the link references at the bottom:
|
|
35
|
-
|
|
36
|
-
- Add `[VERSION]: https://github.com/parkiyong/git-chopstick-core/releases/tag/vVERSION`
|
|
37
|
-
- Update `[Unreleased]: .../compare/vVERSION...HEAD`
|
|
38
|
-
|
|
39
|
-
### 4. Bump `package.json`
|
|
40
|
-
|
|
41
|
-
Update `"version"` field.
|
|
42
|
-
|
|
43
|
-
### 5. Build and validate
|
|
44
|
-
|
|
45
|
-
Run all of these (use parallel agents):
|
|
46
|
-
|
|
47
|
-
- `npm run typecheck` — must pass with zero errors
|
|
48
|
-
- `npm run build` — must produce a clean `dist/`
|
|
49
|
-
- `npm test` — all integration tests must pass
|
|
50
|
-
- `npm pack --dry-run` — confirm all of:
|
|
51
|
-
- `dist/` is included (expect ~351 files, ~700kB)
|
|
52
|
-
- `CHANGELOG.md` and `docs/` appear in the file list
|
|
53
|
-
- `package.json`, `README.md`, `LICENSE` are present (npm includes these automatically)
|
|
54
|
-
- No unexpected files are leaking through (check for test artifacts, `.ts` sources outside `dist/`)
|
|
55
|
-
|
|
56
|
-
If any fail, fix before proceeding.
|
|
57
|
-
|
|
58
|
-
### 6. Stage and commit
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
git add -A
|
|
62
|
-
git commit -m "vVERSION: <short summary of changes>"
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### 7. Push and publish
|
|
66
|
-
|
|
67
|
-
```
|
|
68
|
-
git push origin main
|
|
69
|
-
npm publish
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### 8. Verify
|
|
73
|
-
|
|
74
|
-
- Confirm npm shows the new version: `npm view git-chopstick-core version`
|
|
75
|
-
- Confirm the tag exists on GitHub: `git tag -l`
|
|
76
|
-
- Suggest followup tasks
|
|
77
|
-
|
|
78
|
-
## Notes
|
|
79
|
-
|
|
80
|
-
- `prepublishOnly` runs `npm run typecheck && npm run build` automatically, so no need to build before publish — but validate first anyway.
|
|
81
|
-
- If this is the first release in a session, run `setup-matt-pocock-skills` first to configure issue tracker context.
|