dcdx 1.0.1 → 1.1.0

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 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 y,ConnectionRefusedError as D,ConnectionAcquireTimeoutError as E}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",R={name:"shared",driver:"bridge"};let k=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: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 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,y,D,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(((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 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 O={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends k{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 _=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:R}}}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="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 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()}));
@@ -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",C={name:"shared",driver:"bridge"};let E=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:C}}}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 $={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends E{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 O={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends E{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 T={version:"15",database:"dcdx",port:5432,username:"dcdx",password:"dcdx"};class Postgres extends E{name="postgresql";driver="org.postgresql.Driver";options=T;version="15";get url(){return`jdbc:postgresql://db:${this.options.port}/${this.options.database}`}constructor(t=T){super({...T,...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 x=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:C}}}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(x,this.name,"source");h(e)?await w({baseDir:e}).pull({"--recurse-submodule":null}):(u(i(x,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="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 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()}));
@@ -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 T}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("../",""))},E="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",C={name:"shared",driver:"bridge"};let R=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:C}}}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,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=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 k={port:1433,database:"dcdx",username:"sa",password:"DataCenterDX!",edition:"Developer",version:"2022"};class MSSQL extends R{name="mssql";driver="com.microsoft.sqlserver.jdbc.SQLServerDriver";options=k;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=k){super({...k,...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 x={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends R{name="mysql";driver="com.mysql.jdbc.Driver";options=x;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=x){super({...x,...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 R{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:C}}}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="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||E,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,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()}));
@@ -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 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 A,ConnectionError as v,TimeoutError as f,ConnectionTimedOutError as T,ConnectionRefusedError as y,ConnectionAcquireTimeoutError as D}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("../",""))},E="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",C={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:C}}}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 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,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,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 k={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=k;version="2022";get url(){return`jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`}constructor(t=k){super({...k,...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 P={port:3306,database:"dcdx",username:"dcdx",password:"dcdx",version:"8.0"};class MySQL extends x{name="mysql";driver="com.mysql.jdbc.Driver";options=P;version="8.0";get url(){return`jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`}constructor(t=P){super({...P,...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 x{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 L=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:C}}}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} ⛔`),e(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(),e=i(L,this.name,"source");h(e)?await b({baseDir:e}).pull({"--recurse-submodule":null}):(m(i(L,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="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||E}}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 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()}));
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as a}from"commander";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();
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);
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.0.1";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.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);
@@ -0,0 +1,6 @@
1
+ import { SupportedApplications } from '../types/SupportedApplications';
2
+ export declare class AMPS {
3
+ static isAtlassianPlugin: () => boolean;
4
+ static getApplication(): SupportedApplications | null;
5
+ static getApplications(): Array<SupportedApplications>;
6
+ }
@@ -1 +1,6 @@
1
- export type SupportedApplications = 'jira' | 'confluence' | 'bitbucket' | 'bamboo';
1
+ export declare enum SupportedApplications {
2
+ JIRA = "jira",
3
+ CONFLUENCE = "confluence",
4
+ BITBUCKET = "bitbucket",
5
+ BAMBOO = "bamboo"
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dcdx",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "author": "Collabsoft <info@collabsoft.net>",
5
5
  "description": "The Unofficial CLI for Atlassian Data Center Plugin Development",
6
6
  "type": "module",
@@ -59,16 +59,19 @@
59
59
  "typescript-eslint": "7.6.0"
60
60
  },
61
61
  "dependencies": {
62
+ "@xmldom/xmldom": "0.8.10",
62
63
  "axios": "1.6.8",
63
64
  "commander": "12.0.0",
64
65
  "docker-compose": "0.24.8",
65
66
  "exit-hook": "4.0.0",
67
+ "fast-xml-parser": "4.3.6",
66
68
  "js-yaml": "4.1.0",
67
69
  "mysql2": "3.9.4",
68
70
  "pg": "8.11.5",
69
71
  "sequelize": "6.37.2",
70
72
  "simple-git": "3.24.0",
71
- "tedious": "18.1.0"
73
+ "tedious": "18.1.0",
74
+ "xpath": "0.0.34"
72
75
  },
73
76
  "release": {
74
77
  "branches": [