@@ -26,7 +26,7 @@ jest.unstable_mockModule('@actions/core', () => ({
2626 setOutput : jest . fn ( ) ,
2727 getInput : jest . fn ( ) ,
2828 getBooleanInput : jest . fn ( ) ,
29- getMultilineInput : jest . fn ( ) ,
29+ getMultilineInput : jest . fn ( ( ) => [ ] ) ,
3030 addPath : jest . fn ( ) ,
3131 exportVariable : jest . fn ( ) ,
3232 saveState : jest . fn ( ) ,
@@ -57,6 +57,9 @@ const {M2_DIR, MVN_SETTINGS_FILE, STATE_GPG_HOME} =
5757const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) ) ;
5858const m2Dir = path . join ( __dirname , M2_DIR ) ;
5959const settingsFile = path . join ( m2Dir , MVN_SETTINGS_FILE ) ;
60+ const credentials = ( id : string , username : string , password : string ) => [
61+ { id, usernameEnvVar : username , passwordEnvVar : password }
62+ ] ;
6063
6164describe ( 'auth tests' , ( ) => {
6265 let spyOSHomedir : any ;
@@ -73,6 +76,9 @@ describe('auth tests', () => {
7376
7477 afterEach ( ( ) => {
7578 ( core . getInput as jest . Mock ) . mockReset ( ) ;
79+ ( core . getMultilineInput as jest . Mock ) . mockReset ( ) ;
80+ ( core . getMultilineInput as jest . Mock ) . mockReturnValue ( [ ] ) ;
81+ ( core . warning as jest . Mock ) . mockReset ( ) ;
7682 ( core . exportVariable as jest . Mock ) . mockReset ( ) ;
7783 ( gpg . importKey as jest . Mock ) . mockReset ( ) ;
7884 ( gpg . removeGpgHome as jest . Mock ) . mockReset ( ) ;
@@ -100,9 +106,7 @@ describe('auth tests', () => {
100106 await io . rmRF ( altHome ) ; // ensure it doesn't already exist
101107
102108 await auth . createAuthenticationSettings (
103- id ,
104- username ,
105- password ,
109+ credentials ( id , username , password ) ,
106110 altHome ,
107111 true
108112 ) ;
@@ -113,7 +117,7 @@ describe('auth tests', () => {
113117 expect ( fs . existsSync ( altHome ) ) . toBe ( true ) ;
114118 expect ( fs . existsSync ( altSettingsFile ) ) . toBe ( true ) ;
115119 expect ( fs . readFileSync ( altSettingsFile , 'utf-8' ) ) . toEqual (
116- auth . generate ( id , username , password )
120+ auth . generate ( credentials ( id , username , password ) )
117121 ) ;
118122
119123 await io . rmRF ( altHome ) ;
@@ -125,17 +129,15 @@ describe('auth tests', () => {
125129 const password = 'TOKEN' ;
126130
127131 await auth . createAuthenticationSettings (
128- id ,
129- username ,
130- password ,
132+ credentials ( id , username , password ) ,
131133 m2Dir ,
132134 true
133135 ) ;
134136
135137 expect ( fs . existsSync ( m2Dir ) ) . toBe ( true ) ;
136138 expect ( fs . existsSync ( settingsFile ) ) . toBe ( true ) ;
137139 expect ( fs . readFileSync ( settingsFile , 'utf-8' ) ) . toEqual (
138- auth . generate ( id , username , password )
140+ auth . generate ( credentials ( id , username , password ) )
139141 ) ;
140142 } , 100000 ) ;
141143
@@ -146,9 +148,7 @@ describe('auth tests', () => {
146148 const gpgPassphrase = 'GPG' ;
147149
148150 await auth . createAuthenticationSettings (
149- id ,
150- username ,
151- password ,
151+ credentials ( id , username , password ) ,
152152 m2Dir ,
153153 true ,
154154 gpgPassphrase
@@ -157,7 +157,7 @@ describe('auth tests', () => {
157157 expect ( fs . existsSync ( m2Dir ) ) . toBe ( true ) ;
158158 expect ( fs . existsSync ( settingsFile ) ) . toBe ( true ) ;
159159 expect ( fs . readFileSync ( settingsFile , 'utf-8' ) ) . toEqual (
160- auth . generate ( id , username , password , gpgPassphrase )
160+ auth . generate ( credentials ( id , username , password ) , gpgPassphrase )
161161 ) ;
162162 } , 100000 ) ;
163163
@@ -218,17 +218,15 @@ describe('auth tests', () => {
218218 expect ( fs . existsSync ( settingsFile ) ) . toBe ( true ) ;
219219
220220 await auth . createAuthenticationSettings (
221- id ,
222- username ,
223- password ,
221+ credentials ( id , username , password ) ,
224222 m2Dir ,
225223 true
226224 ) ;
227225
228226 expect ( fs . existsSync ( m2Dir ) ) . toBe ( true ) ;
229227 expect ( fs . existsSync ( settingsFile ) ) . toBe ( true ) ;
230228 expect ( fs . readFileSync ( settingsFile , 'utf-8' ) ) . toEqual (
231- auth . generate ( id , username , password )
229+ auth . generate ( credentials ( id , username , password ) )
232230 ) ;
233231 } , 100000 ) ;
234232
@@ -243,9 +241,7 @@ describe('auth tests', () => {
243241 expect ( fs . existsSync ( settingsFile ) ) . toBe ( true ) ;
244242
245243 await auth . createAuthenticationSettings (
246- id ,
247- username ,
248- password ,
244+ credentials ( id , username , password ) ,
249245 m2Dir ,
250246 false
251247 ) ;
@@ -273,7 +269,9 @@ describe('auth tests', () => {
273269 </servers>
274270</settings>` ;
275271
276- expect ( auth . generate ( id , username , password ) ) . toEqual ( expectedSettings ) ;
272+ expect ( auth . generate ( credentials ( id , username , password ) ) ) . toEqual (
273+ expectedSettings
274+ ) ;
277275 } ) ;
278276
279277 it ( 'generates valid settings.xml with additional configuration' , ( ) => {
@@ -306,9 +304,9 @@ describe('auth tests', () => {
306304 </activeProfiles>
307305</settings>` ;
308306
309- expect ( auth . generate ( id , username , password , gpgPassphrase ) ) . toEqual (
310- expectedSettings
311- ) ;
307+ expect (
308+ auth . generate ( credentials ( id , username , password ) , gpgPassphrase )
309+ ) . toEqual ( expectedSettings ) ;
312310 } ) ;
313311
314312 it ( 'does not add a gpg profile when the passphrase env var is the maven-gpg-plugin default' , ( ) => {
@@ -330,9 +328,9 @@ describe('auth tests', () => {
330328 </servers>
331329</settings>` ;
332330
333- expect ( auth . generate ( id , username , password , gpgPassphrase ) ) . toEqual (
334- expectedSettings
335- ) ;
331+ expect (
332+ auth . generate ( credentials ( id , username , password ) , gpgPassphrase )
333+ ) . toEqual ( expectedSettings ) ;
336334 } ) ;
337335
338336 it ( 'escapes settings.xml values while preserving parsed semantics' , ( ) => {
@@ -341,7 +339,10 @@ describe('auth tests', () => {
341339 const password = `TOKEN&<>"'é` ;
342340 const gpgPassphrase = `GPG&<>"'é` ;
343341
344- const xml = auth . generate ( id , username , password , gpgPassphrase ) ;
342+ const xml = auth . generate (
343+ credentials ( id , username , password ) ,
344+ gpgPassphrase
345+ ) ;
345346 const parsed = parseXmlObject ( xml ) as any ;
346347
347348 expect ( parsed . settings . interactiveMode ) . toBe ( 'false' ) ;
@@ -352,6 +353,144 @@ describe('auth tests', () => {
352353 expect ( parsed . settings . activeProfiles . activeProfile ) . toBe ( 'setup-java-gpg' ) ;
353354 } ) ;
354355
356+ it ( 'generates ordered settings.xml entries for multiple servers' , ( ) => {
357+ const servers = [
358+ {
359+ id : 'releases' ,
360+ usernameEnvVar : 'RELEASES_USERNAME' ,
361+ passwordEnvVar : 'RELEASES_PASSWORD'
362+ } ,
363+ {
364+ id : 'snapshots' ,
365+ usernameEnvVar : 'SNAPSHOTS_USERNAME' ,
366+ passwordEnvVar : 'SNAPSHOTS_PASSWORD'
367+ }
368+ ] ;
369+
370+ const parsed = parseXmlObject ( auth . generate ( servers ) ) as any ;
371+
372+ expect ( parsed . settings . servers . server ) . toEqual ( [
373+ {
374+ id : 'releases' ,
375+ username : '${env.RELEASES_USERNAME}' ,
376+ password : '${env.RELEASES_PASSWORD}'
377+ } ,
378+ {
379+ id : 'snapshots' ,
380+ username : '${env.SNAPSHOTS_USERNAME}' ,
381+ password : '${env.SNAPSHOTS_PASSWORD}'
382+ }
383+ ] ) ;
384+ } ) ;
385+
386+ it ( 'parses and trims multiline Maven server credentials' , ( ) => {
387+ expect (
388+ auth . parseMavenServerCredentials ( [
389+ '' ,
390+ ' releases : RELEASES_USERNAME : RELEASES_PASSWORD ' ,
391+ 'snapshots:SNAPSHOTS_USERNAME:SNAPSHOTS_PASSWORD'
392+ ] )
393+ ) . toEqual ( [
394+ {
395+ id : 'releases' ,
396+ usernameEnvVar : 'RELEASES_USERNAME' ,
397+ passwordEnvVar : 'RELEASES_PASSWORD'
398+ } ,
399+ {
400+ id : 'snapshots' ,
401+ usernameEnvVar : 'SNAPSHOTS_USERNAME' ,
402+ passwordEnvVar : 'SNAPSHOTS_PASSWORD'
403+ }
404+ ] ) ;
405+ } ) ;
406+
407+ it . each ( [
408+ {
409+ entries : [ 'releases:RELEASES_USERNAME' ] ,
410+ error :
411+ 'Invalid mvn-server-credentials entry at line 1. Expected format: server-id:USERNAME_ENV:PASSWORD_ENV'
412+ } ,
413+ {
414+ entries : [ '' , 'releases:RELEASES_USERNAME:RELEASES_PASSWORD:EXTRA' ] ,
415+ error :
416+ 'Invalid mvn-server-credentials entry at line 2. Expected format: server-id:USERNAME_ENV:PASSWORD_ENV'
417+ } ,
418+ {
419+ entries : [ 'releases::RELEASES_PASSWORD' ] ,
420+ error :
421+ 'Invalid mvn-server-credentials entry at line 1. server-id, username environment variable, and password environment variable are required'
422+ }
423+ ] ) (
424+ 'rejects malformed Maven server credentials: $entries' ,
425+ ( { entries, error} ) => {
426+ expect ( ( ) => auth . parseMavenServerCredentials ( entries ) ) . toThrow ( error ) ;
427+ }
428+ ) ;
429+
430+ it ( 'rejects duplicate Maven server ids' , ( ) => {
431+ expect ( ( ) =>
432+ auth . parseMavenServerCredentials ( [
433+ 'releases:RELEASES_USERNAME:RELEASES_PASSWORD' ,
434+ 'releases:OTHER_USERNAME:OTHER_PASSWORD'
435+ ] )
436+ ) . toThrow ( "Duplicate server-id 'releases' in mvn-server-credentials input" ) ;
437+ } ) ;
438+
439+ it ( 'uses multiline Maven server credentials instead of single-server inputs' , ( ) => {
440+ ( core . getMultilineInput as jest . Mock ) . mockReturnValue ( [
441+ 'releases:RELEASES_USERNAME:RELEASES_PASSWORD' ,
442+ 'snapshots:SNAPSHOTS_USERNAME:SNAPSHOTS_PASSWORD'
443+ ] ) ;
444+
445+ expect ( auth . getMavenServerSettings ( ) ) . toEqual ( [
446+ {
447+ id : 'releases' ,
448+ usernameEnvVar : 'RELEASES_USERNAME' ,
449+ passwordEnvVar : 'RELEASES_PASSWORD'
450+ } ,
451+ {
452+ id : 'snapshots' ,
453+ usernameEnvVar : 'SNAPSHOTS_USERNAME' ,
454+ passwordEnvVar : 'SNAPSHOTS_PASSWORD'
455+ }
456+ ] ) ;
457+ expect ( core . getInput ) . not . toHaveBeenCalled ( ) ;
458+ } ) ;
459+
460+ it ( 'uses the existing single-server inputs when multiline credentials are absent' , ( ) => {
461+ ( core . getInput as jest . Mock ) . mockImplementation ( ( name : string ) =>
462+ name === 'server-id' ? 'github' : ''
463+ ) ;
464+
465+ expect ( auth . getMavenServerSettings ( ) ) . toEqual ( [
466+ {
467+ id : 'github' ,
468+ usernameEnvVar : 'GITHUB_ACTOR' ,
469+ passwordEnvVar : 'GITHUB_TOKEN'
470+ }
471+ ] ) ;
472+ } ) ;
473+
474+ it ( 'preserves deprecated single-server aliases as fallback inputs' , ( ) => {
475+ ( core . getInput as jest . Mock ) . mockImplementation ( ( name : string ) => {
476+ const inputs : Record < string , string > = {
477+ 'server-id' : 'legacy' ,
478+ 'server-username' : 'LEGACY_USERNAME' ,
479+ 'server-password' : 'LEGACY_PASSWORD'
480+ } ;
481+ return inputs [ name ] ?? '' ;
482+ } ) ;
483+
484+ expect ( auth . getMavenServerSettings ( ) ) . toEqual ( [
485+ {
486+ id : 'legacy' ,
487+ usernameEnvVar : 'LEGACY_USERNAME' ,
488+ passwordEnvVar : 'LEGACY_PASSWORD'
489+ }
490+ ] ) ;
491+ expect ( core . warning ) . toHaveBeenCalledTimes ( 2 ) ;
492+ } ) ;
493+
355494 function xmlElementText ( xml : string , tagName : string ) : string {
356495 const match = new RegExp ( `<${ tagName } >([\\s\\S]*?)</${ tagName } >` ) . exec ( xml ) ;
357496 expect ( match ) . not . toBeNull ( ) ;
0 commit comments