Skip to content

Commit e59d2f4

Browse files
authored
Merge pull request #153 from TrueNine/dev
Prepare CLI and MCP release pipeline
2 parents 0652241 + 2db1575 commit e59d2f4

476 files changed

Lines changed: 21256 additions & 64143 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.githooks/sync-versions.test.ts

Lines changed: 94 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -35,42 +35,80 @@ function createFixtureRepo(): string {
3535
name: '@truenine/memory-sync-cli',
3636
version: initialVersion
3737
})
38-
writeJson(join(rootDir, 'sdk', 'package.json'), {
39-
name: '@truenine/memory-sync-sdk',
40-
version: initialVersion,
41-
private: true
38+
writeJson(join(rootDir, 'gui', 'package.json'), {
39+
name: '@truenine/memory-sync-gui',
40+
version: initialVersion
4241
})
43-
writeJson(join(rootDir, 'cli-integration-test', 'package.json'), {
44-
name: '@truenine/memory-sync-cli-integration-test',
42+
writeJson(join(rootDir, 'doc', 'package.json'), {
43+
name: '@truenine/memory-sync-docs',
4544
version: initialVersion,
4645
private: true
4746
})
4847
writeJson(join(rootDir, 'cli', 'npm', 'darwin-arm64', 'package.json'), {
4948
name: '@truenine/memory-sync-cli-darwin-arm64',
5049
version: initialVersion
5150
})
52-
writeJson(join(rootDir, 'libraries', 'logger', 'package.json'), {
53-
name: '@truenine/logger',
51+
writeJson(join(rootDir, 'cli', 'npm', 'linux-x64-gnu', 'package.json'), {
52+
name: '@truenine/memory-sync-cli-linux-x64-gnu',
5453
version: initialVersion
5554
})
55+
5656
writeText(join(rootDir, 'Cargo.toml'), [
5757
'[workspace]',
58-
'members = ["cli-crate"]',
58+
'members = ["sdk", "cli", "mcp", "gui/src-tauri"]',
5959
'',
6060
'[workspace.package]',
6161
`version = "${initialVersion}"`,
6262
''
6363
].join('\n'))
64-
writeText(join(rootDir, 'cli-crate', 'Cargo.toml'), [
64+
writeText(join(rootDir, 'sdk', 'Cargo.toml'), [
65+
'[package]',
66+
'name = "tnmsd"',
67+
'version.workspace = true',
68+
''
69+
].join('\n'))
70+
writeText(join(rootDir, 'cli', 'Cargo.toml'), [
71+
'[package]',
72+
'name = "tnmsc"',
73+
'version.workspace = true',
74+
''
75+
].join('\n'))
76+
writeText(join(rootDir, 'mcp', 'Cargo.toml'), [
6577
'[package]',
66-
'name = "cli-crate"',
78+
'name = "tnmsm"',
79+
'version.workspace = true',
80+
''
81+
].join('\n'))
82+
writeText(join(rootDir, 'gui', 'src-tauri', 'Cargo.toml'), [
83+
'[package]',
84+
'name = "memory-sync-gui"',
6785
`version = "${initialVersion}"`,
6886
''
6987
].join('\n'))
7088
writeJson(join(rootDir, 'gui', 'src-tauri', 'tauri.conf.json'), {
7189
version: initialVersion,
7290
productName: 'Memory Sync'
7391
})
92+
writeText(join(rootDir, 'Cargo.lock'), [
93+
'version = 4',
94+
'',
95+
'[[package]]',
96+
'name = "tnmsd"',
97+
`version = "${initialVersion}"`,
98+
'',
99+
'[[package]]',
100+
'name = "tnmsc"',
101+
`version = "${initialVersion}"`,
102+
'',
103+
'[[package]]',
104+
'name = "tnmsm"',
105+
`version = "${initialVersion}"`,
106+
'',
107+
'[[package]]',
108+
'name = "memory-sync-gui"',
109+
`version = "${initialVersion}"`,
110+
''
111+
].join('\n'))
74112

75113
runGit(rootDir, ['init'])
76114
runGit(rootDir, ['config', 'user.email', 'codex@example.com'])
@@ -81,6 +119,19 @@ function createFixtureRepo(): string {
81119
return rootDir
82120
}
83121

122+
function expectSharedVersionSurfaces(rootDir: string, nextVersion: string): void {
123+
expect(JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
124+
expect(JSON.parse(readFileSync(join(rootDir, 'cli', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
125+
expect(JSON.parse(readFileSync(join(rootDir, 'gui', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
126+
expect(JSON.parse(readFileSync(join(rootDir, 'doc', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
127+
expect(JSON.parse(readFileSync(join(rootDir, 'cli', 'npm', 'darwin-arm64', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
128+
expect(JSON.parse(readFileSync(join(rootDir, 'cli', 'npm', 'linux-x64-gnu', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
129+
expect(readFileSync(join(rootDir, 'Cargo.toml'), 'utf-8')).toContain(`version = "${nextVersion}"`)
130+
expect(readFileSync(join(rootDir, 'gui', 'src-tauri', 'Cargo.toml'), 'utf-8')).toContain(`version = "${nextVersion}"`)
131+
expect(JSON.parse(readFileSync(join(rootDir, 'gui', 'src-tauri', 'tauri.conf.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
132+
expect(readFileSync(join(rootDir, 'Cargo.lock'), 'utf-8')).toContain(`version = "${nextVersion}"`)
133+
}
134+
84135
const tempDirs: string[] = []
85136

86137
afterEach(() => {
@@ -93,7 +144,7 @@ afterEach(() => {
93144
})
94145

95146
describe('sync-versions hook', () => {
96-
it('uses a staged package.json version as the sync source and stages all propagated changes', () => {
147+
it('uses a staged platform package version as the sync source and stages propagated changes', () => {
97148
const rootDir = createFixtureRepo()
98149
tempDirs.push(rootDir)
99150

@@ -109,101 +160,69 @@ describe('sync-versions hook', () => {
109160

110161
expect(result.targetVersion).toBe(nextVersion)
111162
expect(result.versionSource).toBe('cli/npm/darwin-arm64/package.json')
112-
expect(JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
113-
expect(JSON.parse(readFileSync(join(rootDir, 'cli', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
114-
expect(JSON.parse(readFileSync(join(rootDir, 'cli-integration-test', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
115-
expect(JSON.parse(readFileSync(join(rootDir, 'sdk', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
116-
expect(JSON.parse(readFileSync(join(rootDir, 'libraries', 'logger', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
117-
expect(readFileSync(join(rootDir, 'Cargo.toml'), 'utf-8')).toContain(`version = "${nextVersion}"`)
118-
expect(readFileSync(join(rootDir, 'cli-crate', 'Cargo.toml'), 'utf-8')).toContain(`version = "${nextVersion}"`)
119-
expect(JSON.parse(readFileSync(join(rootDir, 'gui', 'src-tauri', 'tauri.conf.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
163+
expectSharedVersionSurfaces(rootDir, nextVersion)
120164
expect(stagedFiles).toEqual(new Set([
165+
'Cargo.lock',
121166
'Cargo.toml',
122-
'cli-crate/Cargo.toml',
123-
'cli-integration-test/package.json',
124167
'cli/npm/darwin-arm64/package.json',
168+
'cli/npm/linux-x64-gnu/package.json',
125169
'cli/package.json',
170+
'doc/package.json',
171+
'gui/package.json',
172+
'gui/src-tauri/Cargo.toml',
126173
'gui/src-tauri/tauri.conf.json',
127-
'libraries/logger/package.json',
128-
'package.json',
129-
'sdk/package.json'
174+
'package.json'
130175
]))
131176
})
132177

133-
it('accepts sdk/package.json as a staged version source and propagates it', () => {
178+
it('accepts gui/package.json as a staged version source and propagates it', () => {
134179
const rootDir = createFixtureRepo()
135180
tempDirs.push(rootDir)
136181

137182
const nextVersion = '2026.10324.10316'
138-
writeJson(join(rootDir, 'sdk', 'package.json'), {
139-
name: '@truenine/memory-sync-sdk',
140-
version: nextVersion,
141-
private: true
183+
writeJson(join(rootDir, 'gui', 'package.json'), {
184+
name: '@truenine/memory-sync-gui',
185+
version: nextVersion
142186
})
143-
runGit(rootDir, ['add', 'sdk/package.json'])
187+
runGit(rootDir, ['add', 'gui/package.json'])
144188

145189
const result = runSyncVersions({rootDir})
146190
const stagedFiles = new Set(runGit(rootDir, ['diff', '--cached', '--name-only']).split(/\r?\n/).filter(Boolean))
147191

148192
expect(result.targetVersion).toBe(nextVersion)
149-
expect(result.versionSource).toBe('sdk/package.json')
150-
expect(JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
151-
expect(JSON.parse(readFileSync(join(rootDir, 'cli', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
152-
expect(JSON.parse(readFileSync(join(rootDir, 'cli-integration-test', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
153-
expect(JSON.parse(readFileSync(join(rootDir, 'sdk', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
154-
expect(JSON.parse(readFileSync(join(rootDir, 'libraries', 'logger', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
155-
expect(readFileSync(join(rootDir, 'Cargo.toml'), 'utf-8')).toContain(`version = "${nextVersion}"`)
156-
expect(readFileSync(join(rootDir, 'cli-crate', 'Cargo.toml'), 'utf-8')).toContain(`version = "${nextVersion}"`)
157-
expect(JSON.parse(readFileSync(join(rootDir, 'gui', 'src-tauri', 'tauri.conf.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
193+
expect(result.versionSource).toBe('gui/package.json')
194+
expectSharedVersionSurfaces(rootDir, nextVersion)
158195
expect(stagedFiles).toEqual(new Set([
196+
'Cargo.lock',
159197
'Cargo.toml',
160-
'cli-crate/Cargo.toml',
161-
'cli-integration-test/package.json',
162198
'cli/npm/darwin-arm64/package.json',
199+
'cli/npm/linux-x64-gnu/package.json',
163200
'cli/package.json',
201+
'doc/package.json',
202+
'gui/package.json',
203+
'gui/src-tauri/Cargo.toml',
164204
'gui/src-tauri/tauri.conf.json',
165-
'libraries/logger/package.json',
166-
'package.json',
167-
'sdk/package.json'
205+
'package.json'
168206
]))
169207
})
170208

171-
it('accepts cli-integration-test/package.json as a staged version source and propagates it', () => {
209+
it('accepts doc/package.json as a staged version source and propagates it', () => {
172210
const rootDir = createFixtureRepo()
173211
tempDirs.push(rootDir)
174212

175213
const nextVersion = '2026.10324.10318'
176-
writeJson(join(rootDir, 'cli-integration-test', 'package.json'), {
177-
name: '@truenine/memory-sync-cli-integration-test',
214+
writeJson(join(rootDir, 'doc', 'package.json'), {
215+
name: '@truenine/memory-sync-docs',
178216
version: nextVersion,
179217
private: true
180218
})
181-
runGit(rootDir, ['add', 'cli-integration-test/package.json'])
219+
runGit(rootDir, ['add', 'doc/package.json'])
182220

183221
const result = runSyncVersions({rootDir})
184-
const stagedFiles = new Set(runGit(rootDir, ['diff', '--cached', '--name-only']).split(/\r?\n/).filter(Boolean))
185222

186223
expect(result.targetVersion).toBe(nextVersion)
187-
expect(result.versionSource).toBe('cli-integration-test/package.json')
188-
expect(JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
189-
expect(JSON.parse(readFileSync(join(rootDir, 'cli', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
190-
expect(JSON.parse(readFileSync(join(rootDir, 'cli-integration-test', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
191-
expect(JSON.parse(readFileSync(join(rootDir, 'sdk', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
192-
expect(JSON.parse(readFileSync(join(rootDir, 'libraries', 'logger', 'package.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
193-
expect(readFileSync(join(rootDir, 'Cargo.toml'), 'utf-8')).toContain(`version = "${nextVersion}"`)
194-
expect(readFileSync(join(rootDir, 'cli-crate', 'Cargo.toml'), 'utf-8')).toContain(`version = "${nextVersion}"`)
195-
expect(JSON.parse(readFileSync(join(rootDir, 'gui', 'src-tauri', 'tauri.conf.json'), 'utf-8')) as {version: string}).toMatchObject({version: nextVersion})
196-
expect(stagedFiles).toEqual(new Set([
197-
'Cargo.toml',
198-
'cli-crate/Cargo.toml',
199-
'cli-integration-test/package.json',
200-
'cli/npm/darwin-arm64/package.json',
201-
'cli/package.json',
202-
'gui/src-tauri/tauri.conf.json',
203-
'libraries/logger/package.json',
204-
'package.json',
205-
'sdk/package.json'
206-
]))
224+
expect(result.versionSource).toBe('doc/package.json')
225+
expectSharedVersionSurfaces(rootDir, nextVersion)
207226
})
208227

209228
it('fails when staged package.json files propose conflicting versions', () => {
@@ -214,11 +233,12 @@ describe('sync-versions hook', () => {
214233
name: '@truenine/memory-sync-cli-darwin-arm64',
215234
version: '2026.10324.10314'
216235
})
217-
writeJson(join(rootDir, 'libraries', 'logger', 'package.json'), {
218-
name: '@truenine/logger',
219-
version: '2026.10324.10315'
236+
writeJson(join(rootDir, 'doc', 'package.json'), {
237+
name: '@truenine/memory-sync-docs',
238+
version: '2026.10324.10315',
239+
private: true
220240
})
221-
runGit(rootDir, ['add', 'cli/npm/darwin-arm64/package.json', 'libraries/logger/package.json'])
241+
runGit(rootDir, ['add', 'cli/npm/darwin-arm64/package.json', 'doc/package.json'])
222242

223243
expect(() => runSyncVersions({rootDir})).toThrowError(/Conflicting staged package\.json versions detected/)
224244
})

.githooks/sync-versions.ts

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -290,21 +290,37 @@ function syncCargoLockVersion(
290290
}
291291
}
292292

293-
function getStagedPackageVersionCandidates(rootDir: string, rootVersion: string): Map<string, string[]> {
293+
function getStagedVersionCandidates(rootDir: string, rootVersion: string): Map<string, string[]> {
294294
const stagedFiles = runGit(rootDir, ['diff', '--cached', '--name-only', '--diff-filter=ACMR'])
295295
.split(/\r?\n/)
296296
.map(line => line.trim())
297297
.filter(line => line.length > 0)
298-
.filter(filePath => basename(filePath) === 'package.json')
298+
.filter(filePath => {
299+
const name = basename(filePath)
300+
return name === 'package.json' || name === 'Cargo.toml' || name === 'tauri.conf.json'
301+
})
299302

300303
const candidates = new Map<string, string[]>()
301304

302305
for (const relativePath of stagedFiles) {
303306
const stagedContent = runGit(rootDir, ['show', `:${relativePath}`])
304-
const json = JSON.parse(stagedContent.replace(/^\uFEFF/, '')) as VersionedJson
305-
const version = typeof json.version === 'string' ? json.version.trim() : ''
307+
const name = basename(relativePath)
308+
309+
let version: string | undefined
310+
if (name === 'package.json' || name === 'tauri.conf.json') {
311+
const json = JSON.parse(stagedContent.replace(/^\uFEFF/, '')) as VersionedJson
312+
version = typeof json.version === 'string' ? json.version.trim() : ''
313+
} else if (name === 'Cargo.toml') {
314+
version = extractTomlSectionValue(stagedContent, 'workspace.package', 'version')
315+
if (version == null) {
316+
version = extractTomlSectionValue(stagedContent, 'package', 'version')
317+
}
318+
if (version != null) {
319+
version = version.trim()
320+
}
321+
}
306322

307-
if (version.length === 0 || version === rootVersion) {
323+
if (version == null || version === '' || version === rootVersion) {
308324
continue
309325
}
310326

@@ -335,7 +351,7 @@ function resolveTargetVersion(
335351
}
336352
}
337353

338-
const candidates = getStagedPackageVersionCandidates(rootDir, rootVersion)
354+
const candidates = getStagedVersionCandidates(rootDir, rootVersion)
339355
const versions = [...candidates.keys()]
340356

341357
if (versions.length === 0) {
@@ -376,6 +392,16 @@ export function runSyncVersions(options: SyncVersionsOptions = {}): SyncVersions
376392
const rootPackagePath = resolve(rootDir, 'package.json')
377393
const rootCargoPath = resolve(rootDir, 'Cargo.toml')
378394
const cargoLockPath = resolve(rootDir, 'Cargo.lock')
395+
396+
const rootCargoContent = readFileSync(rootCargoPath, 'utf-8')
397+
const cargoVersion = extractTomlSectionValue(rootCargoContent, 'workspace.package', 'version')
398+
399+
if (cargoVersion == null || cargoVersion === '') {
400+
throw new Error('Root Cargo.toml missing workspace.package.version field')
401+
}
402+
403+
validateVersion(cargoVersion, 'root Cargo.toml workspace.package.version')
404+
379405
const rootPkg = readJsonFile(rootPackagePath)
380406
const currentRootVersion = typeof rootPkg.version === 'string' ? rootPkg.version.trim() : ''
381407

@@ -385,15 +411,6 @@ export function runSyncVersions(options: SyncVersionsOptions = {}): SyncVersions
385411

386412
validateVersion(currentRootVersion, 'root package.json')
387413

388-
const target = resolveTargetVersion(rootDir, currentRootVersion, options.requestedVersion?.trim())
389-
const changedPaths = new Set<string>()
390-
391-
if (rootPkg.version !== target.version) {
392-
rootPkg.version = target.version
393-
writeJsonFile(rootPackagePath, rootPkg)
394-
changedPaths.add(rootPackagePath)
395-
}
396-
397414
const packageJsonPaths = discoverFilesByName(rootDir, 'package.json')
398415
.filter(filePath => resolve(filePath) !== rootPackagePath)
399416
.sort()

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
blank_issues_enabled: false
22
contact_links:
33
- name: Documentation and setup guide
4-
url: https://github.com/TrueNine/memory-sync/tree/main/doc
4+
url: https://memory-sync-docs.vercel.app/docs
55
about: Read the docs and setup references before opening a support issue.
66
- name: Security policy
77
url: https://github.com/TrueNine/memory-sync/security/policy

.github/actions/check-gui-release-state/action.yml

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)