@@ -2,6 +2,8 @@ import { existsSync } from 'node:fs';
22import { readFile , writeFile } from 'node:fs/promises' ;
33import path from 'node:path' ;
44
5+ export const DESKTOP_TAURI_CRATE_NAME = 'astrbot-desktop-tauri' ;
6+
57export const normalizeDesktopVersionOverride = ( version ) => {
68 const trimmed = typeof version === 'string' ? version . trim ( ) : '' ;
79 if ( ! trimmed ) {
@@ -47,10 +49,112 @@ export const readAstrbotVersionFromPyproject = async ({ sourceDir }) => {
4749 throw new Error ( `Cannot resolve [project].version from ${ pyprojectPath } ` ) ;
4850} ;
4951
52+ const escapeRegExp = ( value ) => value . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
53+ const CARGO_LOCK_PACKAGE_HEADER = / ^ \s * \[ \[ p a c k a g e \] \] \s * (?: # .* ) ? $ / ;
54+ const CARGO_LOCK_ANY_HEADER = / ^ \s * \[ \[ / ;
55+ const CARGO_LOCK_VERSION_LINE = / ^ \s * v e r s i o n \s * = / ;
56+ const escapeTomlBasicString = ( value ) => String ( value ) . replace ( / \\ / g, '\\\\' ) . replace ( / " / g, '\\"' ) ;
57+
58+ const updateVersionLine = ( line , version ) => {
59+ const commentIndex = line . indexOf ( '#' ) ;
60+ const beforeComment = commentIndex === - 1 ? line : line . slice ( 0 , commentIndex ) ;
61+ const comment = commentIndex === - 1 ? '' : line . slice ( commentIndex ) ;
62+ const separatorIndex = beforeComment . indexOf ( '=' ) ;
63+
64+ if ( separatorIndex === - 1 ) {
65+ return null ;
66+ }
67+
68+ const left = beforeComment . slice ( 0 , separatorIndex ) . trimEnd ( ) ;
69+ const right = beforeComment . slice ( separatorIndex + 1 ) ;
70+ if ( ! right . trim ( ) ) {
71+ return null ;
72+ }
73+
74+ const trailingWhitespace = beforeComment . match ( / \s * $ / u) ?. [ 0 ] ?? '' ;
75+ const updatedLine = `${ left } = "${ escapeTomlBasicString ( version ) } "` ;
76+
77+ if ( ! comment ) {
78+ return `${ updatedLine } ${ trailingWhitespace } ` ;
79+ }
80+
81+ return `${ updatedLine } ${ trailingWhitespace } ${ comment } ` ;
82+ } ;
83+
84+ const updateCargoLockPackageVersion = ( { cargoLock, packageName, version } ) => {
85+ const lines = cargoLock . split ( / \r ? \n / ) ;
86+ const newline = cargoLock . includes ( '\r\n' ) ? '\r\n' : '\n' ;
87+ const packageNameLinePattern = new RegExp (
88+ `^\\s*name\\s*=\\s*"${ escapeRegExp ( packageName ) } "\\s*(?:#.*)?$` ,
89+ ) ;
90+
91+ let inPackageBlock = false ;
92+ let inTargetPackage = false ;
93+ let foundTargetPackage = false ;
94+
95+ for ( let index = 0 ; index < lines . length ; index += 1 ) {
96+ const line = lines [ index ] ;
97+
98+ if ( CARGO_LOCK_PACKAGE_HEADER . test ( line ) ) {
99+ if ( inTargetPackage ) {
100+ throw new Error (
101+ `Cannot update Cargo.lock: version entry for package "${ packageName } " not found or has an unexpected layout` ,
102+ ) ;
103+ }
104+ inPackageBlock = true ;
105+ inTargetPackage = false ;
106+ continue ;
107+ }
108+
109+ if ( inPackageBlock && CARGO_LOCK_ANY_HEADER . test ( line ) ) {
110+ if ( inTargetPackage ) {
111+ throw new Error (
112+ `Cannot update Cargo.lock: version entry for package "${ packageName } " not found or has an unexpected layout` ,
113+ ) ;
114+ }
115+ inPackageBlock = false ;
116+ inTargetPackage = false ;
117+ }
118+
119+ if ( ! inPackageBlock ) {
120+ continue ;
121+ }
122+
123+ if ( ! inTargetPackage && packageNameLinePattern . test ( line ) ) {
124+ inTargetPackage = true ;
125+ foundTargetPackage = true ;
126+ continue ;
127+ }
128+
129+ if ( ! inTargetPackage || ! CARGO_LOCK_VERSION_LINE . test ( line ) ) {
130+ continue ;
131+ }
132+
133+ const updatedLine = updateVersionLine ( line , version ) ;
134+ if ( updatedLine === null ) {
135+ throw new Error (
136+ `Cannot update Cargo.lock: version entry for package "${ packageName } " not found or has an unexpected layout` ,
137+ ) ;
138+ }
139+
140+ lines [ index ] = updatedLine ;
141+ return { content : lines . join ( newline ) , updated : true , foundTargetPackage : true } ;
142+ }
143+
144+ if ( inTargetPackage ) {
145+ throw new Error (
146+ `Cannot update Cargo.lock: version entry for package "${ packageName } " not found or has an unexpected layout` ,
147+ ) ;
148+ }
149+
150+ return { content : cargoLock , updated : false , foundTargetPackage } ;
151+ } ;
152+
50153export const syncDesktopVersionFiles = async ( { projectRoot, version } ) => {
51154 const packageJsonPath = path . join ( projectRoot , 'package.json' ) ;
52155 const tauriConfigPath = path . join ( projectRoot , 'src-tauri' , 'tauri.conf.json' ) ;
53156 const cargoTomlPath = path . join ( projectRoot , 'src-tauri' , 'Cargo.toml' ) ;
157+ const cargoLockPath = path . join ( projectRoot , 'src-tauri' , 'Cargo.lock' ) ;
54158
55159 const packageJson = JSON . parse ( await readFile ( packageJsonPath , 'utf8' ) ) ;
56160 if ( packageJson . version !== version ) {
@@ -73,4 +177,21 @@ export const syncDesktopVersionFiles = async ({ projectRoot, version }) => {
73177 if ( updatedCargoToml !== cargoToml ) {
74178 await writeFile ( cargoTomlPath , updatedCargoToml , 'utf8' ) ;
75179 }
180+
181+ if ( existsSync ( cargoLockPath ) ) {
182+ const cargoLock = await readFile ( cargoLockPath , 'utf8' ) ;
183+ const { content : updatedCargoLock , updated, foundTargetPackage } = updateCargoLockPackageVersion ( {
184+ cargoLock,
185+ packageName : DESKTOP_TAURI_CRATE_NAME ,
186+ version,
187+ } ) ;
188+
189+ if ( ! foundTargetPackage ) {
190+ console . warn (
191+ `${ cargoLockPath } : package "${ DESKTOP_TAURI_CRATE_NAME } " not found. Skipping Cargo.lock version sync.` ,
192+ ) ;
193+ } else if ( updated && updatedCargoLock !== cargoLock ) {
194+ await writeFile ( cargoLockPath , updatedCargoLock , 'utf8' ) ;
195+ }
196+ }
76197} ;
0 commit comments