dcdx 1.1.0 → 1.2.0-next.1

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.
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as e,Option as t}from"commander";import{gracefulExit as o,asyncExitHook as s}from"exit-hook";import{spawn as i}from"child_process";import{upAll as r}from"docker-compose";import{downAll as a,ps as n}from"docker-compose/dist/v2.js";import c from"events";import{dump as d}from"js-yaml";import{cwd as p}from"process";import{Sequelize as l,ConnectionError as h,TimeoutError as m,ConnectionTimedOutError as g,ConnectionRefusedError as S,ConnectionAcquireTimeoutError as u}from"sequelize";const w={name:"shared",driver:"bridge"};class Base extends c{options;sequelize=null;constructor(e){super(),this.options=e}async run(e,t){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(e,{logging:t})}catch(t){console.error("An error occurred while trying to run the following SQL query:",e,t),o()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const e=await this.getServiceState();e&&await this.showDockerLogs(e.name)}}else console.log(`Failed to start database ${this.name} ⛔`),o(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:w}}}async up(){const e=d(this.getDockerComposeConfig());return r({cwd:p(),configAsString:e,log:!0})}async down(){const e=d(this.getDockerComposeConfig());return a({cwd:p(),configAsString:e,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const e="mssql"===this.name?"master":this.options.database;return this.sequelize=new l(e,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[h,m,g,S,u],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((e=>(console.log(e),!1)))}catch(e){return!1}}async getServiceState(){const e=d(this.getDockerComposeConfig());return(await n({configAsString:e,log:!1,commandOptions:["--all"]})).data.services.find((e=>e.name.includes(this.name)))}async showDockerLogs(e){return new Promise(((t,o)=>{const s=i("docker",["logs","-f","-n","5000",e],{cwd:p()});s.stdout.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.stderr.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.on("exit",(e=>0===e?t():o(new Error(`Docker exited with code ${e}`))))}))}}const f={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends Base{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=f;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(e=f){super({...f,...e})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}(async()=>{const o=e.addOption(new t("-v, --version <version>","The version of Microsoft SQL Server").choices(["2017","2019","2022"]).default("2022")).addOption(new t("-e, --edition <edition>","The edition of Microsoft SQL Server").choices(["Developer","Express","Standard","Enterprise","EnterpriseCore"]).default("Developer")).addOption(new t("-p, --port <port>","The port on which the database will be accessible").default("1433")).addOption(new t("-P, --password <password>","The value passed to MSSQL_SA_PASSWORD environment variable. MS SQL Server password policy applies.").default("DataCenterDX!")).parse(process.argv).opts(),i=new MSSQL({version:o.version,edition:o.edition,port:Number(o.port),password:o.password,logging:!0});s((async()=>{console.log(`Stopping ${i.name}... ⏳`),await i.stop(),console.log(`Stopped ${i.name} 💪`)}),{wait:3e4}),await i.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),o()})),setInterval((()=>{}),1<<30);
2
+ import{program as e,Option as t}from"commander";import{gracefulExit as o,asyncExitHook as s}from"exit-hook";import{spawn as r}from"child_process";import{upAll as i}from"docker-compose";import{downAll as a,ps as n}from"docker-compose/dist/v2.js";import c from"events";import{dump as p}from"js-yaml";import{cwd as d}from"process";import{Sequelize as l,ConnectionError as h,TimeoutError as m,ConnectionTimedOutError as g,ConnectionRefusedError as S,ConnectionAcquireTimeoutError as w}from"sequelize";const u={name:"shared",driver:"bridge"};class Base extends c{options;sequelize=null;constructor(e){super(),this.options=e}async run(e,t){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(e,{logging:t})}catch(t){console.error("An error occurred while trying to run the following SQL query:",e,t),o()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const e=await this.getServiceState();e&&await this.showDockerLogs(e.name)}}else console.log(`Failed to start database ${this.name} ⛔`),o(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:u}}}async up(){const e=p(this.getDockerComposeConfig());return i({cwd:d(),configAsString:e,log:!0})}async down(){const e=p(this.getDockerComposeConfig());return a({cwd:d(),configAsString:e,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const e="mssql"===this.name?"master":this.options.database;return this.sequelize=new l(e,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[h,m,g,S,w],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((e=>(console.log(e),!1)))}catch(e){return!1}}async getServiceState(){const e=p(this.getDockerComposeConfig());return(await n({configAsString:e,log:!1,commandOptions:["--all"]})).data.services.find((e=>e.name.includes(this.name)))}async showDockerLogs(e){return new Promise(((t,o)=>{const s=r("docker",["logs","-f","-n","5000",e],{cwd:d()});s.stdout.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.stderr.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.on("exit",(e=>0===e?t():o(new Error(`Docker exited with code ${e}`))))}))}}const f={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends Base{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=f;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(e=f){super({...f,...e})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}(async()=>{const o=e.showHelpAfterError(!0).addOption(new t("-v, --version <version>","The version of Microsoft SQL Server").choices(["2017","2019","2022"]).default("2022")).addOption(new t("-e, --edition <edition>","The edition of Microsoft SQL Server").choices(["Developer","Express","Standard","Enterprise","EnterpriseCore"]).default("Developer")).addOption(new t("-p, --port <port>","The port on which the database will be accessible").default("1433")).addOption(new t("-P, --password <password>","The value passed to MSSQL_SA_PASSWORD environment variable. MS SQL Server password policy applies.").default("DataCenterDX!")).parse(process.argv).opts(),r=new MSSQL({version:o.version,edition:o.edition,port:Number(o.port),password:o.password,logging:!0});s((async()=>{console.log(`Stopping ${r.name}... ⏳`),await r.stop(),console.log(`Stopped ${r.name} 💪`)}),{wait:3e4}),await r.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),o()})),setInterval((()=>{}),1<<30);
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as e,Option as t}from"commander";import{gracefulExit as o,asyncExitHook as s}from"exit-hook";import{spawn as a}from"child_process";import{upAll as n}from"docker-compose";import{downAll as i,ps as r}from"docker-compose/dist/v2.js";import c from"events";import{dump as d}from"js-yaml";import{cwd as p}from"process";import{Sequelize as l,ConnectionError as m,TimeoutError as h,ConnectionTimedOutError as g,ConnectionRefusedError as u,ConnectionAcquireTimeoutError as w}from"sequelize";const f={name:"shared",driver:"bridge"};class Base extends c{options;sequelize=null;constructor(e){super(),this.options=e}async run(e,t){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(e,{logging:t})}catch(t){console.error("An error occurred while trying to run the following SQL query:",e,t),o()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const e=await this.getServiceState();e&&await this.showDockerLogs(e.name)}}else console.log(`Failed to start database ${this.name} ⛔`),o(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:f}}}async up(){const e=d(this.getDockerComposeConfig());return n({cwd:p(),configAsString:e,log:!0})}async down(){const e=d(this.getDockerComposeConfig());return i({cwd:p(),configAsString:e,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const e="mssql"===this.name?"master":this.options.database;return this.sequelize=new l(e,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[m,h,g,u,w],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((e=>(console.log(e),!1)))}catch(e){return!1}}async getServiceState(){const e=d(this.getDockerComposeConfig());return(await r({configAsString:e,log:!1,commandOptions:["--all"]})).data.services.find((e=>e.name.includes(this.name)))}async showDockerLogs(e){return new Promise(((t,o)=>{const s=a("docker",["logs","-f","-n","5000",e],{cwd:p()});s.stdout.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.stderr.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.on("exit",(e=>0===e?t():o(new Error(`Docker exited with code ${e}`))))}))}}const b={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends Base{name="mysql";driver="com.mysql.jdbc.Driver";options=b;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(e=b){super({...b,...e})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}(async()=>{const o=e.addOption(new t("-v, --version <version>","The version of Postgres").choices(["8.0","8.3"]).default("8.3")).addOption(new t("-d, --database <database>","The value passed to MYSQL_DATABASE environment variable").default("dcdx")).addOption(new t("-p, --port <port>","The port on which the database will be accessible").default("3306")).addOption(new t("-U, --username <username>","The value passed to MYSQL_USER environment variable").default("dcdx")).addOption(new t("-P, --password <password>","The value passed to MYSQL_PASSWORD environment variable").default("dcdx")).parse(process.argv).opts(),a=new MySQL({version:o.version,database:o.database,port:Number(o.port),username:o.username,password:o.password,logging:!0});s((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),o()})),setInterval((()=>{}),1<<30);
2
+ import{program as e,Option as t}from"commander";import{gracefulExit as o,asyncExitHook as s}from"exit-hook";import{spawn as a}from"child_process";import{upAll as n}from"docker-compose";import{downAll as i,ps as r}from"docker-compose/dist/v2.js";import c from"events";import{dump as d}from"js-yaml";import{cwd as p}from"process";import{Sequelize as l,ConnectionError as m,TimeoutError as h,ConnectionTimedOutError as g,ConnectionRefusedError as u,ConnectionAcquireTimeoutError as w}from"sequelize";const f={name:"shared",driver:"bridge"};class Base extends c{options;sequelize=null;constructor(e){super(),this.options=e}async run(e,t){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(e,{logging:t})}catch(t){console.error("An error occurred while trying to run the following SQL query:",e,t),o()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const e=await this.getServiceState();e&&await this.showDockerLogs(e.name)}}else console.log(`Failed to start database ${this.name} ⛔`),o(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:f}}}async up(){const e=d(this.getDockerComposeConfig());return n({cwd:p(),configAsString:e,log:!0})}async down(){const e=d(this.getDockerComposeConfig());return i({cwd:p(),configAsString:e,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const e="mssql"===this.name?"master":this.options.database;return this.sequelize=new l(e,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[m,h,g,u,w],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((e=>(console.log(e),!1)))}catch(e){return!1}}async getServiceState(){const e=d(this.getDockerComposeConfig());return(await r({configAsString:e,log:!1,commandOptions:["--all"]})).data.services.find((e=>e.name.includes(this.name)))}async showDockerLogs(e){return new Promise(((t,o)=>{const s=a("docker",["logs","-f","-n","5000",e],{cwd:p()});s.stdout.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.stderr.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.on("exit",(e=>0===e?t():o(new Error(`Docker exited with code ${e}`))))}))}}const b={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends Base{name="mysql";driver="com.mysql.jdbc.Driver";options=b;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(e=b){super({...b,...e})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}(async()=>{const o=e.showHelpAfterError(!0).addOption(new t("-v, --version <version>","The version of Postgres").choices(["8.0","8.3"]).default("8.3")).addOption(new t("-d, --database <database>","The value passed to MYSQL_DATABASE environment variable").default("dcdx")).addOption(new t("-p, --port <port>","The port on which the database will be accessible").default("3306")).addOption(new t("-U, --username <username>","The value passed to MYSQL_USER environment variable").default("dcdx")).addOption(new t("-P, --password <password>","The value passed to MYSQL_PASSWORD environment variable").default("dcdx")).parse(process.argv).opts(),a=new MySQL({version:o.version,database:o.database,port:Number(o.port),username:o.username,password:o.password,logging:!0});s((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),o()})),setInterval((()=>{}),1<<30);
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as e,Option as o}from"commander";import{gracefulExit as t,asyncExitHook as s}from"exit-hook";import{spawn as a}from"child_process";import{upAll as n}from"docker-compose";import{downAll as r,ps as i}from"docker-compose/dist/v2.js";import c from"events";import{dump as d}from"js-yaml";import{cwd as p}from"process";import{Sequelize as l,ConnectionError as g,TimeoutError as m,ConnectionTimedOutError as h,ConnectionRefusedError as u,ConnectionAcquireTimeoutError as w}from"sequelize";const S={name:"shared",driver:"bridge"};class Base extends c{options;sequelize=null;constructor(e){super(),this.options=e}async run(e,o){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(e,{logging:o})}catch(o){console.error("An error occurred while trying to run the following SQL query:",e,o),t()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const e=await this.getServiceState();e&&await this.showDockerLogs(e.name)}}else console.log(`Failed to start database ${this.name} ⛔`),t(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:S}}}async up(){const e=d(this.getDockerComposeConfig());return n({cwd:p(),configAsString:e,log:!0})}async down(){const e=d(this.getDockerComposeConfig());return r({cwd:p(),configAsString:e,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const e="mssql"===this.name?"master":this.options.database;return this.sequelize=new l(e,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[g,m,h,u,w],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((e=>(console.log(e),!1)))}catch(e){return!1}}async getServiceState(){const e=d(this.getDockerComposeConfig());return(await i({configAsString:e,log:!1,commandOptions:["--all"]})).data.services.find((e=>e.name.includes(this.name)))}async showDockerLogs(e){return new Promise(((o,t)=>{const s=a("docker",["logs","-f","-n","5000",e],{cwd:p()});s.stdout.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.stderr.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.on("exit",(e=>0===e?o():t(new Error(`Docker exited with code ${e}`))))}))}}const f={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends Base{name="postgresql";driver="org.postgresql.Driver";options=f;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(e=f){super({...f,...e})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}(async()=>{const t=e.addOption(new o("-v, --version <version>","The version of Postgres").choices(["12","13","14","15"]).default("15")).addOption(new o("-d, --database <database>","The value passed to POSTGRES_DB environment variable").default("dcdx")).addOption(new o("-p, --port <port>","The port on which the database will be accessible").default("5432")).addOption(new o("-U, --username <username>","The value passed to POSTGRES_USER environment variable").default("dcdx")).addOption(new o("-P, --password <password>","The value passed to POSTGRES_PASSWORD environment variable").default("dcdx")).parse(process.argv).opts(),a=new Postgres({version:t.version,database:t.database,port:Number(t.port),username:t.username,password:t.password,logging:!0});s((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),t()})),setInterval((()=>{}),1<<30);
2
+ import{program as e,Option as o}from"commander";import{gracefulExit as t,asyncExitHook as s}from"exit-hook";import{spawn as a}from"child_process";import{upAll as r}from"docker-compose";import{downAll as n,ps as i}from"docker-compose/dist/v2.js";import c from"events";import{dump as d}from"js-yaml";import{cwd as p}from"process";import{Sequelize as l,ConnectionError as g,TimeoutError as m,ConnectionTimedOutError as h,ConnectionRefusedError as u,ConnectionAcquireTimeoutError as w}from"sequelize";const S={name:"shared",driver:"bridge"};class Base extends c{options;sequelize=null;constructor(e){super(),this.options=e}async run(e,o){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(e,{logging:o})}catch(o){console.error("An error occurred while trying to run the following SQL query:",e,o),t()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const e=await this.getServiceState();e&&await this.showDockerLogs(e.name)}}else console.log(`Failed to start database ${this.name} ⛔`),t(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:S}}}async up(){const e=d(this.getDockerComposeConfig());return r({cwd:p(),configAsString:e,log:!0})}async down(){const e=d(this.getDockerComposeConfig());return n({cwd:p(),configAsString:e,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const e="mssql"===this.name?"master":this.options.database;return this.sequelize=new l(e,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[g,m,h,u,w],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((e=>(console.log(e),!1)))}catch(e){return!1}}async getServiceState(){const e=d(this.getDockerComposeConfig());return(await i({configAsString:e,log:!1,commandOptions:["--all"]})).data.services.find((e=>e.name.includes(this.name)))}async showDockerLogs(e){return new Promise(((o,t)=>{const s=a("docker",["logs","-f","-n","5000",e],{cwd:p()});s.stdout.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.stderr.on("data",(e=>{console.log(e.toString("utf-8").trim())})),s.on("exit",(e=>0===e?o():t(new Error(`Docker exited with code ${e}`))))}))}}const f={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends Base{name="postgresql";driver="org.postgresql.Driver";options=f;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(e=f){super({...f,...e})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}(async()=>{const t=e.showHelpAfterError(!0).addOption(new o("-v, --version <version>","The version of Postgres").choices(["12","13","14","15"]).default("15")).addOption(new o("-d, --database <database>","The value passed to POSTGRES_DB environment variable").default("dcdx")).addOption(new o("-p, --port <port>","The port on which the database will be accessible").default("5432")).addOption(new o("-U, --username <username>","The value passed to POSTGRES_USER environment variable").default("dcdx")).addOption(new o("-P, --password <password>","The value passed to POSTGRES_PASSWORD environment variable").default("dcdx")).parse(process.argv).opts(),a=new Postgres({version:t.version,database:t.database,port:Number(t.port),username:t.username,password:t.password,logging:!0});s((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),t()})),setInterval((()=>{}),1<<30);
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as a}from"commander";a.name("dcdx database").command("postgresql","Start PostgreSQL",{executableFile:"./database-postgres.js"}).command("mysql","Start MySQL",{executableFile:"./database-mysql.js"}).command("mssql","Start Microsoft SQL Server",{executableFile:"./database-mssql.js"}),a.parse();
2
+ import{program as e}from"commander";e.name("dcdx database").command("postgresql","Start PostgreSQL",{executableFile:"./database-postgres.js"}).command("mysql","Start MySQL",{executableFile:"./database-mysql.js"}).command("mssql","Start Microsoft SQL Server",{executableFile:"./database-mssql.js"}).showHelpAfterError(!0),e.parse();
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as t,Option as s}from"commander";import{gracefulExit as o,asyncExitHook as e}from"exit-hook";import a from"axios";import{dirname as i,join as n}from"path";import{spawn as r}from"child_process";import{downAll as c,ps as d,upAll as l}from"docker-compose/dist/v2.js";import p from"events";import{existsSync as h,mkdirSync as m}from"fs";import{dump as u}from"js-yaml";import{homedir as g}from"os";import{cwd as w}from"process";import b from"simple-git";import{upAll as S}from"docker-compose";import{Sequelize as v,ConnectionError as A,TimeoutError as f,ConnectionTimedOutError as E,ConnectionRefusedError as y,ConnectionAcquireTimeoutError as D}from"sequelize";const getFullPath=t=>{const[,s]=process.argv,o=i(s),e=o.substring(0,o.indexOf("dcdx")+4);return n(e,t.replaceAll("../",""))},T="AAAB3w0ODAoPeNp9Ultr2zAYffevEOwtoMR10qYLGJbYWsno7GA7g90eZPlrotWWjCSnzX79FF8IYWkfDPJ3Oedwzvch2zdoWSvkzpF7v5jdLrwpCkiaIc/1POeRMxAasmMNEa3Az2xnHT04gQJquBQhNeB77vQGuzPs3jk5rXIpx5QZfgDfqAaGUtRUOaj4adX+PkpGy+UOhNE+vnGG5laDags9L3mtuToOJF5Pkja5ZorXJwEXHG+pJQdaNvS/cYtLAysBVFfu19ehvxXPQr4IJwV1AGULKzIneBb+8PBd/GWOH6Yf751No9ieaujleYMHKYl8++Fb13VGo1EUZ/hznOBNEofbIFvHEd6mxDb81kQoUH5EZg+op0dEMFmAQrWSf4AZ9HNvTP1rMZnsrLGmpFpzKsZMVpOy28DQbfweo1AiIQ0quDaK540Bi8w1MhKxRhtZWXvHTghn+zLQBvU46Ela1rLZcYEKOEApazvvfKXceiSoYG/kcTXhTUlFG+XVbgKVNHA+gF7BN8t3UuU5sdpRwXUXGnmlVV0CCmRVU3G8Evf5MKUw9vqIFV36tX7moNjfo7CPTxfenZIJErLMSIhX309pvDd8SUgK3smKMpJsknVKBtr26t4D+gd3AC5qMCwCFHOaTWfFvLCLJPft3qktVo3FafmZAhRojb9IAxj8KEqPDQy0stos394SQQ==X02mq";var R;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(R||(R={}));const k={name:"shared",driver:"bridge"};let O=class Base extends p{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,s){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:s})}catch(s){console.error("An error occurred while trying to run the following SQL query:",t,s),o()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),o(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:k}}}async up(){const t=u(this.getDockerComposeConfig());return S({cwd:w(),configAsString:t,log:!0})}async down(){const t=u(this.getDockerComposeConfig());return c({cwd:w(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new v(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[A,f,E,y,D],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=u(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((s,o)=>{const e=r("docker",["logs","-f","-n","5000",t],{cwd:w()});e.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),e.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),e.on("exit",(t=>0===t?s():o(new Error(`Docker exited with code ${t}`))))}))}};const x={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends O{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=x;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=x){super({...x,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const C={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends O{name="mysql";driver="com.mysql.jdbc.Driver";options=C;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=C){super({...C,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const L={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends O{name="postgresql";driver="org.postgresql.Driver";options=L;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=L){super({...L,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const _=n(g(),".dcdx");class Base extends p{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async isApplicationReady(){try{const t=await a.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:s}=t;if("FIRST_RUN"===s.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:k}}}async up(){const t=this.getDockerComposeConfig(),s=u(t);await l({cwd:w(),configAsString:s,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),o(0)}async down(){const t=u(this.getDockerComposeConfig());await c({cwd:w(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=u(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const s=await this.getServiceState();return!!(s&&s.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),s&&await this.showDockerLogs(s.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const s=this.getDockerRepositoryUrl(),o=n(_,this.name,"source");h(o)?await b({baseDir:o}).pull({"--recurse-submodule":null}):(m(n(_,this.name),{recursive:!0}),await b().clone(s,o,{"--recurse-submodule":null})),await new Promise(((s,e)=>{const a=r("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:o});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((s,o)=>{const e=r("docker",["logs","-f","-n","5000",t],{cwd:w()});e.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),e.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),e.on("exit",(t=>0===t?s():o(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((s,o)=>{const e=r("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:w()});e.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),e.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),e.on("SIGINT",(()=>s())),e.on("exit",(t=>0===t?s():o(new Error(`Docker exited with code ${t}`))))}))}}class Bamboo extends Base{name=R.BAMBOO;database;logFilePath="/var/atlassian/application-data/bamboo/logs/atlassian-bamboo.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),s=this.getEnvironmentVariables();return{build:{context:getFullPath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bamboo/shared/plugins/quickreload-5.0.2.jar\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/bamboo/lib/mysql-connector-j-8.3.0.jar\nRUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo`},ports:[`${this.options.port||80}:8085`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(s).length>0?s:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};async isApplicationReady(){try{const t=await a.get(`${this.baseUrl}/setup/setupGeneralConfiguration.action`,{validateStatus:()=>!0}).catch((()=>null));return 200===t?.status}catch(t){return!1}}getEnvironmentVariables(){return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_BAMBOO_ENABLE_UNATTENDED_SETUP:"true",ATL_LICENSE:this.options.license||T,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_TYPE:`${this.database.name}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}(async()=>{const o=t.addOption(new s("-v, --version <version>","The version of the host application").choices(["9.4.3"]).default("9.4.3")).addOption(new s("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new s("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new s("-c, --contextPath <contextPath>","The context path on which the host application will be accessible")).addOption(new s("-qr, --quickReload <path_to_watch>","Add support for QuickReload and add the provided path to the watch list")).addOption(new s("--debug","Add support for JVM debugger on port 5005")).parse(process.argv).opts(),a=new Bamboo({version:o.version,database:o.database,port:Number(o.port),contextPath:o.contextPath,quickReload:o.qr,debug:o.debug});e((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),o()}));
2
+ import{program as t,Option as s}from"commander";import{gracefulExit as e,asyncExitHook as o}from"exit-hook";import a from"axios";import{dirname as i,join as n}from"path";import{spawn as r}from"child_process";import{downAll as c,ps as d,execCompose as p,upAll as l}from"docker-compose/dist/v2.js";import h from"events";import{existsSync as m,mkdirSync as u}from"fs";import{dump as g}from"js-yaml";import{homedir as w}from"os";import{cwd as b}from"process";import S from"simple-git";import{upAll as v}from"docker-compose";import{Sequelize as A,ConnectionError as f,TimeoutError as E,ConnectionTimedOutError as y,ConnectionRefusedError as D,ConnectionAcquireTimeoutError as T}from"sequelize";const k="AAAB3w0ODAoPeNp9Ultr2zAYffevEOwtoMR10qYLGJbYWsno7GA7g90eZPlrotWWjCSnzX79FF8IYWkfDPJ3Oedwzvch2zdoWSvkzpF7v5jdLrwpCkiaIc/1POeRMxAasmMNEa3Az2xnHT04gQJquBQhNeB77vQGuzPs3jk5rXIpx5QZfgDfqAaGUtRUOaj4adX+PkpGy+UOhNE+vnGG5laDags9L3mtuToOJF5Pkja5ZorXJwEXHG+pJQdaNvS/cYtLAysBVFfu19ehvxXPQr4IJwV1AGULKzIneBb+8PBd/GWOH6Yf751No9ieaujleYMHKYl8++Fb13VGo1EUZ/hznOBNEofbIFvHEd6mxDb81kQoUH5EZg+op0dEMFmAQrWSf4AZ9HNvTP1rMZnsrLGmpFpzKsZMVpOy28DQbfweo1AiIQ0quDaK540Bi8w1MhKxRhtZWXvHTghn+zLQBvU46Ela1rLZcYEKOEApazvvfKXceiSoYG/kcTXhTUlFG+XVbgKVNHA+gF7BN8t3UuU5sdpRwXUXGnmlVV0CCmRVU3G8Evf5MKUw9vqIFV36tX7moNjfo7CPTxfenZIJErLMSIhX309pvDd8SUgK3smKMpJsknVKBtr26t4D+gd3AC5qMCwCFHOaTWfFvLCLJPft3qktVo3FafmZAhRojb9IAxj8KEqPDQy0stos394SQQ==X02mq",toAbsolutePath=t=>{const[,s]=process.argv,e=i(s),o=e.substring(0,e.indexOf("dcdx")+4);return n(o,t.replaceAll("../",""))};var R;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(R||(R={}));const C={name:"shared",driver:"bridge"};let O=class Base extends h{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,s){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:s})}catch(s){console.error("An error occurred while trying to run the following SQL query:",t,s),e()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),e(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:C}}}async up(){const t=g(this.getDockerComposeConfig());return v({cwd:b(),configAsString:t,log:!0})}async down(){const t=g(this.getDockerComposeConfig());return c({cwd:b(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new A(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[f,E,y,D,T],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=g(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((s,e)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:b()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}};const x={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends O{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=x;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=x){super({...x,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const L={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends O{name="mysql";driver="com.mysql.jdbc.Driver";options=L;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=L){super({...L,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const q={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends O{name="postgresql";driver="org.postgresql.Driver";options=q;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=q){super({...q,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const _=n(w(),".dcdx");class Base extends h{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async cp(t){const s=await this.getServiceState();if(s&&s.state.toLowerCase().startsWith("up")){const s=this.getDockerComposeConfig(),e=g(s);await p("cp",[t,`${this.name}:/opt/quickreload/`],{cwd:b(),configAsString:e,log:!1})}}async isApplicationReady(){try{const t=await a.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:s}=t;if("FIRST_RUN"===s.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:C}}}async up(){const t=this.getDockerComposeConfig(),s=g(t);await l({cwd:b(),configAsString:s,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),e(0)}async down(){const t=g(this.getDockerComposeConfig());await c({cwd:b(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=g(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const s=await this.getServiceState();return!!(s&&s.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),s&&await this.showDockerLogs(s.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const s=this.getDockerRepositoryUrl(),e=n(_,this.name,"source");m(e)?await S({baseDir:e}).pull({"--recurse-submodule":null}):(u(n(_,this.name),{recursive:!0}),await S().clone(s,e,{"--recurse-submodule":null})),await new Promise(((s,o)=>{const a=r("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:e});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?s():o(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((s,e)=>{r("docker",["logs","-f","-n","5000",t],{cwd:b(),stdio:"inherit"}).on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((s,e)=>{r("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:b(),stdio:"inherit"}).on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}}class Bamboo extends Base{name=R.BAMBOO;database;logFilePath="/var/atlassian/application-data/bamboo/logs/atlassian-bamboo.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),s=this.getEnvironmentVariables();return{build:{context:toAbsolutePath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/bamboo/lib/mysql-connector-j-8.3.0.jar\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bamboo/shared/plugins/quickreload-5.0.2.jar\nRUN echo "/opt/quickreload" > /var/atlassian/application-data/bamboo/quickreload.properties; mkdir -p /opt/quickreload; chown -R bamboo:bamboo /opt/quickreload;\n\nRUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo`},ports:[`${this.options.port||80}:8085`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(s).length>0?s:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};async isApplicationReady(){try{const t=await a.get(`${this.baseUrl}/setup/setupGeneralConfiguration.action`,{validateStatus:()=>!0}).catch((()=>null));return 200===t?.status}catch(t){return!1}}getEnvironmentVariables(){return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_BAMBOO_ENABLE_UNATTENDED_SETUP:"true",ATL_LICENSE:this.options.license||k,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_TYPE:`${this.database.name}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}(async()=>{const e=t.showHelpAfterError(!0).addOption(new s("-v, --version <version>","The version of the host application").choices(["9.4.3"]).default("9.4.3")).addOption(new s("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new s("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new s("-c, --contextPath <contextPath>","The context path on which the host application will be accessible")).addOption(new s("-qr, --quickReload <path_to_watch>","Add support for QuickReload and add the provided path to the watch list")).addOption(new s("--debug","Add support for JVM debugger on port 5005")).parse(process.argv).opts(),a=new Bamboo({version:e.version,database:e.database,port:Number(e.port),contextPath:e.contextPath,quickReload:e.qr,debug:e.debug});o((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),e()}));
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as t,Option as s}from"commander";import{gracefulExit as e,asyncExitHook as o}from"exit-hook";import{dirname as a,join as i}from"path";import n from"axios";import{spawn as r}from"child_process";import{downAll as c,ps as d,upAll as l}from"docker-compose/dist/v2.js";import p from"events";import{existsSync as h,mkdirSync as u}from"fs";import{dump as m}from"js-yaml";import{homedir as g}from"os";import{cwd as b}from"process";import w from"simple-git";import{upAll as S}from"docker-compose";import{Sequelize as v,ConnectionError as f,TimeoutError as A,ConnectionTimedOutError as k,ConnectionRefusedError as R,ConnectionAcquireTimeoutError as y}from"sequelize";const getFullPath=t=>{const[,s]=process.argv,e=a(s),o=e.substring(0,e.indexOf("dcdx")+4);return i(o,t.replaceAll("../",""))},D="AAABmg0ODAoPeNp9Ul1v0zAUfc+vsMRbJadJtjGpUiS2JJRONOlaB4SAB9e9W0xTu7JvCv33eImDOoR4vefe4/PhN59hR5bckPiWRNezKJ5FCZkvGUmiJA4yrZALLPkBUrHXotEnu5eBRW6b8KMUoCyw8xH6BVZs2KKce3TJpUJQXAkofh2lOeccIV1dfRhJC7fRXrJuwJzALPL0/qF+pI9XGaPz+uGGspvbkdRx8Awcr0nRdOCnlXnmSlqOUqv0DltureTKg6vOiIZb6J9PoviaRm9pHHs0M9Cf/Rv1Dv/SP5lMyorR99WartZVXmdsUZW03hQOSHtCF+n2TLAB4hlIoYTegSFHo3+AQPK1QTx+m02nzzrko+JQ6MO0HS4oDBffQ5JrojSSnbRo5LZDcMzSEtREdBb1AYwNRzsXff0JgjCwLjG/4mB5giE+JzhbF3esyOn9lxf1F2145a6OWu2V/qmC/2dVdoctmOqptk5PGid+XJx42w3NPPHWQuD9fXJbL8PktfLhU2Av+N2rYMY6uUEFpncY/AYCgfFAMCwCFHzkBlkGH/vRdTeeBSfUMzUDb4W2AhQyM893WPg2Q8oQAYRdBSyKeVUdog==X02jr";var C;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(C||(C={}));const E={name:"shared",driver:"bridge"};let T=class Base extends p{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,s){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:s})}catch(s){console.error("An error occurred while trying to run the following SQL query:",t,s),e()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),e(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:E}}}async up(){const t=m(this.getDockerComposeConfig());return S({cwd:b(),configAsString:t,log:!0})}async down(){const t=m(this.getDockerComposeConfig());return c({cwd:b(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new v(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[f,A,k,R,y],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=m(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((s,e)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:b()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}};const O={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends T{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=O;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=O){super({...O,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const $={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends T{name="mysql";driver="com.mysql.jdbc.Driver";options=$;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=$){super({...$,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const x={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends T{name="postgresql";driver="org.postgresql.Driver";options=x;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=x){super({...x,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const q=i(g(),".dcdx");class Base extends p{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async isApplicationReady(){try{const t=await n.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:s}=t;if("FIRST_RUN"===s.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:E}}}async up(){const t=this.getDockerComposeConfig(),s=m(t);await l({cwd:b(),configAsString:s,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),e(0)}async down(){const t=m(this.getDockerComposeConfig());await c({cwd:b(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=m(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const s=await this.getServiceState();return!!(s&&s.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),s&&await this.showDockerLogs(s.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const s=this.getDockerRepositoryUrl(),e=i(q,this.name,"source");h(e)?await w({baseDir:e}).pull({"--recurse-submodule":null}):(u(i(q,this.name),{recursive:!0}),await w().clone(s,e,{"--recurse-submodule":null})),await new Promise(((s,o)=>{const a=r("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:e});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?s():o(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((s,e)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:b()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((s,e)=>{const o=r("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:b()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("SIGINT",(()=>s())),o.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}}class Bitbucket extends Base{name=C.BITBUCKET;database;logFilePath="/var/atlassian/application-data/bitbucket/log/atlassian-bitbucket.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),s=this.getEnvironmentVariables();return{build:{context:getFullPath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bitbucket/plugins/installed-plugins/quickreload-5.0.2.jar\nCOPY ./mysql-connector-j-8.3.0.jar /var/atlassian/application-data/bitbucket/lib/mysql-connector-j-8.3.0.jar\n\nRUN mkdir -p /var/atlassian/application-data/bitbucket/shared; touch /var/atlassian/application-data/bitbucket/shared/bitbucket.properties; echo "setup.license=${this.options.license||D}" >> /var/atlassian/application-data/bitbucket/shared/bitbucket.properties;\n\nRUN chown -R bitbucket:bitbucket /var/atlassian/application-data/bitbucket`},ports:[`${this.options.port||80}:7990`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(s).length>0?s:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){return{...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",JDBC_URL:this.database.url,JDBC_USER:this.database.options.username,JDBC_PASSWORD:this.database.options.password,JDBC_DRIVER:`${this.database.driver}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}(async()=>{const e=t.addOption(new s("-v, --version <version>","The version of the host application").choices(["8.9.0"]).default("8.9.0")).addOption(new s("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new s("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new s("-qr, --quickReload <path_to_watch>","Add support for QuickReload and add the provided path to the watch list")).addOption(new s("--debug","Add support for JVM debugger on port 5005")).parse(process.argv).opts(),a=new Bitbucket({version:e.version,database:e.database,port:Number(e.port),quickReload:e.qr,debug:e.debug});o((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),e()}));
2
+ import{program as t,Option as e}from"commander";import{gracefulExit as s,asyncExitHook as o}from"exit-hook";import{dirname as a,join as i}from"path";import n from"axios";import{spawn as r}from"child_process";import{downAll as c,ps as d,execCompose as p,upAll as l}from"docker-compose/dist/v2.js";import h from"events";import{existsSync as u,mkdirSync as m}from"fs";import{dump as g}from"js-yaml";import{homedir as b}from"os";import{cwd as w}from"process";import S from"simple-git";import{upAll as v}from"docker-compose";import{Sequelize as f,ConnectionError as k,TimeoutError as A,ConnectionTimedOutError as R,ConnectionRefusedError as y,ConnectionAcquireTimeoutError as D}from"sequelize";const C="AAABmg0ODAoPeNp9Ul1v0zAUfc+vsMRbJadJtjGpUiS2JJRONOlaB4SAB9e9W0xTu7JvCv33eImDOoR4vefe4/PhN59hR5bckPiWRNezKJ5FCZkvGUmiJA4yrZALLPkBUrHXotEnu5eBRW6b8KMUoCyw8xH6BVZs2KKce3TJpUJQXAkofh2lOeccIV1dfRhJC7fRXrJuwJzALPL0/qF+pI9XGaPz+uGGspvbkdRx8Awcr0nRdOCnlXnmSlqOUqv0DltureTKg6vOiIZb6J9PoviaRm9pHHs0M9Cf/Rv1Dv/SP5lMyorR99WartZVXmdsUZW03hQOSHtCF+n2TLAB4hlIoYTegSFHo3+AQPK1QTx+m02nzzrko+JQ6MO0HS4oDBffQ5JrojSSnbRo5LZDcMzSEtREdBb1AYwNRzsXff0JgjCwLjG/4mB5giE+JzhbF3esyOn9lxf1F2145a6OWu2V/qmC/2dVdoctmOqptk5PGid+XJx42w3NPPHWQuD9fXJbL8PktfLhU2Av+N2rYMY6uUEFpncY/AYCgfFAMCwCFHzkBlkGH/vRdTeeBSfUMzUDb4W2AhQyM893WPg2Q8oQAYRdBSyKeVUdog==X02jr",toAbsolutePath=t=>{const[,e]=process.argv,s=a(e),o=s.substring(0,s.indexOf("dcdx")+4);return i(o,t.replaceAll("../",""))};var E;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(E||(E={}));const O={name:"shared",driver:"bridge"};let T=class Base extends h{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,e){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:e})}catch(e){console.error("An error occurred while trying to run the following SQL query:",t,e),s()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),s(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:O}}}async up(){const t=g(this.getDockerComposeConfig());return v({cwd:w(),configAsString:t,log:!0})}async down(){const t=g(this.getDockerComposeConfig());return c({cwd:w(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new f(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[k,A,R,y,D],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=g(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((e,s)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:w()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}};const $={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends T{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=$;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=$){super({...$,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const q={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends T{name="mysql";driver="com.mysql.jdbc.Driver";options=q;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=q){super({...q,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const x={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends T{name="postgresql";driver="org.postgresql.Driver";options=x;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=x){super({...x,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const L=i(b(),".dcdx");class Base extends h{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async cp(t){const e=await this.getServiceState();if(e&&e.state.toLowerCase().startsWith("up")){const e=this.getDockerComposeConfig(),s=g(e);await p("cp",[t,`${this.name}:/opt/quickreload/`],{cwd:w(),configAsString:s,log:!1})}}async isApplicationReady(){try{const t=await n.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:e}=t;if("FIRST_RUN"===e.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:O}}}async up(){const t=this.getDockerComposeConfig(),e=g(t);await l({cwd:w(),configAsString:e,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),s(0)}async down(){const t=g(this.getDockerComposeConfig());await c({cwd:w(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=g(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const e=await this.getServiceState();return!!(e&&e.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),e&&await this.showDockerLogs(e.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const e=this.getDockerRepositoryUrl(),s=i(L,this.name,"source");u(s)?await S({baseDir:s}).pull({"--recurse-submodule":null}):(m(i(L,this.name),{recursive:!0}),await S().clone(e,s,{"--recurse-submodule":null})),await new Promise(((e,o)=>{const a=r("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:s});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?e():o(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((e,s)=>{r("docker",["logs","-f","-n","5000",t],{cwd:w(),stdio:"inherit"}).on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((e,s)=>{r("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:w(),stdio:"inherit"}).on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}}class Bitbucket extends Base{name=E.BITBUCKET;database;logFilePath="/var/atlassian/application-data/bitbucket/log/atlassian-bitbucket.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),e=this.getEnvironmentVariables();return{build:{context:toAbsolutePath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bitbucket/plugins/installed-plugins/quickreload-5.0.2.jar\nCOPY ./mysql-connector-j-8.3.0.jar /var/atlassian/application-data/bitbucket/lib/mysql-connector-j-8.3.0.jar\nRUN echo "/opt/quickreload" > /var/atlassian/application-data/bitbucket/quickreload.properties; mkdir -p /opt/quickreload; chown -R bitbucket:bitbucket /opt/quickreload;\n\nRUN mkdir -p /var/atlassian/application-data/bitbucket/shared; touch /var/atlassian/application-data/bitbucket/shared/bitbucket.properties; echo "setup.license=${this.options.license||C}" >> /var/atlassian/application-data/bitbucket/shared/bitbucket.properties;\n\nRUN chown -R bitbucket:bitbucket /var/atlassian/application-data/bitbucket`},ports:[`${this.options.port||80}:7990`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(e).length>0?e:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){return{...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",JDBC_URL:this.database.url,JDBC_USER:this.database.options.username,JDBC_PASSWORD:this.database.options.password,JDBC_DRIVER:`${this.database.driver}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}(async()=>{const s=t.showHelpAfterError(!0).addOption(new e("-v, --version <version>","The version of the host application").choices(["8.9.0"]).default("8.9.0")).addOption(new e("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new e("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new e("-qr, --quickReload <path_to_watch>","Add support for QuickReload and add the provided path to the watch list")).addOption(new e("--debug","Add support for JVM debugger on port 5005")).parse(process.argv).opts(),a=new Bitbucket({version:s.version,database:s.database,port:Number(s.port),quickReload:s.qr,debug:s.debug});o((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),s()}));
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as t,Option as e}from"commander";import{gracefulExit as s,asyncExitHook as o}from"exit-hook";import{dirname as a,join as i}from"path";import n from"axios";import{spawn as r}from"child_process";import{downAll as c,ps as d,upAll as l}from"docker-compose/dist/v2.js";import p from"events";import{existsSync as h,mkdirSync as m}from"fs";import{dump as u}from"js-yaml";import{homedir as g}from"os";import{cwd as w}from"process";import b from"simple-git";import{upAll as S}from"docker-compose";import{Sequelize as f,ConnectionError as A,TimeoutError as v,ConnectionTimedOutError as D,ConnectionRefusedError as y,ConnectionAcquireTimeoutError as E}from"sequelize";const getFullPath=t=>{const[,e]=process.argv,s=a(e),o=s.substring(0,s.indexOf("dcdx")+4);return i(o,t.replaceAll("../",""))},C="AAACzg0ODAoPeNp9VO9v2jAQ/Z6/wtK+IYWGsEKHFGmQmJau/GgIm9iYJhMOYprYke1A4a+fSchGKtqPPvvevXv3zp/6gqLHLEb2HbKsTvNzp9lC98MA2ZbdNKYgdiAGntPDQ2z688HcdH/aM7P5o2kZTzQEJiE4pDAiCTgBngaD0b0RCr5f1S9uHfcUWXSQBzuIeQrCCDlb10mo6A4cJTLQSVlIlzFUggpI8ickMbAVEbI+ypIliPG6R5Il5z4kXEF3A0xJp1Gywa8pFQePKHAmDdt6+A9cJVQEK5yWmhMV8HGZLRWkwrF8PZMgpGPfGh7IUNBUUc6cAKRCcVEYrblAaZxtKEOrsqj8V/US85zxXd+fUGwD70ickRwyf7ARACziqUYo+8Irmt/jUYD9iT+YYmNIKFPACAuvyHIaQEUSHYgz0G8rorgC8rp5qvaEZVp3pt0u8sve3TiTCsSIr0A6llGr1VwfdwPsmb25PjjbVwq2EcZcm6nS6DKX+I3iT1yP/Cy42TBe4FAK0WhZVtu6azYbZeZlB0U2wrpnkQoqq40Uyst67RqB9zU8k64olYeqKnGmNCzWiseOUoKwhLLoK1ExkZISVg95cr3ZN/YqdueSYW649/mdrfJAZOQM3b3b96DRuhVk3261kkFvexwe41UQtJN12u5Gz6GcPT7Pn71H70t69CPcbx/a9rcj3rDuwlk4xprKCA5VN+oJjsaB2R/75sQfezM3GIxH5myKT6PN/QErtDwgFQE6E9VDCLUZtN8F30Ko0K9IqXTRubnZ8HpFlZtzAyYUGb/ryOOIcYVWVCpBl5kCjUwlUhyF2mY80WaoXxdzEhOWe2YsNoRRWWxMt6xX/hH6P5uxF8b3rLJIlz1LpQW95pQPvqfLq0kmwohIeLs2l9PMzfQ48LvvWTbfMY1AXDg9KJDLCV2C9HUMH+CaJfOfudi/v0kIGVswLAIUEMQ9X77gqGBdfH0HIzBaV8NadPUCFBn+RiUA4D+IqVQpTTouLVY66aCGX02121";var T;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(T||(T={}));const R={name:"shared",driver:"bridge"};let k=class Base extends p{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,e){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:e})}catch(e){console.error("An error occurred while trying to run the following SQL query:",t,e),s()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),s(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:R}}}async up(){const t=u(this.getDockerComposeConfig());return S({cwd:w(),configAsString:t,log:!0})}async down(){const t=u(this.getDockerComposeConfig());return c({cwd:w(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new f(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[A,v,D,y,E],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=u(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((e,s)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:w()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}};const x={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends k{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=x;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=x){super({...x,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const L={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends k{name="mysql";driver="com.mysql.jdbc.Driver";options=L;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=L){super({...L,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const P={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends k{name="postgresql";driver="org.postgresql.Driver";options=P;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=P){super({...P,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const q=i(g(),".dcdx");class Base extends p{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async isApplicationReady(){try{const t=await n.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:e}=t;if("FIRST_RUN"===e.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:R}}}async up(){const t=this.getDockerComposeConfig(),e=u(t);await l({cwd:w(),configAsString:e,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),s(0)}async down(){const t=u(this.getDockerComposeConfig());await c({cwd:w(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=u(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const e=await this.getServiceState();return!!(e&&e.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),e&&await this.showDockerLogs(e.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const e=this.getDockerRepositoryUrl(),s=i(q,this.name,"source");h(s)?await b({baseDir:s}).pull({"--recurse-submodule":null}):(m(i(q,this.name),{recursive:!0}),await b().clone(e,s,{"--recurse-submodule":null})),await new Promise(((e,o)=>{const a=r("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:s});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?e():o(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((e,s)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:w()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((e,s)=>{const o=r("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:w()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("SIGINT",(()=>e())),o.on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}}class Confluence extends Base{name=T.CONFLUENCE;database;logFilePath="/var/atlassian/application-data/confluence/logs/atlassian-confluence.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),e=this.getEnvironmentVariables();return{build:{context:getFullPath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./quickreload-5.0.2.jar /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/quickreload-5.0.2.jar\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/confluence/confluence/WEB-INF/lib/mysql-connector-j-8.3.0.jar\nRUN chown -R confluence:confluence /opt/atlassian/confluence`},ports:[`${this.options.port||80}:8090`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(e).length>0?e:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_LICENSE_KEY:this.options.license||C,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_TYPE:`${this.database.name}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-Xrunjdwp:transport=dt_socket,address=*:5005,server=y,suspend=n"),t.push("-Dcom.sun.management.jmxremote.port=9999"),t.push("-Dcom.sun.management.jmxremote.rmi.port=9998"),t.push("-Dcom.sun.management.jmxremote.authenticate=false"),t.push("-Dcom.sun.management.jmxremote.ssl=false")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}(async()=>{const s=t.addOption(new e("-v, --version <version>","The version of the host application").choices(["8.9.0"]).default("8.9.0")).addOption(new e("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new e("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new e("-c, --contextPath <contextPath>","The context path on which the host application will be accessible")).addOption(new e("-qr, --quickReload <path_to_watch>","Add support for QuickReload and add the provided path to the watch list")).addOption(new e("--debug","Add support for JVM debugger on port 5005")).parse(process.argv).opts(),a=new Confluence({version:s.version,database:s.database,port:Number(s.port),contextPath:s.contextPath,quickReload:s.qr,debug:s.debug});o((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),s()}));
2
+ import{program as t,Option as e}from"commander";import{gracefulExit as s,asyncExitHook as o}from"exit-hook";import{dirname as a,join as i}from"path";import n from"axios";import{spawn as r}from"child_process";import{downAll as c,ps as d,execCompose as l,upAll as p}from"docker-compose/dist/v2.js";import h from"events";import{existsSync as u,mkdirSync as m}from"fs";import{dump as g}from"js-yaml";import{homedir as w}from"os";import{cwd as b}from"process";import S from"simple-git";import{upAll as f}from"docker-compose";import{Sequelize as A,ConnectionError as v,TimeoutError as D,ConnectionTimedOutError as y,ConnectionRefusedError as C,ConnectionAcquireTimeoutError as E}from"sequelize";const k="AAACzg0ODAoPeNp9VO9v2jAQ/Z6/wtK+IYWGsEKHFGmQmJau/GgIm9iYJhMOYprYke1A4a+fSchGKtqPPvvevXv3zp/6gqLHLEb2HbKsTvNzp9lC98MA2ZbdNKYgdiAGntPDQ2z688HcdH/aM7P5o2kZTzQEJiE4pDAiCTgBngaD0b0RCr5f1S9uHfcUWXSQBzuIeQrCCDlb10mo6A4cJTLQSVlIlzFUggpI8ickMbAVEbI+ypIliPG6R5Il5z4kXEF3A0xJp1Gywa8pFQePKHAmDdt6+A9cJVQEK5yWmhMV8HGZLRWkwrF8PZMgpGPfGh7IUNBUUc6cAKRCcVEYrblAaZxtKEOrsqj8V/US85zxXd+fUGwD70ickRwyf7ARACziqUYo+8Irmt/jUYD9iT+YYmNIKFPACAuvyHIaQEUSHYgz0G8rorgC8rp5qvaEZVp3pt0u8sve3TiTCsSIr0A6llGr1VwfdwPsmb25PjjbVwq2EcZcm6nS6DKX+I3iT1yP/Cy42TBe4FAK0WhZVtu6azYbZeZlB0U2wrpnkQoqq40Uyst67RqB9zU8k64olYeqKnGmNCzWiseOUoKwhLLoK1ExkZISVg95cr3ZN/YqdueSYW649/mdrfJAZOQM3b3b96DRuhVk3261kkFvexwe41UQtJN12u5Gz6GcPT7Pn71H70t69CPcbx/a9rcj3rDuwlk4xprKCA5VN+oJjsaB2R/75sQfezM3GIxH5myKT6PN/QErtDwgFQE6E9VDCLUZtN8F30Ko0K9IqXTRubnZ8HpFlZtzAyYUGb/ryOOIcYVWVCpBl5kCjUwlUhyF2mY80WaoXxdzEhOWe2YsNoRRWWxMt6xX/hH6P5uxF8b3rLJIlz1LpQW95pQPvqfLq0kmwohIeLs2l9PMzfQ48LvvWTbfMY1AXDg9KJDLCV2C9HUMH+CaJfOfudi/v0kIGVswLAIUEMQ9X77gqGBdfH0HIzBaV8NadPUCFBn+RiUA4D+IqVQpTTouLVY66aCGX02121",toAbsolutePath=t=>{const[,e]=process.argv,s=a(e),o=s.substring(0,s.indexOf("dcdx")+4);return i(o,t.replaceAll("../",""))};var T;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(T||(T={}));const R={name:"shared",driver:"bridge"};let x=class Base extends h{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,e){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:e})}catch(e){console.error("An error occurred while trying to run the following SQL query:",t,e),s()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),s(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:R}}}async up(){const t=g(this.getDockerComposeConfig());return f({cwd:b(),configAsString:t,log:!0})}async down(){const t=g(this.getDockerComposeConfig());return c({cwd:b(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new A(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[v,D,y,C,E],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=g(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((e,s)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:b()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}};const q={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends x{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=q;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=q){super({...q,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const L={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends x{name="mysql";driver="com.mysql.jdbc.Driver";options=L;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=L){super({...L,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const P={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends x{name="postgresql";driver="org.postgresql.Driver";options=P;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=P){super({...P,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const O=i(w(),".dcdx");class Base extends h{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async cp(t){const e=await this.getServiceState();if(e&&e.state.toLowerCase().startsWith("up")){const e=this.getDockerComposeConfig(),s=g(e);await l("cp",[t,`${this.name}:/opt/quickreload/`],{cwd:b(),configAsString:s,log:!1})}}async isApplicationReady(){try{const t=await n.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:e}=t;if("FIRST_RUN"===e.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:R}}}async up(){const t=this.getDockerComposeConfig(),e=g(t);await p({cwd:b(),configAsString:e,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),s(0)}async down(){const t=g(this.getDockerComposeConfig());await c({cwd:b(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=g(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const e=await this.getServiceState();return!!(e&&e.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),e&&await this.showDockerLogs(e.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const e=this.getDockerRepositoryUrl(),s=i(O,this.name,"source");u(s)?await S({baseDir:s}).pull({"--recurse-submodule":null}):(m(i(O,this.name),{recursive:!0}),await S().clone(e,s,{"--recurse-submodule":null})),await new Promise(((e,o)=>{const a=r("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:s});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?e():o(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((e,s)=>{r("docker",["logs","-f","-n","5000",t],{cwd:b(),stdio:"inherit"}).on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((e,s)=>{r("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:b(),stdio:"inherit"}).on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}}class Confluence extends Base{name=T.CONFLUENCE;database;logFilePath="/var/atlassian/application-data/confluence/logs/atlassian-confluence.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),e=this.getEnvironmentVariables();return{build:{context:toAbsolutePath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/confluence/confluence/WEB-INF/lib/mysql-connector-j-8.3.0.jar\nCOPY ./quickreload-5.0.2.jar /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/quickreload-5.0.2.jar\nRUN echo "/opt/quickreload" > /var/atlassian/application-data/confluence/quickreload.properties; mkdir -p /opt/quickreload; chown -R confluence:confluence /opt/quickreload;\n\nRUN chown -R confluence:confluence /opt/atlassian/confluence`},ports:[`${this.options.port||80}:8090`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(e).length>0?e:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_LICENSE_KEY:this.options.license||k,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_TYPE:`${this.database.name}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-Xrunjdwp:transport=dt_socket,address=*:5005,server=y,suspend=n"),t.push("-Dcom.sun.management.jmxremote.port=9999"),t.push("-Dcom.sun.management.jmxremote.rmi.port=9998"),t.push("-Dcom.sun.management.jmxremote.authenticate=false"),t.push("-Dcom.sun.management.jmxremote.ssl=false")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}(async()=>{const s=t.showHelpAfterError(!0).addOption(new e("-v, --version <version>","The version of the host application").choices(["8.9.0"]).default("8.9.0")).addOption(new e("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new e("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new e("-c, --contextPath <contextPath>","The context path on which the host application will be accessible")).addOption(new e("-qr, --quickReload <path_to_watch>","Add support for QuickReload and add the provided path to the watch list")).addOption(new e("--debug","Add support for JVM debugger on port 5005")).parse(process.argv).opts(),a=new Confluence({version:s.version,database:s.database,port:Number(s.port),contextPath:s.contextPath,quickReload:s.qr,debug:s.debug});o((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),s()}));
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as t,Option as s}from"commander";import{gracefulExit as e,asyncExitHook as o}from"exit-hook";import{dirname as a,join as i}from"path";import n from"axios";import{spawn as r}from"child_process";import{downAll as c,ps as d,upAll as l}from"docker-compose/dist/v2.js";import p from"events";import{existsSync as h,mkdirSync as u}from"fs";import{dump as m}from"js-yaml";import{homedir as g}from"os";import{cwd as w}from"process";import b from"simple-git";import{upAll as S}from"docker-compose";import{Sequelize as A,ConnectionError as v,TimeoutError as f,ConnectionTimedOutError as T,ConnectionRefusedError as y,ConnectionAcquireTimeoutError as E}from"sequelize";const getFullPath=t=>{const[,s]=process.argv,e=a(s),o=e.substring(0,e.indexOf("dcdx")+4);return i(o,t.replaceAll("../",""))},D="AAACBA0ODAoPeNp9kl1v2jAUhu/zK460OyQHSLuVIkUaTbwNVBKUhEnbugsTDuA2sSPbgbFfPxOC1i+4yEXO5+vnPR+yGiGSW/CuoXc7vBoMvT4ENAOv5/WctUIUG1lVqNx7nqPQSJfccCl8GmU0mSXjlDpRXS5Qxau5RqV90ncCKQzLTcRK9Eu944Ip/cQ/M1MwrTkTbi5LZ1arfMM0hsyg7/X6A9LvE89z2j3ZvsJmQBBPpzQJxqP7U4r+qbjaN32zG+/baR2dMl6c35ei2qIah/4dvaHkOvzpkU/x5IZ8vbodOI9cMbdSclnnxj38EC1XZscUunYy36JvVI3OxGZmbZXdzwIU5lVJWi90rnjVQGoi70B873GNhPOQO51OFGfkS5yQWRKH8yAbxxGZp9Qm/EChpbGExR7MBqGdAlTkcokK7MMeMTfwa2NM9TDsdtfSfQGnWxw7CB47frsQShDSwJJro/iiNmgncw1GQl5rI0trtetY4sKgYCJ/Y4qVFSR0lNGQ3P04aDxrTKvWOjMXT0LuxCU33txaSiPffuRjr3fsO/mC6og/VmsmuGYN0Ij93TFYcVXunQaaDb4+wJbFd7vg0OI5If53NENtoK2AlVQwGScjSFt5lrjdWymu8WEIgSwtppyzAsLgxRk8P5hG9PMA3bKiPspdsULjJRwX7ukf5/NX7TAtAhUAhQSHz9DgycOX7wcwERMJb3TLGb4CFGkB/xUWSRK3BZLrPF9Rn6ZmrUqJX02oc",R="AAACzg0ODAoPeNp9VO9v2jAQ/Z6/wtK+IYWGsEKHFGmQmJau/GgIm9iYJhMOYprYke1A4a+fSchGKtqPPvvevXv3zp/6gqLHLEb2HbKsTvNzp9lC98MA2ZbdNKYgdiAGntPDQ2z688HcdH/aM7P5o2kZTzQEJiE4pDAiCTgBngaD0b0RCr5f1S9uHfcUWXSQBzuIeQrCCDlb10mo6A4cJTLQSVlIlzFUggpI8ickMbAVEbI+ypIliPG6R5Il5z4kXEF3A0xJp1Gywa8pFQePKHAmDdt6+A9cJVQEK5yWmhMV8HGZLRWkwrF8PZMgpGPfGh7IUNBUUc6cAKRCcVEYrblAaZxtKEOrsqj8V/US85zxXd+fUGwD70ickRwyf7ARACziqUYo+8Irmt/jUYD9iT+YYmNIKFPACAuvyHIaQEUSHYgz0G8rorgC8rp5qvaEZVp3pt0u8sve3TiTCsSIr0A6llGr1VwfdwPsmb25PjjbVwq2EcZcm6nS6DKX+I3iT1yP/Cy42TBe4FAK0WhZVtu6azYbZeZlB0U2wrpnkQoqq40Uyst67RqB9zU8k64olYeqKnGmNCzWiseOUoKwhLLoK1ExkZISVg95cr3ZN/YqdueSYW649/mdrfJAZOQM3b3b96DRuhVk3261kkFvexwe41UQtJN12u5Gz6GcPT7Pn71H70t69CPcbx/a9rcj3rDuwlk4xprKCA5VN+oJjsaB2R/75sQfezM3GIxH5myKT6PN/QErtDwgFQE6E9VDCLUZtN8F30Ko0K9IqXTRubnZ8HpFlZtzAyYUGb/ryOOIcYVWVCpBl5kCjUwlUhyF2mY80WaoXxdzEhOWe2YsNoRRWWxMt6xX/hH6P5uxF8b3rLJIlz1LpQW95pQPvqfLq0kmwohIeLs2l9PMzfQ48LvvWTbfMY1AXDg9KJDLCV2C9HUMH+CaJfOfudi/v0kIGVswLAIUEMQ9X77gqGBdfH0HIzBaV8NadPUCFBn+RiUA4D+IqVQpTTouLVY66aCGX02121";var C;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(C||(C={}));const k={name:"shared",driver:"bridge"};let x=class Base extends p{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,s){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:s})}catch(s){console.error("An error occurred while trying to run the following SQL query:",t,s),e()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),e(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:k}}}async up(){const t=m(this.getDockerComposeConfig());return S({cwd:w(),configAsString:t,log:!0})}async down(){const t=m(this.getDockerComposeConfig());return c({cwd:w(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new A(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[v,f,T,y,E],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=m(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((s,e)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:w()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}};const P={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends x{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=P;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=P){super({...P,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const O={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends x{name="mysql";driver="com.mysql.jdbc.Driver";options=O;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=O){super({...O,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const L={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends x{name="postgresql";driver="org.postgresql.Driver";options=L;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=L){super({...L,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const q=i(g(),".dcdx");class Base extends p{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async isApplicationReady(){try{const t=await n.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:s}=t;if("FIRST_RUN"===s.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:k}}}async up(){const t=this.getDockerComposeConfig(),s=m(t);await l({cwd:w(),configAsString:s,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),e(0)}async down(){const t=m(this.getDockerComposeConfig());await c({cwd:w(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=m(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const s=await this.getServiceState();return!!(s&&s.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),s&&await this.showDockerLogs(s.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const s=this.getDockerRepositoryUrl(),e=i(q,this.name,"source");h(e)?await b({baseDir:e}).pull({"--recurse-submodule":null}):(u(i(q,this.name),{recursive:!0}),await b().clone(s,e,{"--recurse-submodule":null})),await new Promise(((s,o)=>{const a=r("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:e});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?s():o(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((s,e)=>{const o=r("docker",["logs","-f","-n","5000",t],{cwd:w()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((s,e)=>{const o=r("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:w()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("SIGINT",(()=>s())),o.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}}class Jira extends Base{name=C.JIRA;database;logFilePath="/var/atlassian/application-data/jira/log/atlassian-jira.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),s=this.getEnvironmentVariables();return{build:{context:getFullPath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./jira-data-generator-5.0.0.jar /var/atlassian/application-data/jira/plugins/installed-plugins/jira-data-generator-5.0.0.jar\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/jira/plugins/installed-plugins/quickreload-5.0.2.jar\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/jira/lib/mysql-connector-j-8.3.0.jar\nRUN chown -R jira:jira /var/atlassian/application-data/jira`},ports:[`${this.options.port||80}:8080`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(s).length>0?s:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){const t="postgresql"===this.database.name?"postgres72":this.database.name;return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_LICENSE_KEY:this.options.license||R,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_DRIVER:this.database.driver,ATL_DB_TYPE:t,JIRA_SETUP_LICENSE:this.options.license||D}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}(async()=>{const e=t.addOption(new s("-v, --version <version>","The version of the host application").choices(["9.15.0"]).default("9.15.0")).addOption(new s("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new s("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new s("-c, --contextPath <contextPath>","The context path on which the host application will be accessible")).addOption(new s("-qr, --quickReload <path_to_watch>","Add support for QuickReload and add the provided path to the watch list")).addOption(new s("--debug","Add support for JVM debugger on port 5005")).parse(process.argv).opts(),a=new Jira({version:e.version,database:e.database,port:Number(e.port),contextPath:e.contextPath,quickReload:e.qr,debug:e.debug});o((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),e()}));
2
+ import{program as t,Option as s}from"commander";import{gracefulExit as e,asyncExitHook as o}from"exit-hook";import{dirname as a,join as i}from"path";import r from"axios";import{spawn as n}from"child_process";import{downAll as c,ps as d,execCompose as l,upAll as p}from"docker-compose/dist/v2.js";import h from"events";import{existsSync as u,mkdirSync as m}from"fs";import{dump as g}from"js-yaml";import{homedir as w}from"os";import{cwd as b}from"process";import S from"simple-git";import{upAll as A}from"docker-compose";import{Sequelize as v,ConnectionError as f,TimeoutError as T,ConnectionTimedOutError as y,ConnectionRefusedError as E,ConnectionAcquireTimeoutError as R}from"sequelize";const C="AAACBA0ODAoPeNp9kl1v2jAUhu/zK460OyQHSLuVIkUaTbwNVBKUhEnbugsTDuA2sSPbgbFfPxOC1i+4yEXO5+vnPR+yGiGSW/CuoXc7vBoMvT4ENAOv5/WctUIUG1lVqNx7nqPQSJfccCl8GmU0mSXjlDpRXS5Qxau5RqV90ncCKQzLTcRK9Eu944Ip/cQ/M1MwrTkTbi5LZ1arfMM0hsyg7/X6A9LvE89z2j3ZvsJmQBBPpzQJxqP7U4r+qbjaN32zG+/baR2dMl6c35ei2qIah/4dvaHkOvzpkU/x5IZ8vbodOI9cMbdSclnnxj38EC1XZscUunYy36JvVI3OxGZmbZXdzwIU5lVJWi90rnjVQGoi70B873GNhPOQO51OFGfkS5yQWRKH8yAbxxGZp9Qm/EChpbGExR7MBqGdAlTkcokK7MMeMTfwa2NM9TDsdtfSfQGnWxw7CB47frsQShDSwJJro/iiNmgncw1GQl5rI0trtetY4sKgYCJ/Y4qVFSR0lNGQ3P04aDxrTKvWOjMXT0LuxCU33txaSiPffuRjr3fsO/mC6og/VmsmuGYN0Ij93TFYcVXunQaaDb4+wJbFd7vg0OI5If53NENtoK2AlVQwGScjSFt5lrjdWymu8WEIgSwtppyzAsLgxRk8P5hG9PMA3bKiPspdsULjJRwX7ukf5/NX7TAtAhUAhQSHz9DgycOX7wcwERMJb3TLGb4CFGkB/xUWSRK3BZLrPF9Rn6ZmrUqJX02oc",D="AAACzg0ODAoPeNp9VO9v2jAQ/Z6/wtK+IYWGsEKHFGmQmJau/GgIm9iYJhMOYprYke1A4a+fSchGKtqPPvvevXv3zp/6gqLHLEb2HbKsTvNzp9lC98MA2ZbdNKYgdiAGntPDQ2z688HcdH/aM7P5o2kZTzQEJiE4pDAiCTgBngaD0b0RCr5f1S9uHfcUWXSQBzuIeQrCCDlb10mo6A4cJTLQSVlIlzFUggpI8ickMbAVEbI+ypIliPG6R5Il5z4kXEF3A0xJp1Gywa8pFQePKHAmDdt6+A9cJVQEK5yWmhMV8HGZLRWkwrF8PZMgpGPfGh7IUNBUUc6cAKRCcVEYrblAaZxtKEOrsqj8V/US85zxXd+fUGwD70ickRwyf7ARACziqUYo+8Irmt/jUYD9iT+YYmNIKFPACAuvyHIaQEUSHYgz0G8rorgC8rp5qvaEZVp3pt0u8sve3TiTCsSIr0A6llGr1VwfdwPsmb25PjjbVwq2EcZcm6nS6DKX+I3iT1yP/Cy42TBe4FAK0WhZVtu6azYbZeZlB0U2wrpnkQoqq40Uyst67RqB9zU8k64olYeqKnGmNCzWiseOUoKwhLLoK1ExkZISVg95cr3ZN/YqdueSYW649/mdrfJAZOQM3b3b96DRuhVk3261kkFvexwe41UQtJN12u5Gz6GcPT7Pn71H70t69CPcbx/a9rcj3rDuwlk4xprKCA5VN+oJjsaB2R/75sQfezM3GIxH5myKT6PN/QErtDwgFQE6E9VDCLUZtN8F30Ko0K9IqXTRubnZ8HpFlZtzAyYUGb/ryOOIcYVWVCpBl5kCjUwlUhyF2mY80WaoXxdzEhOWe2YsNoRRWWxMt6xX/hH6P5uxF8b3rLJIlz1LpQW95pQPvqfLq0kmwohIeLs2l9PMzfQ48LvvWTbfMY1AXDg9KJDLCV2C9HUMH+CaJfOfudi/v0kIGVswLAIUEMQ9X77gqGBdfH0HIzBaV8NadPUCFBn+RiUA4D+IqVQpTTouLVY66aCGX02121",toAbsolutePath=t=>{const[,s]=process.argv,e=a(s),o=e.substring(0,e.indexOf("dcdx")+4);return i(o,t.replaceAll("../",""))};var k;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(k||(k={}));const x={name:"shared",driver:"bridge"};let P=class Base extends h{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,s){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:s})}catch(s){console.error("An error occurred while trying to run the following SQL query:",t,s),e()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),e(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:x}}}async up(){const t=g(this.getDockerComposeConfig());return A({cwd:b(),configAsString:t,log:!0})}async down(){const t=g(this.getDockerComposeConfig());return c({cwd:b(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new v(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[f,T,y,E,R],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=g(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((s,e)=>{const o=n("docker",["logs","-f","-n","5000",t],{cwd:b()});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}};const q={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends P{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=q;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=q){super({...q,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const L={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends P{name="mysql";driver="com.mysql.jdbc.Driver";options=L;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=L){super({...L,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const O={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends P{name="postgresql";driver="org.postgresql.Driver";options=O;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=O){super({...O,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const U=i(w(),".dcdx");class Base extends h{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async cp(t){const s=await this.getServiceState();if(s&&s.state.toLowerCase().startsWith("up")){const s=this.getDockerComposeConfig(),e=g(s);await l("cp",[t,`${this.name}:/opt/quickreload/`],{cwd:b(),configAsString:e,log:!1})}}async isApplicationReady(){try{const t=await r.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:s}=t;if("FIRST_RUN"===s.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:x}}}async up(){const t=this.getDockerComposeConfig(),s=g(t);await p({cwd:b(),configAsString:s,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),e(0)}async down(){const t=g(this.getDockerComposeConfig());await c({cwd:b(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=g(this.getDockerComposeConfig());return(await d({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const s=await this.getServiceState();return!!(s&&s.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),s&&await this.showDockerLogs(s.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const s=this.getDockerRepositoryUrl(),e=i(U,this.name,"source");u(e)?await S({baseDir:e}).pull({"--recurse-submodule":null}):(m(i(U,this.name),{recursive:!0}),await S().clone(s,e,{"--recurse-submodule":null})),await new Promise(((s,o)=>{const a=n("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:e});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?s():o(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((s,e)=>{n("docker",["logs","-f","-n","5000",t],{cwd:b(),stdio:"inherit"}).on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((s,e)=>{n("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:b(),stdio:"inherit"}).on("exit",(t=>0===t?s():e(new Error(`Docker exited with code ${t}`))))}))}}class Jira extends Base{name=k.JIRA;database;logFilePath="/var/atlassian/application-data/jira/log/atlassian-jira.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),s=this.getEnvironmentVariables();return{build:{context:toAbsolutePath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./jira-data-generator-5.0.0.jar /var/atlassian/application-data/jira/plugins/installed-plugins/jira-data-generator-5.0.0.jar\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/jira/lib/mysql-connector-j-8.3.0.jar\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/jira/plugins/installed-plugins/quickreload-5.0.2.jar\nRUN echo "/opt/quickreload" > /var/atlassian/application-data/jira/quickreload.properties; mkdir -p /opt/quickreload; chown -R jira:jira /opt/quickreload;\n\nRUN chown -R jira:jira /var/atlassian/application-data/jira`},ports:[`${this.options.port||80}:8080`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(s).length>0?s:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){const t="postgresql"===this.database.name?"postgres72":this.database.name;return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_LICENSE_KEY:this.options.license||D,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_DRIVER:this.database.driver,ATL_DB_TYPE:t,JIRA_SETUP_LICENSE:this.options.license||C}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}(async()=>{const e=t.showHelpAfterError(!0).addOption(new s("-v, --version <version>","The version of the host application").choices(["9.15.0"]).default("9.15.0")).addOption(new s("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new s("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new s("-c, --contextPath <contextPath>","The context path on which the host application will be accessible")).addOption(new s("-qr, --quickReload <path_to_watch>","Add support for QuickReload and add the provided path to the watch list")).addOption(new s("--debug","Add support for JVM debugger on port 5005")).parse(process.argv).opts(),a=new Jira({version:e.version,database:e.database,port:Number(e.port),contextPath:e.contextPath,quickReload:e.qr,debug:e.debug});o((async()=>{console.log(`Stopping ${a.name}... ⏳`),await a.stop(),console.log(`Stopped ${a.name} 💪`)}),{wait:3e4}),await a.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),e()}));
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as a}from"commander";import{DOMParser as t,XMLSerializer as e}from"@xmldom/xmldom";import{XMLParser as n}from"fast-xml-parser";import{existsSync as r,readFileSync as s}from"fs";import i from"xpath";var o;!function(a){a.JIRA="jira",a.CONFLUENCE="confluence",a.BITBUCKET="bitbucket",a.BAMBOO="bamboo"}(o||(o={}));class AMPS{static isAtlassianPlugin=()=>{try{if(r("./pom.xml")){const a=s("./pom.xml","utf8"),t=(new n).parse(a);return"atlassian-plugin"===t?.project?.packaging}return!1}catch(a){return!1}};static getApplication(){const a=AMPS.getApplications();return 1===a.length?a[0]:null}static getApplications(){const a=new Set;if(AMPS.isAtlassianPlugin()){const r=s("./pom.xml","utf8"),c=(new t).parseFromString(r,"text/xml"),l=i.select("//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']",c);Array.isArray(l)&&l.forEach((t=>{const r=t.parentNode;if(r){const t=new n,{plugin:s}=t.parse((new e).serializeToString(r));s?.artifactId?.includes(o.JIRA)?a.add(o.JIRA):s?.artifactId?.includes(o.CONFLUENCE)?a.add(o.CONFLUENCE):s?.artifactId?.includes(o.BAMBOO)?a.add(o.BAMBOO):s?.artifactId?.includes(o.BITBUCKET)&&a.add(o.BITBUCKET)}}))}return Array.from(a)}}if(!process.argv.some((a=>Object.values(o).includes(a)))){const a=AMPS.getApplication();if(a){const t=[a,...process.argv.splice(2)];process.argv=[...process.argv.slice(0,2),...t]}}a.name("dcdx run").command("bamboo","Start Atlassian Bamboo (standalone)",{executableFile:"./run-bamboo.js"}).command("bitbucket","Start Atlassian Bitbucket (standalone)",{executableFile:"./run-bitbucket.js"}).command("confluence","Start Atlassian Confluence (standalone)",{executableFile:"./run-confluence.js"}).command("jira","Start Atlassian Jira (standalone)",{executableFile:"./run-jira.js"}),a.parse(process.argv);
2
+ import{program as e}from"commander";import{DOMParser as t,XMLSerializer as a}from"@xmldom/xmldom";import{spawn as n}from"child_process";import{XMLParser as r}from"fast-xml-parser";import{existsSync as o,readFileSync as s}from"fs";import{cwd as i}from"process";import c from"xpath";import{hideBin as l}from"yargs/helpers";import m from"yargs/yargs";var p;!function(e){e.JIRA="jira",e.CONFLUENCE="confluence",e.BITBUCKET="bitbucket",e.BAMBOO="bamboo"}(p||(p={}));const{P:d,activeProfiles:u}=m(l(process.argv)).parseSync(),g=d||u||void 0;class AMPS{static maven;static stop(){AMPS.maven&&AMPS.maven.kill(0)}static async build(e){return new Promise(((t,a)=>{if(AMPS.maven){AMPS.maven.kill(0)||a(new Error("Failed to terminate existing Maven process"))}AMPS.maven=n("mvn",["package",...e],{cwd:i(),stdio:"inherit"}),AMPS.maven.on("exit",(e=>{AMPS.maven=null,0===e||130===e?t():a(new Error(`Maven exited with code ${e}`))}))}))}static isAtlassianPlugin=()=>{try{return AMPS.getNodes("//*[local-name()='packaging']").some((e=>"atlassian-plugin"===e.textContent))}catch(e){return console.log(e),!1}};static getApplicationVersion(){const e=g?AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${g}']/..//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']`,!0):AMPS.getNodes("//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']",!0);if(e){const t=e.parentNode;if(t){const{plugin:e}=AMPS.toObject(t),a=e?.configuration?.productVersion;return a?this.doPropertyReplacement(a):void 0}}}static getApplication(){const e=AMPS.getApplications();if(1===e.length)return e[0];if(g){const e=AMPS.getApplications(g);if(1===e.length)return e[0]}return null}static getApplications(e){const t=new Set;return(e?AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${e}']/..//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']`):AMPS.getNodes("//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']")).forEach((e=>{const a=e.parentNode;if(a){const{plugin:e}=AMPS.toObject(a);e?.artifactId?.includes(p.JIRA)?t.add(p.JIRA):e?.artifactId?.includes(p.CONFLUENCE)?t.add(p.CONFLUENCE):e?.artifactId?.includes(p.BAMBOO)?t.add(p.BAMBOO):e?.artifactId?.includes(p.BITBUCKET)&&t.add(p.BITBUCKET)}})),Array.from(t)}static doPropertyReplacement(e){let t=e;const a=g?AMPS.getProperties(g):{};Object.entries(a).forEach((([e,a])=>{t=t.replaceAll(`\${${e}}`,a)}));const n=AMPS.getProperties();return Object.entries(n).forEach((([e,a])=>{t=t.replaceAll(`\${${e}}`,a)})),t}static getProperties(e){const t={};return(e?AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${e}']/..//*[local-name()='properties']`):AMPS.getNodes("//*[local-name()='properties']")).forEach((e=>{const{properties:a}=AMPS.toObject(e);Object.entries(a).forEach((([e,a])=>t[e]=a))})),t}static getNodes(e,a){if(o("./pom.xml")){const n=s("./pom.xml","utf8"),r=(new t).parseFromString(n,"text/xml"),o=a?c.select(e,r,!0):c.select(e,r,!1);return Array.isArray(o)||a?o:[]}return[]}static toObject(e){try{return(new r).parse((new a).serializeToString(e))}catch(e){return null}}}if(!process.argv.some((e=>Object.values(p).includes(e)))){const e=AMPS.getApplication();if(e){const t=[e,...process.argv.splice(2)];process.argv=[...process.argv.slice(0,2),...t]}}e.name("dcdx run").command("bamboo","Start Atlassian Bamboo (standalone)",{executableFile:"./run-bamboo.js"}).command("bitbucket","Start Atlassian Bitbucket (standalone)",{executableFile:"./run-bitbucket.js"}).command("confluence","Start Atlassian Confluence (standalone)",{executableFile:"./run-confluence.js"}).command("jira","Start Atlassian Jira (standalone)",{executableFile:"./run-jira.js"}).showHelpAfterError(!0),e.parse(process.argv);
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{watch as t}from"chokidar";import{program as e,Option as s}from"commander";import{gracefulExit as a,asyncExitHook as o}from"exit-hook";import{cwd as i}from"process";import{DOMParser as n,XMLSerializer as r}from"@xmldom/xmldom";import{spawn as c}from"child_process";import{XMLParser as l}from"fast-xml-parser";import{existsSync as d,readFileSync as p,mkdirSync as u}from"fs";import h from"xpath";import{hideBin as g}from"yargs/helpers";import m from"yargs/yargs";import b from"axios";import{dirname as A,join as v}from"path";import{downAll as f,ps as w,execCompose as S,upAll as k}from"docker-compose/dist/v2.js";import P from"events";import{dump as R}from"js-yaml";import{homedir as E}from"os";import C from"simple-git";import{upAll as D}from"docker-compose";import{Sequelize as T,ConnectionError as y,TimeoutError as O,ConnectionTimedOutError as M,ConnectionRefusedError as x,ConnectionAcquireTimeoutError as q}from"sequelize";var U;!function(t){t.JIRA="jira",t.CONFLUENCE="confluence",t.BITBUCKET="bitbucket",t.BAMBOO="bamboo"}(U||(U={}));const{P:L,activeProfiles:j}=m(g(process.argv)).parseSync(),B=L||j||void 0;class AMPS{static maven;static stop(){AMPS.maven&&AMPS.maven.kill(0)}static async build(t){return new Promise(((e,s)=>{if(AMPS.maven){AMPS.maven.kill(0)||s(new Error("Failed to terminate existing Maven process"))}AMPS.maven=c("mvn",["package",...t],{cwd:i(),stdio:"inherit"}),AMPS.maven.on("exit",(t=>{AMPS.maven=null,0===t||130===t?e():s(new Error(`Maven exited with code ${t}`))}))}))}static isAtlassianPlugin=()=>{try{return AMPS.getNodes("//*[local-name()='packaging']").some((t=>"atlassian-plugin"===t.textContent))}catch(t){return console.log(t),!1}};static getApplicationVersion(){const t=B?AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${B}']/..//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']`,!0):AMPS.getNodes("//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']",!0);if(t){const e=t.parentNode;if(e){const{plugin:t}=AMPS.toObject(e),s=t?.configuration?.productVersion;return s?this.doPropertyReplacement(s):void 0}}}static getApplication(){const t=AMPS.getApplications();if(1===t.length)return t[0];if(B){const t=AMPS.getApplications(B);if(1===t.length)return t[0]}return null}static getApplications(t){const e=new Set;return(t?AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${t}']/..//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']`):AMPS.getNodes("//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']")).forEach((t=>{const s=t.parentNode;if(s){const{plugin:t}=AMPS.toObject(s);t?.artifactId?.includes(U.JIRA)?e.add(U.JIRA):t?.artifactId?.includes(U.CONFLUENCE)?e.add(U.CONFLUENCE):t?.artifactId?.includes(U.BAMBOO)?e.add(U.BAMBOO):t?.artifactId?.includes(U.BITBUCKET)&&e.add(U.BITBUCKET)}})),Array.from(e)}static doPropertyReplacement(t){let e=t;const s=B?AMPS.getProperties(B):{};Object.entries(s).forEach((([t,s])=>{e=e.replaceAll(`\${${t}}`,s)}));const a=AMPS.getProperties();return Object.entries(a).forEach((([t,s])=>{e=e.replaceAll(`\${${t}}`,s)})),e}static getProperties(t){const e={};return(t?AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${t}']/..//*[local-name()='properties']`):AMPS.getNodes("//*[local-name()='properties']")).forEach((t=>{const{properties:s}=AMPS.toObject(t);Object.entries(s).forEach((([t,s])=>e[t]=s))})),e}static getNodes(t,e){if(d("./pom.xml")){const s=p("./pom.xml","utf8"),a=(new n).parseFromString(s,"text/xml"),o=e?h.select(t,a,!0):h.select(t,a,!1);return Array.isArray(o)||e?o:[]}return[]}static toObject(t){try{return(new l).parse((new r).serializeToString(t))}catch(t){return null}}}const _="AAACBA0ODAoPeNp9kl1v2jAUhu/zK460OyQHSLuVIkUaTbwNVBKUhEnbugsTDuA2sSPbgbFfPxOC1i+4yEXO5+vnPR+yGiGSW/CuoXc7vBoMvT4ENAOv5/WctUIUG1lVqNx7nqPQSJfccCl8GmU0mSXjlDpRXS5Qxau5RqV90ncCKQzLTcRK9Eu944Ip/cQ/M1MwrTkTbi5LZ1arfMM0hsyg7/X6A9LvE89z2j3ZvsJmQBBPpzQJxqP7U4r+qbjaN32zG+/baR2dMl6c35ei2qIah/4dvaHkOvzpkU/x5IZ8vbodOI9cMbdSclnnxj38EC1XZscUunYy36JvVI3OxGZmbZXdzwIU5lVJWi90rnjVQGoi70B873GNhPOQO51OFGfkS5yQWRKH8yAbxxGZp9Qm/EChpbGExR7MBqGdAlTkcokK7MMeMTfwa2NM9TDsdtfSfQGnWxw7CB47frsQShDSwJJro/iiNmgncw1GQl5rI0trtetY4sKgYCJ/Y4qVFSR0lNGQ3P04aDxrTKvWOjMXT0LuxCU33txaSiPffuRjr3fsO/mC6og/VmsmuGYN0Ij93TFYcVXunQaaDb4+wJbFd7vg0OI5If53NENtoK2AlVQwGScjSFt5lrjdWymu8WEIgSwtppyzAsLgxRk8P5hG9PMA3bKiPspdsULjJRwX7ukf5/NX7TAtAhUAhQSHz9DgycOX7wcwERMJb3TLGb4CFGkB/xUWSRK3BZLrPF9Rn6ZmrUqJX02oc",N="AAACzg0ODAoPeNp9VO9v2jAQ/Z6/wtK+IYWGsEKHFGmQmJau/GgIm9iYJhMOYprYke1A4a+fSchGKtqPPvvevXv3zp/6gqLHLEb2HbKsTvNzp9lC98MA2ZbdNKYgdiAGntPDQ2z688HcdH/aM7P5o2kZTzQEJiE4pDAiCTgBngaD0b0RCr5f1S9uHfcUWXSQBzuIeQrCCDlb10mo6A4cJTLQSVlIlzFUggpI8ickMbAVEbI+ypIliPG6R5Il5z4kXEF3A0xJp1Gywa8pFQePKHAmDdt6+A9cJVQEK5yWmhMV8HGZLRWkwrF8PZMgpGPfGh7IUNBUUc6cAKRCcVEYrblAaZxtKEOrsqj8V/US85zxXd+fUGwD70ickRwyf7ARACziqUYo+8Irmt/jUYD9iT+YYmNIKFPACAuvyHIaQEUSHYgz0G8rorgC8rp5qvaEZVp3pt0u8sve3TiTCsSIr0A6llGr1VwfdwPsmb25PjjbVwq2EcZcm6nS6DKX+I3iT1yP/Cy42TBe4FAK0WhZVtu6azYbZeZlB0U2wrpnkQoqq40Uyst67RqB9zU8k64olYeqKnGmNCzWiseOUoKwhLLoK1ExkZISVg95cr3ZN/YqdueSYW649/mdrfJAZOQM3b3b96DRuhVk3261kkFvexwe41UQtJN12u5Gz6GcPT7Pn71H70t69CPcbx/a9rcj3rDuwlk4xprKCA5VN+oJjsaB2R/75sQfezM3GIxH5myKT6PN/QErtDwgFQE6E9VDCLUZtN8F30Ko0K9IqXTRubnZ8HpFlZtzAyYUGb/ryOOIcYVWVCpBl5kCjUwlUhyF2mY80WaoXxdzEhOWe2YsNoRRWWxMt6xX/hH6P5uxF8b3rLJIlz1LpQW95pQPvqfLq0kmwohIeLs2l9PMzfQ48LvvWTbfMY1AXDg9KJDLCV2C9HUMH+CaJfOfudi/v0kIGVswLAIUEMQ9X77gqGBdfH0HIzBaV8NadPUCFBn+RiUA4D+IqVQpTTouLVY66aCGX02121",I="AAABmg0ODAoPeNp9Ul1v0zAUfc+vsMRbJadJtjGpUiS2JJRONOlaB4SAB9e9W0xTu7JvCv33eImDOoR4vefe4/PhN59hR5bckPiWRNezKJ5FCZkvGUmiJA4yrZALLPkBUrHXotEnu5eBRW6b8KMUoCyw8xH6BVZs2KKce3TJpUJQXAkofh2lOeccIV1dfRhJC7fRXrJuwJzALPL0/qF+pI9XGaPz+uGGspvbkdRx8Awcr0nRdOCnlXnmSlqOUqv0DltureTKg6vOiIZb6J9PoviaRm9pHHs0M9Cf/Rv1Dv/SP5lMyorR99WartZVXmdsUZW03hQOSHtCF+n2TLAB4hlIoYTegSFHo3+AQPK1QTx+m02nzzrko+JQ6MO0HS4oDBffQ5JrojSSnbRo5LZDcMzSEtREdBb1AYwNRzsXff0JgjCwLjG/4mB5giE+JzhbF3esyOn9lxf1F2145a6OWu2V/qmC/2dVdoctmOqptk5PGid+XJx42w3NPPHWQuD9fXJbL8PktfLhU2Av+N2rYMY6uUEFpncY/AYCgfFAMCwCFHzkBlkGH/vRdTeeBSfUMzUDb4W2AhQyM893WPg2Q8oQAYRdBSyKeVUdog==X02jr",J="AAAB3w0ODAoPeNp9Ultr2zAYffevEOwtoMR10qYLGJbYWsno7GA7g90eZPlrotWWjCSnzX79FF8IYWkfDPJ3Oedwzvch2zdoWSvkzpF7v5jdLrwpCkiaIc/1POeRMxAasmMNEa3Az2xnHT04gQJquBQhNeB77vQGuzPs3jk5rXIpx5QZfgDfqAaGUtRUOaj4adX+PkpGy+UOhNE+vnGG5laDags9L3mtuToOJF5Pkja5ZorXJwEXHG+pJQdaNvS/cYtLAysBVFfu19ehvxXPQr4IJwV1AGULKzIneBb+8PBd/GWOH6Yf751No9ieaujleYMHKYl8++Fb13VGo1EUZ/hznOBNEofbIFvHEd6mxDb81kQoUH5EZg+op0dEMFmAQrWSf4AZ9HNvTP1rMZnsrLGmpFpzKsZMVpOy28DQbfweo1AiIQ0quDaK540Bi8w1MhKxRhtZWXvHTghn+zLQBvU46Ela1rLZcYEKOEApazvvfKXceiSoYG/kcTXhTUlFG+XVbgKVNHA+gF7BN8t3UuU5sdpRwXUXGnmlVV0CCmRVU3G8Evf5MKUw9vqIFV36tX7moNjfo7CPTxfenZIJErLMSIhX309pvDd8SUgK3smKMpJsknVKBtr26t4D+gd3AC5qMCwCFHOaTWfFvLCLJPft3qktVo3FafmZAhRojb9IAxj8KEqPDQy0stos394SQQ==X02mq",toAbsolutePath=t=>{const[,e]=process.argv,s=A(e),a=s.substring(0,s.indexOf("dcdx")+4);return v(a,t.replaceAll("../",""))},V={name:"shared",driver:"bridge"};let Q=class Base extends P{options;sequelize=null;constructor(t){super(),this.options=t}async run(t,e){try{if(!this.sequelize)throw new Error("Database connection does not exist");await this.sequelize.query(t,{logging:e})}catch(e){console.error("An error occurred while trying to run the following SQL query:",t,e),a()}return null}async start(){console.log(`Starting instance of ${this.name} ⏳`),await this.stop(),await this.up(),this.emit(`${this.name}:up`);if(await this.waitUntilReady()){if(console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`),await this.onDatabaseReady(),this.emit("db:ready"),this.options.logging){const t=await this.getServiceState();t&&await this.showDockerLogs(t.name)}}else console.log(`Failed to start database ${this.name} ⛔`),a(0)}async stop(){await this.down(),this.emit("db:stopped")}async onDatabaseReady(){}getDockerComposeConfig(){return{version:"3.8",services:{db:this.getService()},networks:{shared:V}}}async up(){const t=R(this.getDockerComposeConfig());return D({cwd:i(),configAsString:t,log:!0})}async down(){const t=R(this.getDockerComposeConfig());return f({cwd:i(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0})}async waitUntilReady(){try{const t="mssql"===this.name?"master":this.options.database;return this.sequelize=new T(t,this.options.username,this.options.password,{host:"localhost",port:this.options.port,dialect:this.name.replace("postgresql","postgres"),retry:{max:30,match:[y,O,M,x,q],backoffBase:1e3,backoffExponent:1},logging:!1}),this.sequelize.authenticate().then((()=>!0)).catch((t=>(console.log(t),!1)))}catch(t){return!1}}async getServiceState(){const t=R(this.getDockerComposeConfig());return(await w({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async showDockerLogs(t){return new Promise(((e,s)=>{const a=c("docker",["logs","-f","-n","5000",t],{cwd:i()});a.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),a.on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}};const G={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends Q{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=G;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=G){super({...G,...t})}async onDatabaseReady(){await this.run(`CREATE DATABASE ${this.options.database}`),await this.run(`ALTER DATABASE ${this.options.database} COLLATE SQL_Latin1_General_CP1_CS_AS`),await this.run(`ALTER DATABASE ${this.options.database} SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;`)}getService=()=>({image:`mcr.microsoft.com/mssql/server:${this.version}-latest`,ports:[`${this.options.port||1433}:1433`],environment:{ACCEPT_EULA:"y",MSSQL_SA_PASSWORD:this.options.password||"dcdx",MSSQL_PID:this.options.edition},networks:{shared:{aliases:["db","database"]}}})}const $={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends Q{name="mysql";driver="com.mysql.jdbc.Driver";options=$;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=$){super({...$,...t})}async onDatabaseReady(){await this.run(`ALTER DATABASE ${this.options.database} CHARACTER SET 'utf8mb4' COLLATE utf8mb4_bin`)}getService=()=>({image:`mysql:${this.version}`,ports:[`${this.options.port||3306}:3306`],environment:{MYSQL_ROOT_PASSWORD:this.options.password||"dcdx",MYSQL_USER:this.options.username||"dcdx",MYSQL_PASSWORD:this.options.password||"dcdx",MYSQL_DATABASE:this.options.database||"dcdx"},command:["--log_bin_trust_function_creators=1"],networks:{shared:{aliases:["db","database"]}}})}const F={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends Q{name="postgresql";driver="org.postgresql.Driver";options=F;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=F){super({...F,...t})}getService=()=>({image:`postgres:${this.version}`,ports:[`${this.options.port||5432}:5432`],environment:{POSTGRES_USER:this.options.username||"dcdx",POSTGRES_PASSWORD:this.options.password||"dcdx",POSTGRES_DB:this.options.database||"dcdx",POSTGRES_HOST_AUTH_METHOD:"md5",POSTGRES_INITDB_ARGS:"--encoding=UTF-8 --lc-collate=C --lc-ctype=C"},networks:{shared:{aliases:["db","database"]}}})}const Y=v(E(),".dcdx");class Base extends P{options;constructor(t){super(),this.options=t}get baseUrl(){const t=`http://localhost:${this.options.port}`;return this.options.contextPath?`${t}/${this.options.contextPath}`:t}getDatabaseEngine(t){switch(t){case"postgresql":return new Postgres;case"mssql":return new MSSQL;case"mysql":return new MySQL}}async start(){await this.stop(),await this.build(this.options.version),await this.database.start(this.name,this.options.version),await this.up()}async stop(){await this.database.stop(),await this.down()}async cp(t){const e=await this.getServiceState();if(e&&e.state.toLowerCase().startsWith("up")){const e=this.getDockerComposeConfig(),s=R(e);await S("cp",[t,`${this.name}:/opt/quickreload/`],{cwd:i(),configAsString:s,log:!1})}}async isApplicationReady(){try{const t=await b.get(`${this.baseUrl}/status`,{validateStatus:()=>!0}).catch((()=>null));if(t&&200===t.status){const{data:e}=t;if("FIRST_RUN"===e.state)return console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`),!0}return!1}catch(t){return!1}}getDockerComposeConfig(){return{version:"3.8",services:{[this.name]:this.getService()},networks:{shared:V}}}async up(){const t=this.getDockerComposeConfig(),e=R(t);await k({cwd:i(),configAsString:e,log:!0}),this.emit(`${this.name}:up`);await this.waitUntilReady()?(this.emit(`${this.name}:ready`),await this.tailApplicationLogs()):console.log(`Failed to start ${this.name} ⛔`),a(0)}async down(){const t=R(this.getDockerComposeConfig());await f({cwd:i(),configAsString:t,commandOptions:["-v","--remove-orphans","--rmi","local"],log:!0}),this.emit(`${this.name}:stopped`)}async getServiceState(){const t=R(this.getDockerComposeConfig());return(await w({configAsString:t,log:!1,commandOptions:["--all"]})).data.services.find((t=>t.name.includes(this.name)))}async waitUntilReady(t=0){console.log(`Waiting for ${this.name} to become available... ${t}s`);const e=await this.getServiceState();return!!(e&&e.state.toLowerCase().startsWith("up")&&await this.isApplicationReady())||(t>=300?(console.error(`A timeout occurred while waiting for ${this.name} to become available ⛔`),e&&await this.showDockerLogs(e.name),!1):(await new Promise((t=>setTimeout(t,1e3))),this.waitUntilReady(t+1)))}getDockerRepositoryUrl(){return`https://bitbucket.org/atlassian-docker/docker-${"jira"===this.name?"atlassian-jira":"bamboo"===this.name?`${this.name}-server`:`atlassian-${this.name}-server`}.git`}async build(t){const e=this.getDockerRepositoryUrl(),s=v(Y,this.name,"source");d(s)?await C({baseDir:s}).pull({"--recurse-submodule":null}):(u(v(Y,this.name),{recursive:!0}),await C().clone(e,s,{"--recurse-submodule":null})),await new Promise(((e,a)=>{const o=c("docker",["build","-t",`dcdx/${this.name}:${t}`,"--build-arg",`${this.name.toUpperCase()}_VERSION=${t}`,"."],{cwd:s});o.stdout.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.stderr.on("data",(t=>{console.log(t.toString("utf-8").trim())})),o.on("exit",(t=>0===t?e():a(new Error(`Docker exited with code ${t}`))))}))}async tailApplicationLogs(){const t=await this.getServiceState();t&&t.state.toLowerCase().startsWith("up")&&await this.showApplicationLogs(t.name).catch((()=>null))}async showDockerLogs(t){return new Promise(((e,s)=>{c("docker",["logs","-f","-n","5000",t],{cwd:i(),stdio:"inherit"}).on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}async showApplicationLogs(t){return new Promise(((e,s)=>{c("docker",["exec","-i",t,"tail","-F","-n","5000",this.logFilePath],{cwd:i(),stdio:"inherit"}).on("exit",(t=>0===t?e():s(new Error(`Docker exited with code ${t}`))))}))}}class Bamboo extends Base{name=U.BAMBOO;database;logFilePath="/var/atlassian/application-data/bamboo/logs/atlassian-bamboo.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),e=this.getEnvironmentVariables();return{build:{context:toAbsolutePath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/bamboo/lib/mysql-connector-j-8.3.0.jar\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bamboo/shared/plugins/quickreload-5.0.2.jar\nRUN echo "/opt/quickreload" > /var/atlassian/application-data/bamboo/quickreload.properties; mkdir -p /opt/quickreload; chown -R bamboo:bamboo /opt/quickreload;\n\nRUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo`},ports:[`${this.options.port||80}:8085`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(e).length>0?e:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};async isApplicationReady(){try{const t=await b.get(`${this.baseUrl}/setup/setupGeneralConfiguration.action`,{validateStatus:()=>!0}).catch((()=>null));return 200===t?.status}catch(t){return!1}}getEnvironmentVariables(){return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_BAMBOO_ENABLE_UNATTENDED_SETUP:"true",ATL_LICENSE:this.options.license||J,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_TYPE:`${this.database.name}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}class Bitbucket extends Base{name=U.BITBUCKET;database;logFilePath="/var/atlassian/application-data/bitbucket/log/atlassian-bitbucket.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),e=this.getEnvironmentVariables();return{build:{context:toAbsolutePath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bitbucket/plugins/installed-plugins/quickreload-5.0.2.jar\nCOPY ./mysql-connector-j-8.3.0.jar /var/atlassian/application-data/bitbucket/lib/mysql-connector-j-8.3.0.jar\nRUN echo "/opt/quickreload" > /var/atlassian/application-data/bitbucket/quickreload.properties; mkdir -p /opt/quickreload; chown -R bitbucket:bitbucket /opt/quickreload;\n\nRUN mkdir -p /var/atlassian/application-data/bitbucket/shared; touch /var/atlassian/application-data/bitbucket/shared/bitbucket.properties; echo "setup.license=${this.options.license||I}" >> /var/atlassian/application-data/bitbucket/shared/bitbucket.properties;\n\nRUN chown -R bitbucket:bitbucket /var/atlassian/application-data/bitbucket`},ports:[`${this.options.port||80}:7990`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(e).length>0?e:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){return{...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",JDBC_URL:this.database.url,JDBC_USER:this.database.options.username,JDBC_PASSWORD:this.database.options.password,JDBC_DRIVER:`${this.database.driver}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}class Confluence extends Base{name=U.CONFLUENCE;database;logFilePath="/var/atlassian/application-data/confluence/logs/atlassian-confluence.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),e=this.getEnvironmentVariables();return{build:{context:toAbsolutePath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/confluence/confluence/WEB-INF/lib/mysql-connector-j-8.3.0.jar\nCOPY ./quickreload-5.0.2.jar /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/quickreload-5.0.2.jar\nRUN echo "/opt/quickreload" > /var/atlassian/application-data/confluence/quickreload.properties; mkdir -p /opt/quickreload; chown -R confluence:confluence /opt/quickreload;\n\nRUN chown -R confluence:confluence /opt/atlassian/confluence`},ports:[`${this.options.port||80}:8090`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(e).length>0?e:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_LICENSE_KEY:this.options.license||N,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_TYPE:`${this.database.name}`}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-Xrunjdwp:transport=dt_socket,address=*:5005,server=y,suspend=n"),t.push("-Dcom.sun.management.jmxremote.port=9999"),t.push("-Dcom.sun.management.jmxremote.rmi.port=9998"),t.push("-Dcom.sun.management.jmxremote.authenticate=false"),t.push("-Dcom.sun.management.jmxremote.ssl=false")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}class Jira extends Base{name=U.JIRA;database;logFilePath="/var/atlassian/application-data/jira/log/atlassian-jira.log";constructor(t){super(t),this.database=this.getDatabaseEngine(t.database)}getService=()=>{const t=this.getVolumes(),e=this.getEnvironmentVariables();return{build:{context:toAbsolutePath("../../assets"),dockerfile_inline:`\nFROM dcdx/${this.name}:${this.options.version}\nCOPY ./jira-data-generator-5.0.0.jar /var/atlassian/application-data/jira/plugins/installed-plugins/jira-data-generator-5.0.0.jar\nCOPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/jira/lib/mysql-connector-j-8.3.0.jar\nCOPY ./quickreload-5.0.2.jar /var/atlassian/application-data/jira/plugins/installed-plugins/quickreload-5.0.2.jar\nRUN echo "/opt/quickreload" > /var/atlassian/application-data/jira/quickreload.properties; mkdir -p /opt/quickreload; chown -R jira:jira /opt/quickreload;\n\nRUN chown -R jira:jira /var/atlassian/application-data/jira`},ports:[`${this.options.port||80}:8080`,...this.options.debug?["5005:5005"]:[]],environment:Object.keys(e).length>0?e:void 0,volumes:t.length>0?t:void 0,networks:["shared"]}};getEnvironmentVariables(){const t="postgresql"===this.database.name?"postgres72":this.database.name;return{...this.options.contextPath?{ATL_TOMCAT_CONTEXTPATH:this.options.contextPath}:"",...this.options.debug?{JVM_SUPPORT_RECOMMENDED_ARGS:this.getJVMArgs()}:"",ATL_LICENSE_KEY:this.options.license||N,ATL_JDBC_URL:this.database.url,ATL_JDBC_USER:this.database.options.username,ATL_JDBC_PASSWORD:this.database.options.password,ATL_DB_DRIVER:this.database.driver,ATL_DB_TYPE:t,JIRA_SETUP_LICENSE:this.options.license||_}}getJVMArgs(){const t=[];return this.options.debug&&(t.push("-Dupm.plugin.upload.enabled=true"),t.push("-Xdebug"),t.push("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005")),this.options.quickReload&&t.push("-Dquickreload.dirs=/opt/quickreload"),t.join(" ")}getVolumes(){return[...this.options.quickReload?[`${this.options.quickReload}:/opt/quickreload`]:""]}}AMPS.isAtlassianPlugin()||(console.log("Unable to find an Atlassian Plugin project in the current directory 🤔"),a());const W=AMPS.getApplication();W||(console.log("The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰"),a(),process.exit());const z=(t=>{switch(t){case U.JIRA:return Jira;case U.CONFLUENCE:return Confluence;case U.BAMBOO:return Bamboo;case U.BITBUCKET:return Bitbucket}})(W);z||(console.log("The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰"),process.exit());const K=AMPS.getApplicationVersion();(async()=>{const a=e.name("dcdx start").description("Build & install the Atlassian Data Center plugin from the current directory.\nYou can add Maven build arguments after the command options.").usage("[options] [...maven_arguments]").addOption(new s("-v, --version <version>","The version of the host application").default(K)).addOption(new s("-d, --database <name>","The database engine on which the host application will run").choices(["postgresql","mysql","mssql"]).default("postgresql")).addOption(new s("-p, --port <port>","The HTTP port on which the host application will be accessible").default("80")).addOption(new s("-c, --contextPath <contextPath>","The context path on which the host application will be accessible")).addOption(new s("-P, --activate-profiles <arg>","Comma-delimited list of profiles to activate")).addOption(new s("-o, --outputDirectory <arg>","Output directory where QuickReload will look for generated JAR files").default("target")).addOption(new s("--debug","Add support for JVM debugger on port 5005").default(!0)).allowUnknownOption(!0).parse(process.argv).opts(),n=new z({version:a.version,database:a.database,port:Number(a.port),contextPath:a.contextPath,debug:a.debug}),r=e.args.slice();a.activateProfiles&&r.push("-P",a.activateProfiles),console.log("Watching filesystem for changes to source files (QuickReload)");let c=(new Date).getTime();const l=t("**/*",{cwd:i(),usePolling:!0,interval:2e3,binaryInterval:2e3,awaitWriteFinish:!0}).on("change",(async t=>{var e;t.startsWith(a.outputDirectory)&&t.toLowerCase().endsWith(".jar")?(console.log("Found updated JAR file(s), uploading them to QuickReload"),await n.cp(t),c=(new Date).getTime()):t.startsWith(a.outputDirectory)||((t=>t>(new Date).getTime()-5e3)(c)?(e=a.outputDirectory,console.log(`\n===============================================================================================================\nRecursive build trigger detected. The last build completed last than 5 seconds ago\nThis may indicate that the build changes files outside of the output directory\nAlternatively, Maven is using a different output directory than configured:\n'${e}'\n\nPlease make sure to check your build process and/or specify a different output directory using the '-o' option\n===============================================================================================================\n `)):(console.log("Detected file change, rebuilding Atlasian Plugin for QuickReload"),await AMPS.build(r).catch((()=>Promise.resolve()))))}));o((async()=>{console.log("Stopping filesystem watcher... ⏳"),await l.close(),console.log(`Stopping ${n.name}... ⏳`),await n.stop(),console.log("Successfully stopped all running processes 💪")}),{wait:3e4}),console.log("Starting application..."),await n.start()})(),process.on("SIGINT",(()=>{console.log("Received term signal, trying to stop gracefully 💪"),a()}));
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as a}from"commander";var s="1.1.0";a.name("dcdx").description("The Unofficial Atlassian Data Center Plugin Development CLI").version(s),a.command("run","Start the Atlassian host application (standalone)",{executableFile:"./commands/run.js"}),a.command("run:jira").description("Start Atlassian Jira").action((()=>{process.argv.splice(2,1,"run","jira"),a.parse(process.argv)})),a.command("run:confluence").description("Start Atlassian Confluence").action((()=>{process.argv.splice(2,1,"run","confluence"),a.parse(process.argv)})),a.command("run:bitbucket").description("Start Atlassian Bitbucket").action((()=>{process.argv.splice(2,1,"run","bitbucket"),a.parse(process.argv)})),a.command("run:bamboo").description("Start Atlassian Bamboo").action((()=>{process.argv.splice(2,1,"run","bamboo"),a.parse(process.argv)})),a.command("database","Start a database engine (standalone)",{executableFile:"./commands/database.js"}),a.command("database:postgres").description("Start PostgreSQL").action((()=>{process.argv.splice(2,1,"database","postgresql"),a.parse(process.argv)})),a.command("database:postgresql").description("Start PostgreSQL").action((()=>{process.argv.splice(2,1,"database","postgresql"),a.parse(process.argv)})),a.command("database:mysql").description("Start MySQL").action((()=>{process.argv.splice(2,1,"database","mysql"),a.parse(process.argv)})),a.command("database:mssql").description("Start Microsoft Sql Server").action((()=>{process.argv.splice(2,1,"database","mssql"),a.parse(process.argv)})),a.command("profile","Run a predefined profile",{executableFile:"./commands/profile.js"}).on("command:*",(s=>{const e=s[0];process.argv.splice(2,1,"profile",e),a.parse(process.argv)})),a.parse(process.argv);
2
+ import{program as a}from"commander";var s="1.2.0-next.1";a.name("dcdx").description("The Unofficial Atlassian Data Center Plugin Development CLI").version(s).showHelpAfterError(!0),a.command("start","Build & install the Atlassian Data Center plugin from the current directory",{executableFile:"./commands/start.js"}),a.command("run","Start the Atlassian host application (standalone)",{executableFile:"./commands/run.js"}),a.command("run:jira").description("Start Atlassian Jira").action((()=>{process.argv.splice(2,1,"run","jira"),a.parse(process.argv)})),a.command("run:confluence").description("Start Atlassian Confluence").action((()=>{process.argv.splice(2,1,"run","confluence"),a.parse(process.argv)})),a.command("run:bitbucket").description("Start Atlassian Bitbucket").action((()=>{process.argv.splice(2,1,"run","bitbucket"),a.parse(process.argv)})),a.command("run:bamboo").description("Start Atlassian Bamboo").action((()=>{process.argv.splice(2,1,"run","bamboo"),a.parse(process.argv)})),a.command("database","Start a database engine (standalone)",{executableFile:"./commands/database.js"}),a.command("database:postgres").description("Start PostgreSQL").action((()=>{process.argv.splice(2,1,"database","postgresql"),a.parse(process.argv)})),a.command("database:postgresql").description("Start PostgreSQL").action((()=>{process.argv.splice(2,1,"database","postgresql"),a.parse(process.argv)})),a.command("database:mysql").description("Start MySQL").action((()=>{process.argv.splice(2,1,"database","mysql"),a.parse(process.argv)})),a.command("database:mssql").description("Start Microsoft Sql Server").action((()=>{process.argv.splice(2,1,"database","mssql"),a.parse(process.argv)})),a.command("profile","Run a predefined profile",{executableFile:"./commands/profile.js"}).on("command:*",(s=>{const e=s[0];process.argv.splice(2,1,"profile",e),a.parse(process.argv)})),a.parse(process.argv);
@@ -1,6 +1,14 @@
1
1
  import { SupportedApplications } from '../types/SupportedApplications';
2
2
  export declare class AMPS {
3
+ private static maven;
4
+ static stop(): void;
5
+ static build(args: Array<string>): Promise<void>;
3
6
  static isAtlassianPlugin: () => boolean;
7
+ static getApplicationVersion(): string | undefined;
4
8
  static getApplication(): SupportedApplications | null;
5
- static getApplications(): Array<SupportedApplications>;
9
+ static getApplications(profile?: string): Array<SupportedApplications>;
10
+ private static doPropertyReplacement;
11
+ private static getProperties;
12
+ private static getNodes;
13
+ private static toObject;
6
14
  }
@@ -15,6 +15,7 @@ export declare abstract class Base extends EventEmitter {
15
15
  getDatabaseEngine(name: SupportedDatabaseEngines): DatabaseEngine;
16
16
  start(): Promise<void>;
17
17
  stop(): Promise<void>;
18
+ cp(filename: string): Promise<void>;
18
19
  protected abstract getService(): Service;
19
20
  protected isApplicationReady(): Promise<boolean>;
20
21
  private getDockerComposeConfig;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,6 @@
1
+ import { Bamboo } from '../applications/bamboo';
2
+ import { Bitbucket } from '../applications/bitbucket';
3
+ import { Confluence } from '../applications/confluence';
4
+ import { Jira } from '../applications/jira';
5
+ import { SupportedApplications } from '../types/SupportedApplications';
6
+ export declare const getApplicationByName: (name: SupportedApplications) => typeof Bamboo | typeof Bitbucket | typeof Confluence | typeof Jira;
@@ -0,0 +1 @@
1
+ export declare const isRecursiveBuild: (lastBuildCompleted: number) => boolean;
@@ -0,0 +1 @@
1
+ export declare const showRecursiveBuildWarning: (outputDirectory: string) => void;
@@ -0,0 +1 @@
1
+ export declare const toAbsolutePath: (relativePath: string) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dcdx",
3
- "version": "1.1.0",
3
+ "version": "1.2.0-next.1",
4
4
  "author": "Collabsoft <info@collabsoft.net>",
5
5
  "description": "The Unofficial CLI for Atlassian Data Center Plugin Development",
6
6
  "type": "module",
@@ -47,6 +47,7 @@
47
47
  "@types/js-yaml": "4",
48
48
  "@types/node": "18.16.0",
49
49
  "@types/pg": "8",
50
+ "@types/yargs": "17.0.32",
50
51
  "@typescript-eslint/eslint-plugin": "7.6.0",
51
52
  "@typescript-eslint/parser": "7.6.0",
52
53
  "eslint": "9.0.0",
@@ -61,6 +62,7 @@
61
62
  "dependencies": {
62
63
  "@xmldom/xmldom": "0.8.10",
63
64
  "axios": "1.6.8",
65
+ "chokidar": "3.6.0",
64
66
  "commander": "12.0.0",
65
67
  "docker-compose": "0.24.8",
66
68
  "exit-hook": "4.0.0",
@@ -71,12 +73,19 @@
71
73
  "sequelize": "6.37.2",
72
74
  "simple-git": "3.24.0",
73
75
  "tedious": "18.1.0",
74
- "xpath": "0.0.34"
76
+ "xpath": "0.0.34",
77
+ "yargs": "17.7.2"
75
78
  },
76
79
  "release": {
77
80
  "branches": [
78
- "main",
79
- "next"
81
+ {
82
+ "name": "main"
83
+ },
84
+ {
85
+ "name": "next",
86
+ "channel": "next",
87
+ "prerelease": true
88
+ }
80
89
  ]
81
90
  }
82
91
  }
@@ -1 +0,0 @@
1
- export declare const getFullPath: (relativePath: string) => string;