1+ import { confirm , input , select } from '@inquirer/prompts'
12import { Command , Flags } from '@oclif/core'
3+ import chalk from 'chalk'
4+ import { exec } from 'child_process'
25import * as fs from 'fs'
6+ import * as yaml from 'js-yaml'
37import * as path from 'path'
4- import { exec } from 'child_process'
58import { promisify } from 'util'
6- import { select , input , confirm } from '@inquirer/prompts'
7- import * as yaml from 'js-yaml'
8- import chalk from 'chalk'
99
1010const execAsync = promisify ( exec )
1111
@@ -14,32 +14,85 @@ interface SecretService {
1414}
1515
1616class AWSSecretService implements SecretService {
17- constructor ( private region : string , private prefixName : string ) { }
17+ constructor ( private region : string , private prefixName : string , private debug : boolean ) { }
1818
19- private async convertToJson ( filePath : string ) : Promise < string > {
20- const content = await fs . promises . readFile ( filePath , 'utf-8' )
21- const lines = content . split ( '\n' )
22- const jsonContent : Record < string , string > = { }
23-
24- for ( const line of lines ) {
25- if ( line . trim ( ) && ! line . startsWith ( '#' ) ) {
26- const [ key , ...valueParts ] = line . split ( ':' )
27- const value = valueParts . join ( ':' ) . trim ( )
28- jsonContent [ key . trim ( ) ] = value . replace ( / ^ " / , '' ) . replace ( / " $ / , '' )
19+ private async secretExists ( secretName : string ) : Promise < boolean > {
20+ const fullSecretName = `${ this . prefixName } /${ secretName } `
21+ try {
22+ await execAsync ( `aws secretsmanager describe-secret --secret-id "${ fullSecretName } " --region ${ this . region } ` )
23+ return true
24+ } catch ( error : any ) {
25+ if ( error . message . includes ( 'ResourceNotFoundException' ) ) {
26+ return false
2927 }
28+ throw error
3029 }
30+ }
31+
32+ private async createOrUpdateSecret ( content : Record < string , string > , secretName : string ) : Promise < void > {
33+ const fullSecretName = `${ this . prefixName } /${ secretName } `
34+ const jsonContent = JSON . stringify ( content )
35+ const escapedJsonContent = jsonContent . replace ( / ' / g, "'\\''" )
36+
37+ if ( await this . secretExists ( secretName ) ) {
38+ const shouldOverride = await confirm ( {
39+ message : chalk . yellow ( `Secret ${ fullSecretName } already exists. Do you want to override it?` ) ,
40+ default : false ,
41+ } )
42+
43+ if ( ! shouldOverride ) {
44+ console . log ( chalk . yellow ( `Skipping secret: ${ fullSecretName } ` ) )
45+ return
46+ }
3147
32- return JSON . stringify ( jsonContent )
48+ const command = `aws secretsmanager put-secret-value --secret-id "${ fullSecretName } " --secret-string '${ escapedJsonContent } ' --region ${ this . region } `
49+ if ( this . debug ) {
50+ console . log ( chalk . yellow ( '--- Debug Output ---' ) )
51+ console . log ( chalk . cyan ( `Command: ${ command } ` ) )
52+ console . log ( chalk . yellow ( '-------------------' ) )
53+ }
54+ try {
55+ await execAsync ( command )
56+ console . log ( chalk . green ( `Successfully updated secret: ${ fullSecretName } ` ) )
57+ } catch ( error ) {
58+ console . error ( chalk . red ( `Failed to update secret: ${ fullSecretName } ` ) )
59+ console . error ( chalk . red ( `Error details: ${ error } ` ) )
60+ throw error
61+ }
62+ } else {
63+ const command = `aws secretsmanager create-secret --name "${ fullSecretName } " --secret-string '${ escapedJsonContent } ' --region ${ this . region } `
64+ if ( this . debug ) {
65+ console . log ( chalk . yellow ( '--- Debug Output ---' ) )
66+ console . log ( chalk . cyan ( `Command: ${ command } ` ) )
67+ console . log ( chalk . yellow ( '-------------------' ) )
68+ }
69+ try {
70+ await execAsync ( command )
71+ console . log ( chalk . green ( `Successfully created secret: ${ fullSecretName } ` ) )
72+ } catch ( error ) {
73+ console . error ( chalk . red ( `Failed to create secret: ${ fullSecretName } ` ) )
74+ console . error ( chalk . red ( `Error details: ${ error } ` ) )
75+ throw error
76+ }
77+ }
3378 }
3479
35- private async pushToAWSSecret ( content : string , secretName : string ) : Promise < void > {
36- const command = `aws secretsmanager create-secret --name "${ this . prefixName } /${ secretName } " --secret-string "${ JSON . stringify ( content ) . slice ( 1 , - 1 ) } " --region ${ this . region } `
37- try {
38- await execAsync ( command )
39- console . log ( chalk . green ( `Successfully pushed secret: ${ this . prefixName } /${ secretName } ` ) )
40- } catch ( error ) {
41- console . error ( chalk . red ( `Failed to push secret: ${ this . prefixName } /${ secretName } ` ) )
80+ private async convertEnvToDict ( filePath : string ) : Promise < Record < string , string > > {
81+ const content = await fs . promises . readFile ( filePath , 'utf-8' )
82+ const result : Record < string , string > = { }
83+
84+ const lines = content . split ( '\n' )
85+ for ( const line of lines ) {
86+ const match = line . match ( / ^ ( [ ^ = ] + ) = ( .* ) $ / )
87+ if ( match ) {
88+ const key = match [ 1 ] . trim ( )
89+ let value = match [ 2 ] . trim ( )
90+ value = value . replace ( / ^ [ " ' ] ( .* ) [ " ' ] $ / , '$1' )
91+ result [ key ] = value
92+ }
4293 }
94+
95+ return result
4396 }
4497
4598 async pushSecrets ( ) : Promise < void > {
@@ -51,7 +104,7 @@ class AWSSecretService implements SecretService {
51104 const secretName = path . basename ( file , '.json' )
52105 console . log ( chalk . cyan ( `Processing JSON secret: ${ secretName } ` ) )
53106 const content = await fs . promises . readFile ( path . join ( secretsDir , file ) , 'utf-8' )
54- await this . pushToAWSSecret ( content , secretName )
107+ await this . createOrUpdateSecret ( { 'migrate-db.json' : content } , secretName )
55108 }
56109
57110 // Process ENV files
@@ -62,20 +115,19 @@ class AWSSecretService implements SecretService {
62115 const secretName = path . basename ( file , '.env' )
63116 if ( secretName . startsWith ( 'l2-sequencer-' ) ) {
64117 console . log ( chalk . cyan ( `Processing L2 Sequencer secret: ${ secretName } ` ) )
65- const content = await this . convertToJson ( path . join ( secretsDir , file ) )
66- l2SequencerSecrets = { ...l2SequencerSecrets , ...JSON . parse ( content ) }
118+ const data = await this . convertEnvToDict ( path . join ( secretsDir , file ) )
119+ l2SequencerSecrets = { ...l2SequencerSecrets , ...data }
67120 } else {
68- console . log ( chalk . cyan ( `Processing ENV secret: ${ secretName } ` ) )
69- const content = await this . convertToJson ( path . join ( secretsDir , file ) )
70- await this . pushToAWSSecret ( content , secretName )
121+ console . log ( chalk . cyan ( `Processing ENV secret: ${ secretName } -env ` ) )
122+ const data = await this . convertEnvToDict ( path . join ( secretsDir , file ) )
123+ await this . createOrUpdateSecret ( data , ` ${ secretName } -env` )
71124 }
72125 }
73126
74127 // Push combined L2 Sequencer secrets
75128 if ( Object . keys ( l2SequencerSecrets ) . length > 0 ) {
76- console . log ( chalk . cyan ( `Processing combined L2 Sequencer secrets: l2-sequencer-secret` ) )
77- const combinedContent = JSON . stringify ( l2SequencerSecrets )
78- await this . pushToAWSSecret ( combinedContent , 'l2-sequencer-secret' )
129+ console . log ( chalk . cyan ( `Processing combined L2 Sequencer secrets: l2-sequencer-secret-env` ) )
130+ await this . createOrUpdateSecret ( l2SequencerSecrets , 'l2-sequencer-secret-env' )
79131 }
80132 }
81133}
@@ -321,7 +373,6 @@ export default class SetupPushSecrets extends Command {
321373 const valuesDir = path . join ( process . cwd ( ) , this . flags [ 'values-dir' ] ) ;
322374 if ( ! fs . existsSync ( valuesDir ) ) {
323375 this . error ( chalk . red ( `Values directory not found at ${ valuesDir } ` ) ) ;
324- return ;
325376 }
326377
327378 let credentials : Record < string , string > ;
@@ -422,7 +473,7 @@ export default class SetupPushSecrets extends Command {
422473
423474 if ( secretService === 'aws' ) {
424475 const awsCredentials = await this . getAWSCredentials ( )
425- service = new AWSSecretService ( awsCredentials . secretRegion , awsCredentials . prefixName )
476+ service = new AWSSecretService ( awsCredentials . secretRegion , awsCredentials . prefixName , flags . debug )
426477 provider = 'aws'
427478 } else if ( secretService === 'vault' ) {
428479 service = new HashicorpVaultDevService ( flags . debug )
@@ -451,4 +502,4 @@ export default class SetupPushSecrets extends Command {
451502 this . error ( chalk . red ( `Failed to push secrets: ${ error } ` ) )
452503 }
453504 }
454- }
505+ }
0 commit comments