From 91038bf819b446930bd4a4e7a2898744200df6f7 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Mon, 15 Apr 2024 12:25:46 +0200 Subject: [PATCH 01/18] feat: detect application from AMPS context --- package.json | 5 ++- src/applications/amps.ts | 59 ++++++++++++++++++++++++++++++ src/applications/bamboo.ts | 2 +- src/applications/bitbucket.ts | 2 +- src/applications/confluence.ts | 2 +- src/applications/jira.ts | 2 +- src/commands/run.ts | 19 ++++++++-- src/types/SupportedApplications.ts | 7 +++- yarn.lock | 35 ++++++++++++++++++ 9 files changed, 124 insertions(+), 9 deletions(-) create mode 100644 src/applications/amps.ts diff --git a/package.json b/package.json index ea7eceb..5be2085 100644 --- a/package.json +++ b/package.json @@ -58,15 +58,18 @@ "typescript-eslint": "7.6.0" }, "dependencies": { + "@xmldom/xmldom": "0.8.10", "axios": "1.6.8", "commander": "12.0.0", "docker-compose": "0.24.8", "exit-hook": "4.0.0", + "fast-xml-parser": "4.3.6", "js-yaml": "4.1.0", "mysql2": "3.9.4", "pg": "8.11.5", "sequelize": "6.37.2", "simple-git": "3.24.0", - "tedious": "18.1.0" + "tedious": "18.1.0", + "xpath": "0.0.34" } } diff --git a/src/applications/amps.ts b/src/applications/amps.ts new file mode 100644 index 0000000..e1f3906 --- /dev/null +++ b/src/applications/amps.ts @@ -0,0 +1,59 @@ + +import { DOMParser, XMLSerializer } from '@xmldom/xmldom'; +import { XMLParser } from 'fast-xml-parser'; +import { existsSync, readFileSync } from 'fs' +import xpath from 'xpath'; + +import { SupportedApplications } from '../types/SupportedApplications'; + +export class AMPS { + + public static isAtlassianPlugin = (): boolean => { + try { + const hasPomFile = existsSync('./pom.xml'); + if (hasPomFile) { + const content = readFileSync('./pom.xml', 'utf8'); + const parser = new XMLParser(); + const pom = parser.parse(content); + return pom?.project?.packaging === 'atlassian-plugin'; + } + return false; + } catch (err) { + return false; + } + } + + public static getApplication(): SupportedApplications|null { + const applications = AMPS.getApplications(); + return applications.length === 1 ? applications[0] : null; + } + + public static getApplications(): Array { + const result = new Set(); + if (AMPS.isAtlassianPlugin()) { + const xml = readFileSync('./pom.xml', 'utf8'); + const doc = new DOMParser().parseFromString(xml, 'text/xml'); + const nodes = xpath.select('//*[local-name()=\'groupId\' and text()=\'com.atlassian.maven.plugins\']', doc); + if (Array.isArray(nodes)) { + nodes.forEach(node => { + const parentNode = node.parentNode; + if (parentNode) { + const parser = new XMLParser(); + const { plugin } = parser.parse(new XMLSerializer().serializeToString(parentNode)); + if (plugin?.artifactId?.includes(SupportedApplications.JIRA)) { + result.add(SupportedApplications.JIRA); + } else if (plugin?.artifactId?.includes(SupportedApplications.CONFLUENCE)) { + result.add(SupportedApplications.CONFLUENCE); + } else if (plugin?.artifactId?.includes(SupportedApplications.BAMBOO)) { + result.add(SupportedApplications.BAMBOO); + } else if (plugin?.artifactId?.includes(SupportedApplications.BITBUCKET)) { + result.add(SupportedApplications.BITBUCKET); + } + } + }); + } + } + return Array.from(result); + } + +} \ No newline at end of file diff --git a/src/applications/bamboo.ts b/src/applications/bamboo.ts index 8e02d1b..8333ed1 100644 --- a/src/applications/bamboo.ts +++ b/src/applications/bamboo.ts @@ -11,7 +11,7 @@ import { Base } from './base'; export class Bamboo extends Base { - name: SupportedApplications = 'bamboo'; + name = SupportedApplications.BAMBOO; database: DatabaseEngine; logFilePath = '/var/atlassian/application-data/bamboo/logs/atlassian-bamboo.log'; diff --git a/src/applications/bitbucket.ts b/src/applications/bitbucket.ts index 5cc0617..c4418b2 100644 --- a/src/applications/bitbucket.ts +++ b/src/applications/bitbucket.ts @@ -9,7 +9,7 @@ import { Base } from './base'; export class Bitbucket extends Base { - name: SupportedApplications = 'bitbucket'; + name = SupportedApplications.BITBUCKET; database: DatabaseEngine; logFilePath = '/var/atlassian/application-data/bitbucket/log/atlassian-bitbucket.log'; diff --git a/src/applications/confluence.ts b/src/applications/confluence.ts index 533b4f3..e39f96a 100644 --- a/src/applications/confluence.ts +++ b/src/applications/confluence.ts @@ -9,7 +9,7 @@ import { Base } from './base'; export class Confluence extends Base { - name: SupportedApplications = 'confluence'; + name = SupportedApplications.CONFLUENCE; database: DatabaseEngine; logFilePath = '/var/atlassian/application-data/confluence/logs/atlassian-confluence.log'; diff --git a/src/applications/jira.ts b/src/applications/jira.ts index b814455..cfbb9f5 100644 --- a/src/applications/jira.ts +++ b/src/applications/jira.ts @@ -9,7 +9,7 @@ import { Base } from './base'; export class Jira extends Base { - name: SupportedApplications = 'jira'; + name = SupportedApplications.JIRA; database: DatabaseEngine; logFilePath = '/var/atlassian/application-data/jira/log/atlassian-jira.log'; diff --git a/src/commands/run.ts b/src/commands/run.ts index 038e447..d3d5c1c 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -2,12 +2,25 @@ import { program } from 'commander'; +import { AMPS } from '../applications/amps'; +import { SupportedApplications } from '../types/SupportedApplications'; + +// Check if there is a command in the arguments +const isDefaultCommand = !process.argv.some(item => Object.values(SupportedApplications).includes(item as SupportedApplications)); +// If there is no command, check if we are running this within the context of an Atlassian Plugin project +if (isDefaultCommand) { + const application = AMPS.getApplication(); + if (application) { + const args = [ application, ...process.argv.splice(2) ]; + process.argv = [ ...process.argv.slice(0, 2), ...args ]; + } +} + program .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'}) - -program.parse(); + .command('jira', 'Start Atlassian Jira (standalone)', { executableFile: './run-jira.js'}); +program.parse(process.argv); \ No newline at end of file diff --git a/src/types/SupportedApplications.ts b/src/types/SupportedApplications.ts index 5649169..da7613a 100644 --- a/src/types/SupportedApplications.ts +++ b/src/types/SupportedApplications.ts @@ -1,2 +1,7 @@ -export type SupportedApplications = 'jira'|'confluence'|'bitbucket'|'bamboo'; \ No newline at end of file +export enum SupportedApplications { + JIRA = 'jira', + CONFLUENCE = 'confluence', + BITBUCKET = 'bitbucket', + BAMBOO = 'bamboo' +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 8a281dc..d67603b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -828,6 +828,13 @@ __metadata: languageName: node linkType: hard +"@xmldom/xmldom@npm:0.8.10": + version: 0.8.10 + resolution: "@xmldom/xmldom@npm:0.8.10" + checksum: 10c0/c7647c442502720182b0d65b17d45d2d95317c1c8c497626fe524bda79b4fb768a9aa4fae2da919f308e7abcff7d67c058b102a9d641097e9a57f0b80187851f + languageName: node + linkType: hard + "abbrev@npm:1": version: 1.1.1 resolution: "abbrev@npm:1.1.1" @@ -1212,12 +1219,14 @@ __metadata: "@types/pg": "npm:8" "@typescript-eslint/eslint-plugin": "npm:7.6.0" "@typescript-eslint/parser": "npm:7.6.0" + "@xmldom/xmldom": "npm:0.8.10" axios: "npm:1.6.8" commander: "npm:12.0.0" docker-compose: "npm:0.24.8" eslint: "npm:9.0.0" eslint-plugin-simple-import-sort: "npm:12.0.0" exit-hook: "npm:4.0.0" + fast-xml-parser: "npm:4.3.6" js-yaml: "npm:4.1.0" mysql2: "npm:3.9.4" nodemon: "npm:3.1.0" @@ -1229,6 +1238,7 @@ __metadata: tedious: "npm:18.1.0" typescript: "npm:5.4.4" typescript-eslint: "npm:7.6.0" + xpath: "npm:0.0.34" bin: dcdx: ./lib/index.js languageName: unknown @@ -1555,6 +1565,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:4.3.6": + version: 4.3.6 + resolution: "fast-xml-parser@npm:4.3.6" + dependencies: + strnum: "npm:^1.0.5" + bin: + fxparser: src/cli/cli.js + checksum: 10c0/9ebe2ac142c6978cae423c39c2a9b561edb76be584317d578768ed4a006a61fc0e83abf8c6fe31029139c4ad15ea1f2e7b6720ba9e6eda0e5266d7f2770fb079 + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.17.1 resolution: "fastq@npm:1.17.1" @@ -3432,6 +3453,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 10c0/64fb8cc2effbd585a6821faa73ad97d4b553c8927e49086a162ffd2cc818787643390b89d567460a8e74300148d11ac052e21c921ef2049f2987f4b1b89a7ff1 + languageName: node + linkType: hard + "supports-color@npm:^5.5.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -3714,6 +3742,13 @@ __metadata: languageName: node linkType: hard +"xpath@npm:0.0.34": + version: 0.0.34 + resolution: "xpath@npm:0.0.34" + checksum: 10c0/88335108884ca164421f7fed048ef1a18ab3f7b1ae446b627fd3f51fc2396dcce798601c5e426de3bbd55d5940b84cf2326c75cd76620c1b49491283b85de17a + languageName: node + linkType: hard + "xtend@npm:^4.0.0": version: 4.0.2 resolution: "xtend@npm:4.0.2" From b270199584c78939a445da8fbdb365cb66c3e303 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Mon, 15 Apr 2024 12:46:43 +0200 Subject: [PATCH 02/18] chore: add semantic release --- package.json | 6 +- yarn.lock | 3887 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 3793 insertions(+), 100 deletions(-) diff --git a/package.json b/package.json index ea7eceb..babac5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dcdx", - "version": "1.0.0", + "version": "0.0.0-development", "author": "Collabsoft ", "description": "The Unofficial CLI for Atlassian Data Center Plugin Development", "type": "module", @@ -54,6 +54,7 @@ "nodemon": "3.1.0", "rollup": "4.14.1", "rollup-plugin-executable": "1.6.3", + "semantic-release": "23.0.8", "typescript": "5.4.4", "typescript-eslint": "7.6.0" }, @@ -68,5 +69,8 @@ "sequelize": "6.37.2", "simple-git": "3.24.0", "tedious": "18.1.0" + }, + "release": { + "branches": ["main", "next"] } } diff --git a/yarn.lock b/yarn.lock index 8a281dc..8b310e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -200,6 +200,35 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.22.13": + version: 7.24.2 + resolution: "@babel/code-frame@npm:7.24.2" + dependencies: + "@babel/highlight": "npm:^7.24.2" + picocolors: "npm:^1.0.0" + checksum: 10c0/d1d4cba89475ab6aab7a88242e1fd73b15ecb9f30c109b69752956434d10a26a52cbd37727c4eca104b6d45227bd1dfce39a6a6f4a14c9b2f07f871e968cf406 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 10c0/dcad63db345fb110e032de46c3688384b0008a42a4845180ce7cd62b1a9c0507a1bed727c4d1060ed1a03ae57b4d918570259f81724aaac1a5b776056f37504e + languageName: node + linkType: hard + +"@babel/highlight@npm:^7.24.2": + version: 7.24.2 + resolution: "@babel/highlight@npm:7.24.2" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.22.20" + chalk: "npm:^2.4.2" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.0.0" + checksum: 10c0/98ce00321daedeed33a4ed9362dc089a70375ff1b3b91228b9f05e6591d387a81a8cba68886e207861b8871efa0bc997ceabdd9c90f6cce3ee1b2f7f941b42db + languageName: node + linkType: hard + "@babel/runtime@npm:^7.14.0": version: 7.24.4 resolution: "@babel/runtime@npm:7.24.4" @@ -209,6 +238,13 @@ __metadata: languageName: node linkType: hard +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: 10c0/eb42729851adca56d19a08e48d5a1e95efd2a32c55ae0323de8119052be0510d4b7a1611f2abcbf28c044a6c11e6b7d38f99fccdad7429300c37a8ea5fb95b44 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -290,6 +326,13 @@ __metadata: languageName: node linkType: hard +"@isaacs/string-locale-compare@npm:^1.1.0": + version: 1.1.0 + resolution: "@isaacs/string-locale-compare@npm:1.1.0" + checksum: 10c0/d67226ff7ac544a495c77df38187e69e0e3a0783724777f86caadafb306e2155dc3b5787d5927916ddd7fb4a53561ac8f705448ac3235d18ea60da5854829fdf + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.5 resolution: "@jridgewell/gen-mapping@npm:0.3.5" @@ -405,6 +448,75 @@ __metadata: languageName: node linkType: hard +"@npmcli/arborist@npm:^7.2.1": + version: 7.4.2 + resolution: "@npmcli/arborist@npm:7.4.2" + dependencies: + "@isaacs/string-locale-compare": "npm:^1.1.0" + "@npmcli/fs": "npm:^3.1.0" + "@npmcli/installed-package-contents": "npm:^2.0.2" + "@npmcli/map-workspaces": "npm:^3.0.2" + "@npmcli/metavuln-calculator": "npm:^7.0.0" + "@npmcli/name-from-folder": "npm:^2.0.0" + "@npmcli/node-gyp": "npm:^3.0.0" + "@npmcli/package-json": "npm:^5.0.0" + "@npmcli/query": "npm:^3.1.0" + "@npmcli/redact": "npm:^1.1.0" + "@npmcli/run-script": "npm:^7.0.2" + bin-links: "npm:^4.0.1" + cacache: "npm:^18.0.0" + common-ancestor-path: "npm:^1.0.1" + hosted-git-info: "npm:^7.0.1" + json-parse-even-better-errors: "npm:^3.0.0" + json-stringify-nice: "npm:^1.1.4" + minimatch: "npm:^9.0.4" + nopt: "npm:^7.0.0" + npm-install-checks: "npm:^6.2.0" + npm-package-arg: "npm:^11.0.1" + npm-pick-manifest: "npm:^9.0.0" + npm-registry-fetch: "npm:^16.2.0" + npmlog: "npm:^7.0.1" + pacote: "npm:^17.0.4" + parse-conflict-json: "npm:^3.0.0" + proc-log: "npm:^3.0.0" + promise-all-reject-late: "npm:^1.0.0" + promise-call-limit: "npm:^3.0.1" + read-package-json-fast: "npm:^3.0.2" + semver: "npm:^7.3.7" + ssri: "npm:^10.0.5" + treeverse: "npm:^3.0.0" + walk-up-path: "npm:^3.0.1" + bin: + arborist: bin/index.js + checksum: 10c0/e21e16f33f1c032c18bb2d4f1afd86778915895fff0bc0e8a9f7709a8ec2060207f99436713b8b4d9c7c86e3d1bfda31da02afa599892f8b0a3e821e37cac58a + languageName: node + linkType: hard + +"@npmcli/config@npm:^8.0.2": + version: 8.2.2 + resolution: "@npmcli/config@npm:8.2.2" + dependencies: + "@npmcli/map-workspaces": "npm:^3.0.2" + ci-info: "npm:^4.0.0" + ini: "npm:^4.1.2" + nopt: "npm:^7.0.0" + proc-log: "npm:^3.0.0" + read-package-json-fast: "npm:^3.0.2" + semver: "npm:^7.3.5" + walk-up-path: "npm:^3.0.1" + checksum: 10c0/a75ae647f5242ce49b57daae2b35cd26bed4205fe201cf074302e03025f4037e19feefd230c2757f99d2325d7eba97871b824ccf06e8f4d483b824b720232a33 + languageName: node + linkType: hard + +"@npmcli/disparity-colors@npm:^3.0.0": + version: 3.0.0 + resolution: "@npmcli/disparity-colors@npm:3.0.0" + dependencies: + ansi-styles: "npm:^4.3.0" + checksum: 10c0/b14d95c01ceb037d3b18c96d4a168242c7c8d20720e8d7b81cea1d05e39ff22ae5a5083256aba7729a06384c555838b330d6ee66a76cc6a5cef32d65116eebda + languageName: node + linkType: hard + "@npmcli/fs@npm:^3.1.0": version: 3.1.0 resolution: "@npmcli/fs@npm:3.1.0" @@ -414,6 +526,241 @@ __metadata: languageName: node linkType: hard +"@npmcli/git@npm:^5.0.0, @npmcli/git@npm:^5.0.3": + version: 5.0.6 + resolution: "@npmcli/git@npm:5.0.6" + dependencies: + "@npmcli/promise-spawn": "npm:^7.0.0" + lru-cache: "npm:^10.0.1" + npm-pick-manifest: "npm:^9.0.0" + proc-log: "npm:^4.0.0" + promise-inflight: "npm:^1.0.1" + promise-retry: "npm:^2.0.1" + semver: "npm:^7.3.5" + which: "npm:^4.0.0" + checksum: 10c0/5cb92105517c855f640e97cd9eacf874d14b00c1eb5d96d840378793e7892422a440fb0ea09ac3bc327c73103ae93e4a32bc2482c172e0c84df83926d136aedb + languageName: node + linkType: hard + +"@npmcli/installed-package-contents@npm:^2.0.1, @npmcli/installed-package-contents@npm:^2.0.2": + version: 2.0.2 + resolution: "@npmcli/installed-package-contents@npm:2.0.2" + dependencies: + npm-bundled: "npm:^3.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + bin: + installed-package-contents: lib/index.js + checksum: 10c0/03efadb365997e3b54d1d1ea30ef3555729a68939ab2b7b7800a4a2750afb53da222f52be36bd7c44950434c3e26cbe7be28dac093efdf7b1bbe9e025ab62a07 + languageName: node + linkType: hard + +"@npmcli/map-workspaces@npm:^3.0.2, @npmcli/map-workspaces@npm:^3.0.6": + version: 3.0.6 + resolution: "@npmcli/map-workspaces@npm:3.0.6" + dependencies: + "@npmcli/name-from-folder": "npm:^2.0.0" + glob: "npm:^10.2.2" + minimatch: "npm:^9.0.0" + read-package-json-fast: "npm:^3.0.0" + checksum: 10c0/6bfcf8ca05ab9ddc2bd19c0fd91e9982f03cc6e67b0c03f04ba4d2f29b7d83f96e759c0f8f1f4b6dbe3182272483643a0d1269788352edd0c883d6fbfa2f3f14 + languageName: node + linkType: hard + +"@npmcli/metavuln-calculator@npm:^7.0.0": + version: 7.0.0 + resolution: "@npmcli/metavuln-calculator@npm:7.0.0" + dependencies: + cacache: "npm:^18.0.0" + json-parse-even-better-errors: "npm:^3.0.0" + pacote: "npm:^17.0.0" + semver: "npm:^7.3.5" + checksum: 10c0/ae9084c333a678f3c1f2e30fefbd4cae25b5b5d0b1c27c3c3f92919cf1da85da24c2b3f3112bd53a184f711b2c165c4d709cd6283f5662cefb80903265ca7c81 + languageName: node + linkType: hard + +"@npmcli/name-from-folder@npm:^2.0.0": + version: 2.0.0 + resolution: "@npmcli/name-from-folder@npm:2.0.0" + checksum: 10c0/1aa551771d98ab366d4cb06b33efd3bb62b609942f6d9c3bb667c10e5bb39a223d3e330022bc980a44402133e702ae67603862099ac8254dad11f90e77409827 + languageName: node + linkType: hard + +"@npmcli/node-gyp@npm:^3.0.0": + version: 3.0.0 + resolution: "@npmcli/node-gyp@npm:3.0.0" + checksum: 10c0/5d0ac17dacf2dd6e45312af2c1ae2749bb0730fcc82da101c37d3a4fd963a5e1c5d39781e5e1e5e5828df4ab1ad4e3fdbab1d69b7cd0abebad9983efb87df985 + languageName: node + linkType: hard + +"@npmcli/package-json@npm:^5.0.0, @npmcli/package-json@npm:^5.0.2": + version: 5.0.3 + resolution: "@npmcli/package-json@npm:5.0.3" + dependencies: + "@npmcli/git": "npm:^5.0.0" + glob: "npm:^10.2.2" + hosted-git-info: "npm:^7.0.0" + json-parse-even-better-errors: "npm:^3.0.0" + normalize-package-data: "npm:^6.0.0" + proc-log: "npm:^4.0.0" + semver: "npm:^7.5.3" + checksum: 10c0/01037fe8ff28cfa4283b0d4d43a3168f178d3637ab59e55eb643066cefd356a74f47737adedd2f1b8ce0745a24fe2b2dfba4d1af06b29b1c155875e33c392c30 + languageName: node + linkType: hard + +"@npmcli/promise-spawn@npm:^7.0.0, @npmcli/promise-spawn@npm:^7.0.1": + version: 7.0.1 + resolution: "@npmcli/promise-spawn@npm:7.0.1" + dependencies: + which: "npm:^4.0.0" + checksum: 10c0/441024049170fc9dd0c793fef7366fd1b2a36c06f1036c52ac4a5d0f2d46deced89f2a94fef20f51aa9934edb4d611ff76b060be2b82086d29d2094ee1b46122 + languageName: node + linkType: hard + +"@npmcli/query@npm:^3.1.0": + version: 3.1.0 + resolution: "@npmcli/query@npm:3.1.0" + dependencies: + postcss-selector-parser: "npm:^6.0.10" + checksum: 10c0/9a099677dd188a2d9eb7a49e32c69d315b09faea59e851b7c2013b5bda915a38434efa7295565c40a1098916c06ebfa1840f68d831180e36842f48c24f4c5186 + languageName: node + linkType: hard + +"@npmcli/redact@npm:^1.1.0": + version: 1.1.0 + resolution: "@npmcli/redact@npm:1.1.0" + checksum: 10c0/886995220e60ca00405c93c5588aff524d1dbfee0ca8688b9607fefcda42aa464d4a3f7c75fc03a16a582befe4b6c3ac4493d67c4eb07da2fe0794fbe0dfc89b + languageName: node + linkType: hard + +"@npmcli/run-script@npm:^7.0.0, @npmcli/run-script@npm:^7.0.2, @npmcli/run-script@npm:^7.0.4": + version: 7.0.4 + resolution: "@npmcli/run-script@npm:7.0.4" + dependencies: + "@npmcli/node-gyp": "npm:^3.0.0" + "@npmcli/package-json": "npm:^5.0.0" + "@npmcli/promise-spawn": "npm:^7.0.0" + node-gyp: "npm:^10.0.0" + which: "npm:^4.0.0" + checksum: 10c0/45159ef7d6b8d9e449e87ed401da69da60514f6e7752e268f29a96f17a543c4a8d4eea6fe2f74b07fd41095e48e0f9859ebec558065d2b01849b382b06fefe35 + languageName: node + linkType: hard + +"@octokit/auth-token@npm:^5.0.0": + version: 5.1.0 + resolution: "@octokit/auth-token@npm:5.1.0" + checksum: 10c0/d552000c1535936eb749c263ac2d80e0f07734f9a21e05a8885f1e90ea2e02347b35fe15deb53adfd2c5abdb56e988b6f52735cfc296748df70cf0d5402e9dd9 + languageName: node + linkType: hard + +"@octokit/core@npm:^6.0.0": + version: 6.1.2 + resolution: "@octokit/core@npm:6.1.2" + dependencies: + "@octokit/auth-token": "npm:^5.0.0" + "@octokit/graphql": "npm:^8.0.0" + "@octokit/request": "npm:^9.0.0" + "@octokit/request-error": "npm:^6.0.1" + "@octokit/types": "npm:^13.0.0" + before-after-hook: "npm:^3.0.2" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/f73be16a8013f69197b7744de75537d869f3a2061dda25dcde746d23b87f305bbdc7adbfe044ab0755eec32e6d54d61c73f4ca788d214eba8e88648a3133733e + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^10.0.0": + version: 10.1.0 + resolution: "@octokit/endpoint@npm:10.1.0" + dependencies: + "@octokit/types": "npm:^13.0.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/db32de7f24739159e4e972540ca9e643fb36ce0af14b3a5067bb35b70fb91330a2ad88da3394a3e1af09bf7c343f5a021e2ecb859ab0255913c3135071d68998 + languageName: node + linkType: hard + +"@octokit/graphql@npm:^8.0.0": + version: 8.1.0 + resolution: "@octokit/graphql@npm:8.1.0" + dependencies: + "@octokit/request": "npm:^9.0.0" + "@octokit/types": "npm:^13.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/3fed6b4c3fd8928866314573cc749b3c4c1e05f3777cfea3abc8817f8e03ca2ac40cb86160d7b581c29e8839eac3478f7d82c84cfed812946c18f68808fe2d07 + languageName: node + linkType: hard + +"@octokit/openapi-types@npm:^22.0.1": + version: 22.0.1 + resolution: "@octokit/openapi-types@npm:22.0.1" + checksum: 10c0/af1aa603ba3071d93efa29a6c65d78d496f60523c1c3f58b1d6883fe670fd7c6144f5dd893fc50f9c0de0ec91c23cd729388e773969fde9d6e29dd4b58720c4a + languageName: node + linkType: hard + +"@octokit/plugin-paginate-rest@npm:^11.0.0": + version: 11.1.0 + resolution: "@octokit/plugin-paginate-rest@npm:11.1.0" + dependencies: + "@octokit/types": "npm:^13.4.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/5f1fefcaebdca096a4d5a747ae7e8cc19bb2465eba246c931479c0e44d8834d00623795440bbf12963f1732f0a010e6fcdca605123c75ea2814f1abeb0b27098 + languageName: node + linkType: hard + +"@octokit/plugin-retry@npm:^7.0.0": + version: 7.1.0 + resolution: "@octokit/plugin-retry@npm:7.1.0" + dependencies: + "@octokit/request-error": "npm:^6.0.0" + "@octokit/types": "npm:^13.0.0" + bottleneck: "npm:^2.15.3" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/560ac5ebe1695b2617ed39e328f7d1bfd9cee3893139c276cc69f204650725d11c60696a718299e2fc2cd55fab149c0e975db8a196daa67e2982b230d1659382 + languageName: node + linkType: hard + +"@octokit/plugin-throttling@npm:^9.0.0": + version: 9.1.0 + resolution: "@octokit/plugin-throttling@npm:9.1.0" + dependencies: + "@octokit/types": "npm:^13.0.0" + bottleneck: "npm:^2.15.3" + peerDependencies: + "@octokit/core": ^6.0.0 + checksum: 10c0/2c06847b0096d3ef3080582630ecadbe8d8a2da87998892a92270a264e2a15abfbdd464b5f260aeb91d8d4b37a4be6de170d2aced4fe4b48338cc1ec88e156db + languageName: node + linkType: hard + +"@octokit/request-error@npm:^6.0.0, @octokit/request-error@npm:^6.0.1": + version: 6.1.0 + resolution: "@octokit/request-error@npm:6.1.0" + dependencies: + "@octokit/types": "npm:^13.0.0" + checksum: 10c0/3b45a754b8767e3493ca39361c2835f15c1906b49bda7e708a2871323e063ceb8453a5b11b1db71c194a1956b5f158d620604a437a84e993ae05b3ecc60dba0c + languageName: node + linkType: hard + +"@octokit/request@npm:^9.0.0": + version: 9.1.0 + resolution: "@octokit/request@npm:9.1.0" + dependencies: + "@octokit/endpoint": "npm:^10.0.0" + "@octokit/request-error": "npm:^6.0.1" + "@octokit/types": "npm:^13.1.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/746aeb7b681644ad4cbec49724719038dde1cfa70a007695de33216831473fab8d2ed531edca5c17812d799839e03d917a2752a2a41d46771ca6d687f72649e9 + languageName: node + linkType: hard + +"@octokit/types@npm:^13.0.0, @octokit/types@npm:^13.1.0, @octokit/types@npm:^13.4.0": + version: 13.4.0 + resolution: "@octokit/types@npm:13.4.0" + dependencies: + "@octokit/openapi-types": "npm:^22.0.1" + checksum: 10c0/05f9092d505c8e79c425c9c1aeaeb8b7751a8c1732c1c3c00cac4ffb8f2932c585da64e600c33b49df49d662c34156b92853afcfa08accb82924a0a7bdd9c5fc + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -421,6 +768,33 @@ __metadata: languageName: node linkType: hard +"@pnpm/config.env-replace@npm:^1.1.0": + version: 1.1.0 + resolution: "@pnpm/config.env-replace@npm:1.1.0" + checksum: 10c0/4cfc4a5c49ab3d0c6a1f196cfd4146374768b0243d441c7de8fa7bd28eaab6290f514b98490472cc65dbd080d34369447b3e9302585e1d5c099befd7c8b5e55f + languageName: node + linkType: hard + +"@pnpm/network.ca-file@npm:^1.0.1": + version: 1.0.2 + resolution: "@pnpm/network.ca-file@npm:1.0.2" + dependencies: + graceful-fs: "npm:4.2.10" + checksum: 10c0/95f6e0e38d047aca3283550719155ce7304ac00d98911e4ab026daedaf640a63bd83e3d13e17c623fa41ac72f3801382ba21260bcce431c14fbbc06430ecb776 + languageName: node + linkType: hard + +"@pnpm/npm-conf@npm:^2.1.0": + version: 2.2.2 + resolution: "@pnpm/npm-conf@npm:2.2.2" + dependencies: + "@pnpm/config.env-replace": "npm:^1.1.0" + "@pnpm/network.ca-file": "npm:^1.0.1" + config-chain: "npm:^1.1.11" + checksum: 10c0/71393dcfce85603fddd8484b486767163000afab03918303253ae97992615b91d25942f83751366cb40ad2ee32b0ae0a033561de9d878199a024286ff98b0296 + languageName: node + linkType: hard + "@rollup/plugin-commonjs@npm:25.0.7": version: 25.0.7 resolution: "@rollup/plugin-commonjs@npm:25.0.7" @@ -610,6 +984,186 @@ __metadata: languageName: node linkType: hard +"@semantic-release/commit-analyzer@npm:^12.0.0": + version: 12.0.0 + resolution: "@semantic-release/commit-analyzer@npm:12.0.0" + dependencies: + conventional-changelog-angular: "npm:^7.0.0" + conventional-commits-filter: "npm:^4.0.0" + conventional-commits-parser: "npm:^5.0.0" + debug: "npm:^4.0.0" + import-from-esm: "npm:^1.0.3" + lodash-es: "npm:^4.17.21" + micromatch: "npm:^4.0.2" + peerDependencies: + semantic-release: ">=20.1.0" + checksum: 10c0/d9f76fd64ec679bdbb12b0a10d2493567403067d7fc9271571486f8edd53896b298eec2241a1a4427356309d9dea7e45097c8f1772177e38ebf671ff4fbe09a0 + languageName: node + linkType: hard + +"@semantic-release/error@npm:^4.0.0": + version: 4.0.0 + resolution: "@semantic-release/error@npm:4.0.0" + checksum: 10c0/c97fcfbd341765f7c7430bdb32d5f04c61ee15c3eeec374823fbb157640ad03453f24e3a85241bddb29e193b69c6aab480e4d16e76adabb052c01bfbd1698c18 + languageName: node + linkType: hard + +"@semantic-release/github@npm:^10.0.0": + version: 10.0.3 + resolution: "@semantic-release/github@npm:10.0.3" + dependencies: + "@octokit/core": "npm:^6.0.0" + "@octokit/plugin-paginate-rest": "npm:^11.0.0" + "@octokit/plugin-retry": "npm:^7.0.0" + "@octokit/plugin-throttling": "npm:^9.0.0" + "@semantic-release/error": "npm:^4.0.0" + aggregate-error: "npm:^5.0.0" + debug: "npm:^4.3.4" + dir-glob: "npm:^3.0.1" + globby: "npm:^14.0.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.0" + issue-parser: "npm:^7.0.0" + lodash-es: "npm:^4.17.21" + mime: "npm:^4.0.0" + p-filter: "npm:^4.0.0" + url-join: "npm:^5.0.0" + peerDependencies: + semantic-release: ">=20.1.0" + checksum: 10c0/eedefc60f11516903ef6a3bd552d946ae1d4d94f2e9b2d7d7e8e64e7075bed197fa3c5fcd5a1300b8185c41800055cd282cf0a14718e042bfc797172f7aa5f22 + languageName: node + linkType: hard + +"@semantic-release/npm@npm:^12.0.0": + version: 12.0.0 + resolution: "@semantic-release/npm@npm:12.0.0" + dependencies: + "@semantic-release/error": "npm:^4.0.0" + aggregate-error: "npm:^5.0.0" + execa: "npm:^8.0.0" + fs-extra: "npm:^11.0.0" + lodash-es: "npm:^4.17.21" + nerf-dart: "npm:^1.0.0" + normalize-url: "npm:^8.0.0" + npm: "npm:^10.5.0" + rc: "npm:^1.2.8" + read-pkg: "npm:^9.0.0" + registry-auth-token: "npm:^5.0.0" + semver: "npm:^7.1.2" + tempy: "npm:^3.0.0" + peerDependencies: + semantic-release: ">=20.1.0" + checksum: 10c0/857972de2e9d5292c7d8434f073be125f34c5d355fe7a7f8fbde39c953fbe3b30ded6e1361512c31de3bc4378cb9529ce61eaa60bf9afcab00bd682b5d43c5ff + languageName: node + linkType: hard + +"@semantic-release/release-notes-generator@npm:^13.0.0": + version: 13.0.0 + resolution: "@semantic-release/release-notes-generator@npm:13.0.0" + dependencies: + conventional-changelog-angular: "npm:^7.0.0" + conventional-changelog-writer: "npm:^7.0.0" + conventional-commits-filter: "npm:^4.0.0" + conventional-commits-parser: "npm:^5.0.0" + debug: "npm:^4.0.0" + get-stream: "npm:^7.0.0" + import-from-esm: "npm:^1.0.3" + into-stream: "npm:^7.0.0" + lodash-es: "npm:^4.17.21" + read-pkg-up: "npm:^11.0.0" + peerDependencies: + semantic-release: ">=20.1.0" + checksum: 10c0/46dbc2d59e11d1d4e9b75b7d07f955f74ef36e6f240456141f578ac8626c0bb731c04231f1ad4223d140b9734e5af0d477011c501475849eb24ac0bbdb95c829 + languageName: node + linkType: hard + +"@sigstore/bundle@npm:^2.3.0, @sigstore/bundle@npm:^2.3.1": + version: 2.3.1 + resolution: "@sigstore/bundle@npm:2.3.1" + dependencies: + "@sigstore/protobuf-specs": "npm:^0.3.1" + checksum: 10c0/f5cc08e6420055ca20c1fa458725cf3d090974c3650aacfb6ba0b09d9c59149e5f4d8c5bfe9f2253daf2c29548262e9e4ea83b4b2fc4abbaf93cf49ee2687f05 + languageName: node + linkType: hard + +"@sigstore/core@npm:^1.0.0, @sigstore/core@npm:^1.1.0": + version: 1.1.0 + resolution: "@sigstore/core@npm:1.1.0" + checksum: 10c0/3b3420c1bd17de0371e1ac7c8f07a2cbcd24d6b49ace5bbf2b63f559ee08c4a80622a4d1c0ae42f2c9872166e9cb111f33f78bff763d47e5ef1efc62b8e457ea + languageName: node + linkType: hard + +"@sigstore/protobuf-specs@npm:^0.3.0, @sigstore/protobuf-specs@npm:^0.3.1": + version: 0.3.1 + resolution: "@sigstore/protobuf-specs@npm:0.3.1" + checksum: 10c0/bc926aeb472dcd1f99e887d54d9402e259e186ee2a15cdb395cdb565fdd3457f84a044ef355c96359c3c625127a93fb3c45c7e3bd2f16ac0912a58a6bf3fc137 + languageName: node + linkType: hard + +"@sigstore/sign@npm:^2.3.0": + version: 2.3.0 + resolution: "@sigstore/sign@npm:2.3.0" + dependencies: + "@sigstore/bundle": "npm:^2.3.0" + "@sigstore/core": "npm:^1.0.0" + "@sigstore/protobuf-specs": "npm:^0.3.1" + make-fetch-happen: "npm:^13.0.0" + checksum: 10c0/e11b9318c283604747e0ff6084e17f9da210dd999e8c5c32a229db6b9a9faf54698994458c2a09dec0a1a43ac99eb0d278ca6d5d86045145a96c941aad969e1d + languageName: node + linkType: hard + +"@sigstore/tuf@npm:^2.3.1, @sigstore/tuf@npm:^2.3.2": + version: 2.3.2 + resolution: "@sigstore/tuf@npm:2.3.2" + dependencies: + "@sigstore/protobuf-specs": "npm:^0.3.0" + tuf-js: "npm:^2.2.0" + checksum: 10c0/c05008fa46efec1546cc2cdb46e54d6a4773cbd05efa3ad7272339b4f935d58634b9f8494b109197b506116fb894206bf1cdb1fc09351a00297c23ef3c2a1a01 + languageName: node + linkType: hard + +"@sigstore/verify@npm:^1.2.0": + version: 1.2.0 + resolution: "@sigstore/verify@npm:1.2.0" + dependencies: + "@sigstore/bundle": "npm:^2.3.1" + "@sigstore/core": "npm:^1.1.0" + "@sigstore/protobuf-specs": "npm:^0.3.1" + checksum: 10c0/ed0d9cb8c71bde23bd48e40725e5b4bd3ff1595b6d9e4b1ed4f2dfd06f6bf76a3dc69d86f8b2e4488a9cb4ade0d9a41718bd33119b9afca66f90badd52a373fe + languageName: node + linkType: hard + +"@sindresorhus/is@npm:^4.6.0": + version: 4.6.0 + resolution: "@sindresorhus/is@npm:4.6.0" + checksum: 10c0/33b6fb1d0834ec8dd7689ddc0e2781c2bfd8b9c4e4bacbcb14111e0ae00621f2c264b8a7d36541799d74888b5dccdf422a891a5cb5a709ace26325eedc81e22e + languageName: node + linkType: hard + +"@sindresorhus/merge-streams@npm:^2.1.0": + version: 2.3.0 + resolution: "@sindresorhus/merge-streams@npm:2.3.0" + checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 + languageName: node + linkType: hard + +"@tufjs/canonical-json@npm:2.0.0": + version: 2.0.0 + resolution: "@tufjs/canonical-json@npm:2.0.0" + checksum: 10c0/52c5ffaef1483ed5c3feedfeba26ca9142fa386eea54464e70ff515bd01c5e04eab05d01eff8c2593291dcaf2397ca7d9c512720e11f52072b04c47a5c279415 + languageName: node + linkType: hard + +"@tufjs/models@npm:2.0.0": + version: 2.0.0 + resolution: "@tufjs/models@npm:2.0.0" + dependencies: + "@tufjs/canonical-json": "npm:2.0.0" + minimatch: "npm:^9.0.3" + checksum: 10c0/252f525b05526077430920b30b125e197a3d711f4c6d1ceeee9cea5044035e4d94e57db481d96bd8e9d1ce5ee23fcc9fe989e7e0c9c2aec7e1edc27326ee16e6 + languageName: node + linkType: hard + "@types/debug@npm:^4.1.8": version: 4.1.12 resolution: "@types/debug@npm:4.1.12" @@ -663,6 +1217,13 @@ __metadata: languageName: node linkType: hard +"@types/normalize-package-data@npm:^2.4.3": + version: 2.4.4 + resolution: "@types/normalize-package-data@npm:2.4.4" + checksum: 10c0/aef7bb9b015883d6f4119c423dd28c4bdc17b0e8a0ccf112c78b4fe0e91fbc4af7c6204b04bba0e199a57d2f3fbbd5b4a14bf8739bf9d2a39b2a0aad545e0f86 + languageName: node + linkType: hard + "@types/pg@npm:8": version: 8.11.5 resolution: "@types/pg@npm:8.11.5" @@ -828,6 +1389,18 @@ __metadata: languageName: node linkType: hard +"JSONStream@npm:^1.3.5": + version: 1.3.5 + resolution: "JSONStream@npm:1.3.5" + dependencies: + jsonparse: "npm:^1.2.0" + through: "npm:>=2.2.7 <3" + bin: + JSONStream: ./bin.js + checksum: 10c0/0f54694da32224d57b715385d4a6b668d2117379d1f3223dc758459246cca58fdc4c628b83e8a8883334e454a0a30aa198ede77c788b55537c1844f686a751f2 + languageName: node + linkType: hard + "abbrev@npm:1": version: 1.1.1 resolution: "abbrev@npm:1.1.1" @@ -888,6 +1461,16 @@ __metadata: languageName: node linkType: hard +"aggregate-error@npm:^5.0.0": + version: 5.0.0 + resolution: "aggregate-error@npm:5.0.0" + dependencies: + clean-stack: "npm:^5.2.0" + indent-string: "npm:^5.0.0" + checksum: 10c0/a5de7138571f514bad76290736f49a0db8809247082f2519037e0c37d03fc8d91d733e079d6b1674feda28a757b1932421ad205b8c0f8794a0c0e5bf1be2315e + languageName: node + linkType: hard + "ajv@npm:^6.12.4": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -900,6 +1483,13 @@ __metadata: languageName: node linkType: hard +"ansi-escapes@npm:^6.2.0": + version: 6.2.1 + resolution: "ansi-escapes@npm:6.2.1" + checksum: 10c0/a2c6f58b044be5f69662ee17073229b492daa2425a7fd99a665db6c22eab6e4ab42752807def7281c1c7acfed48f87f2362dda892f08c2c437f1b39c6b033103 + languageName: node + linkType: hard + "ansi-regex@npm:^5.0.1": version: 5.0.1 resolution: "ansi-regex@npm:5.0.1" @@ -914,8 +1504,17 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": - version: 4.3.0 +"ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: "npm:^1.9.0" + checksum: 10c0/ece5a8ef069fcc5298f67e3f4771a663129abd174ea2dfa87923a2be2abf6cd367ef72ac87942da00ce85bd1d651d4cd8595aebdb1b385889b89b205860e977b + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0, ansi-styles@npm:^4.3.0": + version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" dependencies: color-convert: "npm:^2.0.1" @@ -930,6 +1529,13 @@ __metadata: languageName: node linkType: hard +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 + languageName: node + linkType: hard + "anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -940,6 +1546,27 @@ __metadata: languageName: node linkType: hard +"aproba@npm:^1.0.3 || ^2.0.0, aproba@npm:^2.0.0": + version: 2.0.0 + resolution: "aproba@npm:2.0.0" + checksum: 10c0/d06e26384a8f6245d8c8896e138c0388824e259a329e0c9f196b4fa533c82502a6fd449586e3604950a0c42921832a458bb3aa0aa9f0ba449cfd4f50fd0d09b5 + languageName: node + linkType: hard + +"archy@npm:~1.0.0": + version: 1.0.0 + resolution: "archy@npm:1.0.0" + checksum: 10c0/200c849dd1c304ea9914827b0555e7e1e90982302d574153e28637db1a663c53de62bad96df42d50e8ce7fc18d05e3437d9aa8c4b383803763755f0956c7d308 + languageName: node + linkType: hard + +"are-we-there-yet@npm:^4.0.0": + version: 4.0.2 + resolution: "are-we-there-yet@npm:4.0.2" + checksum: 10c0/376204f6f07ee7a5f081f5043c92c4c39fd9984278486e0c7c60e74cfc61dc206d2363a2086610f6b95399d9dc3c193cec1832d0ce10666d567f64571c2dedf5 + languageName: node + linkType: hard + "argparse@npm:^2.0.1": version: 2.0.1 resolution: "argparse@npm:2.0.1" @@ -947,6 +1574,30 @@ __metadata: languageName: node linkType: hard +"argv-formatter@npm:~1.0.0": + version: 1.0.0 + resolution: "argv-formatter@npm:1.0.0" + checksum: 10c0/e5582aef98e6b9a70cfe038a3abf6cdd926714b5ce761830bcbd5ac7be86d17ae583fcc8a2cdf4a2ac0b6024ec100b7312160fcefb1520998f476473da6a941d + languageName: node + linkType: hard + +"array-buffer-byte-length@npm:^1.0.1": + version: 1.0.1 + resolution: "array-buffer-byte-length@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.5" + is-array-buffer: "npm:^3.0.4" + checksum: 10c0/f5cdf54527cd18a3d2852ddf73df79efec03829e7373a8322ef5df2b4ef546fb365c19c71d6b42d641cb6bfe0f1a2f19bc0ece5b533295f86d7c3d522f228917 + languageName: node + linkType: hard + +"array-ify@npm:^1.0.0": + version: 1.0.0 + resolution: "array-ify@npm:1.0.0" + checksum: 10c0/75c9c072faac47bd61779c0c595e912fe660d338504ac70d10e39e1b8a4a0c9c87658703d619b9d1b70d324177ae29dc8d07dda0d0a15d005597bc4c5a59c70c + languageName: node + linkType: hard + "array-union@npm:^2.1.0": version: 2.1.0 resolution: "array-union@npm:2.1.0" @@ -954,6 +1605,22 @@ __metadata: languageName: node linkType: hard +"arraybuffer.prototype.slice@npm:^1.0.3": + version: 1.0.3 + resolution: "arraybuffer.prototype.slice@npm:1.0.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.1" + call-bind: "npm:^1.0.5" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.22.3" + es-errors: "npm:^1.2.1" + get-intrinsic: "npm:^1.2.3" + is-array-buffer: "npm:^3.0.4" + is-shared-array-buffer: "npm:^1.0.2" + checksum: 10c0/d32754045bcb2294ade881d45140a5e52bda2321b9e98fa514797b7f0d252c4c5ab0d1edb34112652c62fa6a9398def568da63a4d7544672229afea283358c36 + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -961,6 +1628,15 @@ __metadata: languageName: node linkType: hard +"available-typed-arrays@npm:^1.0.7": + version: 1.0.7 + resolution: "available-typed-arrays@npm:1.0.7" + dependencies: + possible-typed-array-names: "npm:^1.0.0" + checksum: 10c0/d07226ef4f87daa01bd0fe80f8f310982e345f372926da2e5296aecc25c41cab440916bbaa4c5e1034b453af3392f67df5961124e4b586df1e99793a1374bdb2 + languageName: node + linkType: hard + "axios@npm:1.6.8": version: 1.6.8 resolution: "axios@npm:1.6.8" @@ -986,7 +1662,26 @@ __metadata: languageName: node linkType: hard -"binary-extensions@npm:^2.0.0": +"before-after-hook@npm:^3.0.2": + version: 3.0.2 + resolution: "before-after-hook@npm:3.0.2" + checksum: 10c0/dea640f9e88a1085372c9bcc974b7bf379267490693da92ec102a7d8b515dd1e95f00ef575a146b83ca638104c57406c3427d37bdf082f602dde4b56d05bba14 + languageName: node + linkType: hard + +"bin-links@npm:^4.0.1": + version: 4.0.3 + resolution: "bin-links@npm:4.0.3" + dependencies: + cmd-shim: "npm:^6.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + read-cmd-shim: "npm:^4.0.0" + write-file-atomic: "npm:^5.0.0" + checksum: 10c0/66668e005743e7e8df2ecf3018c0f06c5a87043647280e334abb4577bdef124df2893cd0c61eb7261d24ed9a6a1dc35fd8c4f930c89200251974840b3286236f + languageName: node + linkType: hard + +"binary-extensions@npm:^2.0.0, binary-extensions@npm:^2.3.0": version: 2.3.0 resolution: "binary-extensions@npm:2.3.0" checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 @@ -1005,6 +1700,13 @@ __metadata: languageName: node linkType: hard +"bottleneck@npm:^2.15.3": + version: 2.19.5 + resolution: "bottleneck@npm:2.19.5" + checksum: 10c0/b0f72e45b2e0f56a21ba720183f16bef8e693452fb0495d997fa354e42904353a94bd8fd429868e6751bc85e54b6755190519eed5a0ae0a94a5185209ae7c6d0 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -1064,7 +1766,16 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^18.0.0": +"builtins@npm:^5.0.0": + version: 5.1.0 + resolution: "builtins@npm:5.1.0" + dependencies: + semver: "npm:^7.0.0" + checksum: 10c0/3c32fe5bd7ed4ff7dbd6fb14bcb9d7eaa7e967327f1899cd336f8625d3f46fceead0a53528f1e332aeaee757034ebb307cb2f1a37af2b86a3c5ad4845d01c0c8 + languageName: node + linkType: hard + +"cacache@npm:^18.0.0, cacache@npm:^18.0.2": version: 18.0.2 resolution: "cacache@npm:18.0.2" dependencies: @@ -1084,6 +1795,19 @@ __metadata: languageName: node linkType: hard +"call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.1" + checksum: 10c0/a3ded2e423b8e2a265983dba81c27e125b48eefb2655e7dfab6be597088da3d47c47976c24bc51b8fd9af1061f8f87b4ab78a314f3c77784b2ae2ba535ad8b8d + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -1091,6 +1815,17 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^2.3.2, chalk@npm:^2.4.2": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: "npm:^3.2.1" + escape-string-regexp: "npm:^1.0.5" + supports-color: "npm:^5.3.0" + checksum: 10c0/e6543f02ec877732e3a2d1c3c3323ddb4d39fbab687c23f526e25bd4c6a9bf3b83a696e8c769d078e04e5754921648f7821b2a2acfd16c550435fd630026e073 + languageName: node + linkType: hard + "chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -1101,6 +1836,20 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.3.0": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 10c0/8297d436b2c0f95801103ff2ef67268d362021b8210daf8ddbe349695333eb3610a71122172ff3b0272f1ef2cf7cc2c41fdaa4715f52e49ffe04c56340feed09 + languageName: node + linkType: hard + +"char-regex@npm:^1.0.2": + version: 1.0.2 + resolution: "char-regex@npm:1.0.2" + checksum: 10c0/57a09a86371331e0be35d9083ba429e86c4f4648ecbe27455dbfb343037c16ee6fdc7f6b61f433a57cc5ded5561d71c56a150e018f40c2ffb7bc93a26dae341e + languageName: node + linkType: hard + "chokidar@npm:^3.5.2": version: 3.6.0 resolution: "chokidar@npm:3.6.0" @@ -1127,6 +1876,22 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^4.0.0": + version: 4.0.0 + resolution: "ci-info@npm:4.0.0" + checksum: 10c0/ecc003e5b60580bd081d83dd61d398ddb8607537f916313e40af4667f9c92a1243bd8e8a591a5aa78e418afec245dbe8e90a0e26e39ca0825129a99b978dd3f9 + languageName: node + linkType: hard + +"cidr-regex@npm:^4.0.4": + version: 4.0.5 + resolution: "cidr-regex@npm:4.0.5" + dependencies: + ip-regex: "npm:^5.0.0" + checksum: 10c0/a71ca3b46e63b59def1501377d31f239972c6363218f2894ece3638b4b1e5f30dced8e5603811a6213453d4b57b8d672b7b578ff93839a4866cfef0d4923ef3c + languageName: node + linkType: hard + "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -1134,6 +1899,99 @@ __metadata: languageName: node linkType: hard +"clean-stack@npm:^5.2.0": + version: 5.2.0 + resolution: "clean-stack@npm:5.2.0" + dependencies: + escape-string-regexp: "npm:5.0.0" + checksum: 10c0/0de47a4152e49dcdeede5f47d7bb9a39a3ea748acb1cd2f0160dbee972d920be81390cb4c5566e6b795791b9efb12359e89fdd7c2e63b36025d59529558570f1 + languageName: node + linkType: hard + +"cli-columns@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-columns@npm:4.0.0" + dependencies: + string-width: "npm:^4.2.3" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/f724c874dba09376f7b2d6c70431d8691d5871bd5d26c6f658dd56b514e668ed5f5b8d803fb7e29f4000fc7f3a6d038d415b892ae7fa3dcd9cc458c07df17871 + languageName: node + linkType: hard + +"cli-highlight@npm:^2.1.11": + version: 2.1.11 + resolution: "cli-highlight@npm:2.1.11" + dependencies: + chalk: "npm:^4.0.0" + highlight.js: "npm:^10.7.1" + mz: "npm:^2.4.0" + parse5: "npm:^5.1.1" + parse5-htmlparser2-tree-adapter: "npm:^6.0.0" + yargs: "npm:^16.0.0" + bin: + highlight: bin/highlight + checksum: 10c0/b5b4af3b968aa9df77eee449a400fbb659cf47c4b03a395370bd98d5554a00afaa5819b41a9a8a1ca0d37b0b896a94e57c65289b37359a25b700b1f56eb04852 + languageName: node + linkType: hard + +"cli-table3@npm:^0.6.3, cli-table3@npm:^0.6.4": + version: 0.6.4 + resolution: "cli-table3@npm:0.6.4" + dependencies: + "@colors/colors": "npm:1.5.0" + string-width: "npm:^4.2.0" + dependenciesMeta: + "@colors/colors": + optional: true + checksum: 10c0/8233c3d588db19122ed62a64256c7f0208232d2cece89a6cd7732481887fd9dcef69d976c4719149e77ccbf0a68f637bd5923536adccf6cdea051eeffa0ef1c2 + languageName: node + linkType: hard + +"cliui@npm:^7.0.2": + version: 7.0.4 + resolution: "cliui@npm:7.0.4" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.0" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/6035f5daf7383470cef82b3d3db00bec70afb3423538c50394386ffbbab135e26c3689c41791f911fa71b62d13d3863c712fdd70f0fbdffd938a1e6fd09aac00 + languageName: node + linkType: hard + +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + +"clone@npm:^1.0.2": + version: 1.0.4 + resolution: "clone@npm:1.0.4" + checksum: 10c0/2176952b3649293473999a95d7bebfc9dc96410f6cbd3d2595cf12fd401f63a4bf41a7adbfd3ab2ff09ed60cb9870c58c6acdd18b87767366fabfc163700f13b + languageName: node + linkType: hard + +"cmd-shim@npm:^6.0.0": + version: 6.0.2 + resolution: "cmd-shim@npm:6.0.2" + checksum: 10c0/c34cadcfa32ee923fd055fc6edbd933e56432228b7d8078ea0120e24949343fbc1b24066f817eb4f58a66141443463591c545c0d08cf461203bf20d0f8c55ff2 + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: "npm:1.1.3" + checksum: 10c0/5ad3c534949a8c68fca8fbc6f09068f435f0ad290ab8b2f76841b9e6af7e0bb57b98cb05b0e19fe33f5d91e5a8611ad457e5f69e0a484caad1f7487fd0e8253c + languageName: node + linkType: hard + "color-convert@npm:^2.0.1": version: 2.0.1 resolution: "color-convert@npm:2.0.1" @@ -1143,6 +2001,13 @@ __metadata: languageName: node linkType: hard +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 10c0/566a3d42cca25b9b3cd5528cd7754b8e89c0eb646b7f214e8e2eaddb69994ac5f0557d9c175eb5d8f0ad73531140d9c47525085ee752a91a2ab15ab459caf6d6 + languageName: node + linkType: hard + "color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" @@ -1150,6 +2015,25 @@ __metadata: languageName: node linkType: hard +"color-support@npm:^1.1.3": + version: 1.1.3 + resolution: "color-support@npm:1.1.3" + bin: + color-support: bin.js + checksum: 10c0/8ffeaa270a784dc382f62d9be0a98581db43e11eee301af14734a6d089bd456478b1a8b3e7db7ca7dc5b18a75f828f775c44074020b51c05fc00e6d0992b1cc6 + languageName: node + linkType: hard + +"columnify@npm:^1.6.0": + version: 1.6.0 + resolution: "columnify@npm:1.6.0" + dependencies: + strip-ansi: "npm:^6.0.1" + wcwidth: "npm:^1.0.0" + checksum: 10c0/25b90b59129331bbb8b0c838f8df69924349b83e8eab9549f431062a20a39094b8d744bb83265be38fd5d03140ce4bfbd85837c293f618925e83157ae9535f1d + languageName: node + linkType: hard + "combined-stream@npm:^1.0.8": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" @@ -1173,6 +2057,13 @@ __metadata: languageName: node linkType: hard +"common-ancestor-path@npm:^1.0.1": + version: 1.0.1 + resolution: "common-ancestor-path@npm:1.0.1" + checksum: 10c0/390c08d2a67a7a106d39499c002d827d2874966d938012453fd7ca34cd306881e2b9d604f657fa7a8e6e4896d67f39ebc09bf1bfd8da8ff318e0fb7a8752c534 + languageName: node + linkType: hard + "commondir@npm:^1.0.1": version: 1.0.1 resolution: "commondir@npm:1.0.1" @@ -1180,6 +2071,16 @@ __metadata: languageName: node linkType: hard +"compare-func@npm:^2.0.0": + version: 2.0.0 + resolution: "compare-func@npm:2.0.0" + dependencies: + array-ify: "npm:^1.0.0" + dot-prop: "npm:^5.1.0" + checksum: 10c0/78bd4dd4ed311a79bd264c9e13c36ed564cde657f1390e699e0f04b8eee1fc06ffb8698ce2dfb5fbe7342d509579c82d4e248f08915b708f77f7b72234086cc3 + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -1187,7 +2088,101 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2": +"config-chain@npm:^1.1.11": + version: 1.1.13 + resolution: "config-chain@npm:1.1.13" + dependencies: + ini: "npm:^1.3.4" + proto-list: "npm:~1.2.1" + checksum: 10c0/39d1df18739d7088736cc75695e98d7087aea43646351b028dfabd5508d79cf6ef4c5bcd90471f52cd87ae470d1c5490c0a8c1a292fbe6ee9ff688061ea0963e + languageName: node + linkType: hard + +"console-control-strings@npm:^1.1.0": + version: 1.1.0 + resolution: "console-control-strings@npm:1.1.0" + checksum: 10c0/7ab51d30b52d461412cd467721bb82afe695da78fff8f29fe6f6b9cbaac9a2328e27a22a966014df9532100f6dd85370460be8130b9c677891ba36d96a343f50 + languageName: node + linkType: hard + +"conventional-changelog-angular@npm:^7.0.0": + version: 7.0.0 + resolution: "conventional-changelog-angular@npm:7.0.0" + dependencies: + compare-func: "npm:^2.0.0" + checksum: 10c0/90e73e25e224059b02951b6703b5f8742dc2a82c1fea62163978e6735fd3ab04350897a8fc6f443ec6b672d6b66e28a0820e833e544a0101f38879e5e6289b7e + languageName: node + linkType: hard + +"conventional-changelog-writer@npm:^7.0.0": + version: 7.0.1 + resolution: "conventional-changelog-writer@npm:7.0.1" + dependencies: + conventional-commits-filter: "npm:^4.0.0" + handlebars: "npm:^4.7.7" + json-stringify-safe: "npm:^5.0.1" + meow: "npm:^12.0.1" + semver: "npm:^7.5.2" + split2: "npm:^4.0.0" + bin: + conventional-changelog-writer: cli.mjs + checksum: 10c0/ec51708c33860777f2b85f1df588aed918cab08919146cdfac8f271e31c0caee22c5c50df78e4ce358022e58f65c8de77fd6a5fb529f4bb5ba27c2d1e072750f + languageName: node + linkType: hard + +"conventional-commits-filter@npm:^4.0.0": + version: 4.0.0 + resolution: "conventional-commits-filter@npm:4.0.0" + checksum: 10c0/b26ea11ebb38218cb3cbbaf7d68b0f7c3b0eb7ad69afe9c9431d91e784acbebaeda7a095127ae5a7f8b75087d62b44e8e69d63426ff02b49f7dd504755934247 + languageName: node + linkType: hard + +"conventional-commits-parser@npm:^5.0.0": + version: 5.0.0 + resolution: "conventional-commits-parser@npm:5.0.0" + dependencies: + JSONStream: "npm:^1.3.5" + is-text-path: "npm:^2.0.0" + meow: "npm:^12.0.1" + split2: "npm:^4.0.0" + bin: + conventional-commits-parser: cli.mjs + checksum: 10c0/c9e542f4884119a96a6bf3311ff62cdee55762d8547f4c745ae3ebdc50afe4ba7691e165e34827d5cf63283cbd93ab69917afd7922423075b123d5d9a7a82ed2 + languageName: node + linkType: hard + +"convert-hrtime@npm:^5.0.0": + version: 5.0.0 + resolution: "convert-hrtime@npm:5.0.0" + checksum: 10c0/2092e51aab205e1141440e84e2a89f8881e68e47c1f8bc168dfd7c67047d8f1db43bac28044bc05749205651fead4e7910f52c7bb6066213480df99e333e9f47 + languageName: node + linkType: hard + +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 10c0/90a0e40abbddfd7618f8ccd63a74d88deea94e77d0e8dbbea059fa7ebebb8fbb4e2909667fe26f3a467073de1a542ebe6ae4c73a73745ac5833786759cd906c9 + languageName: node + linkType: hard + +"cosmiconfig@npm:^9.0.0": + version: 9.0.0 + resolution: "cosmiconfig@npm:9.0.0" + dependencies: + env-paths: "npm:^2.2.1" + import-fresh: "npm:^3.3.0" + js-yaml: "npm:^4.1.0" + parse-json: "npm:^5.2.0" + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/1c1703be4f02a250b1d6ca3267e408ce16abfe8364193891afc94c2d5c060b69611fdc8d97af74b7e6d5d1aac0ab2fb94d6b079573146bc2d756c2484ce5f0ee + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -1198,6 +2193,57 @@ __metadata: languageName: node linkType: hard +"crypto-random-string@npm:^4.0.0": + version: 4.0.0 + resolution: "crypto-random-string@npm:4.0.0" + dependencies: + type-fest: "npm:^1.0.1" + checksum: 10c0/16e11a3c8140398f5408b7fded35a961b9423c5dac39a60cbbd08bd3f0e07d7de130e87262adea7db03ec1a7a4b7551054e0db07ee5408b012bac5400cfc07a5 + languageName: node + linkType: hard + +"cssesc@npm:^3.0.0": + version: 3.0.0 + resolution: "cssesc@npm:3.0.0" + bin: + cssesc: bin/cssesc + checksum: 10c0/6bcfd898662671be15ae7827120472c5667afb3d7429f1f917737f3bf84c4176003228131b643ae74543f17a394446247df090c597bb9a728cce298606ed0aa7 + languageName: node + linkType: hard + +"data-view-buffer@npm:^1.0.1": + version: 1.0.1 + resolution: "data-view-buffer@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.1" + checksum: 10c0/8984119e59dbed906a11fcfb417d7d861936f16697a0e7216fe2c6c810f6b5e8f4a5281e73f2c28e8e9259027190ac4a33e2a65fdd7fa86ac06b76e838918583 + languageName: node + linkType: hard + +"data-view-byte-length@npm:^1.0.1": + version: 1.0.1 + resolution: "data-view-byte-length@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.1" + checksum: 10c0/b7d9e48a0cf5aefed9ab7d123559917b2d7e0d65531f43b2fd95b9d3a6b46042dd3fca597c42bba384e66b70d7ad66ff23932f8367b241f53d93af42cfe04ec2 + languageName: node + linkType: hard + +"data-view-byte-offset@npm:^1.0.0": + version: 1.0.0 + resolution: "data-view-byte-offset@npm:1.0.0" + dependencies: + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.1" + checksum: 10c0/21b0d2e53fd6e20cc4257c873bf6d36d77bd6185624b84076c0a1ddaa757b49aaf076254006341d35568e89f52eecd1ccb1a502cfb620f2beca04f48a6a62a8f + languageName: node + linkType: hard + "dcdx@workspace:.": version: 0.0.0-use.local resolution: "dcdx@workspace:." @@ -1224,6 +2270,7 @@ __metadata: pg: "npm:8.11.5" rollup: "npm:4.14.1" rollup-plugin-executable: "npm:1.6.3" + semantic-release: "npm:23.0.8" sequelize: "npm:6.37.2" simple-git: "npm:3.24.0" tedious: "npm:18.1.0" @@ -1234,7 +2281,7 @@ __metadata: languageName: unknown linkType: soft -"debug@npm:4, debug@npm:^4, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -1246,6 +2293,13 @@ __metadata: languageName: node linkType: hard +"deep-extend@npm:^0.6.0": + version: 0.6.0 + resolution: "deep-extend@npm:0.6.0" + checksum: 10c0/1c6b0abcdb901e13a44c7d699116d3d4279fdb261983122a3783e7273844d5f2537dc2e1c454a23fcf645917f93fbf8d07101c1d03c015a87faa662755212566 + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -1260,6 +2314,26 @@ __metadata: languageName: node linkType: hard +"defaults@npm:^1.0.3": + version: 1.0.4 + resolution: "defaults@npm:1.0.4" + dependencies: + clone: "npm:^1.0.2" + checksum: 10c0/9cfbe498f5c8ed733775db62dfd585780387d93c17477949e1670bfcfb9346e0281ce8c4bf9f4ac1fc0f9b851113bd6dc9e41182ea1644ccd97de639fa13c35a + languageName: node + linkType: hard + +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + "define-lazy-prop@npm:^2.0.0": version: 2.0.0 resolution: "define-lazy-prop@npm:2.0.0" @@ -1267,6 +2341,17 @@ __metadata: languageName: node linkType: hard +"define-properties@npm:^1.1.3, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" + dependencies: + define-data-property: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.0" + object-keys: "npm:^1.1.1" + checksum: 10c0/88a152319ffe1396ccc6ded510a3896e77efac7a1bfbaa174a7b00414a1747377e0bb525d303794a47cf30e805c2ec84e575758512c6e44a993076d29fd4e6c3 + languageName: node + linkType: hard + "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -1281,6 +2366,13 @@ __metadata: languageName: node linkType: hard +"diff@npm:^5.1.0": + version: 5.2.0 + resolution: "diff@npm:5.2.0" + checksum: 10c0/aed0941f206fe261ecb258dc8d0ceea8abbde3ace5827518ff8d302f0fc9cc81ce116c4d8f379151171336caf0516b79e01abdc1ed1201b6440d895a66689eb4 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -1299,6 +2391,15 @@ __metadata: languageName: node linkType: hard +"dot-prop@npm:^5.1.0": + version: 5.3.0 + resolution: "dot-prop@npm:5.3.0" + dependencies: + is-obj: "npm:^2.0.0" + checksum: 10c0/93f0d343ef87fe8869320e62f2459f7e70f49c6098d948cc47e060f4a3f827d0ad61e83cb82f2bd90cd5b9571b8d334289978a43c0f98fea4f0e99ee8faa0599 + languageName: node + linkType: hard + "dottie@npm:^2.0.6": version: 2.0.6 resolution: "dottie@npm:2.0.6" @@ -1306,7 +2407,16 @@ __metadata: languageName: node linkType: hard -"eastasianwidth@npm:^0.2.0": +"duplexer2@npm:~0.1.0": + version: 0.1.4 + resolution: "duplexer2@npm:0.1.4" + dependencies: + readable-stream: "npm:^2.0.2" + checksum: 10c0/0765a4cc6fe6d9615d43cc6dbccff6f8412811d89a6f6aa44828ca9422a0a469625ce023bf81cee68f52930dbedf9c5716056ff264ac886612702d134b5e39b4 + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 @@ -1336,6 +2446,13 @@ __metadata: languageName: node linkType: hard +"emojilib@npm:^2.4.0": + version: 2.4.0 + resolution: "emojilib@npm:2.4.0" + checksum: 10c0/6e66ba8921175842193f974e18af448bb6adb0cf7aeea75e08b9d4ea8e9baba0e4a5347b46ed901491dcaba277485891c33a8d70b0560ca5cc9672a94c21ab8f + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -1345,7 +2462,17 @@ __metadata: languageName: node linkType: hard -"env-paths@npm:^2.2.0": +"env-ci@npm:^11.0.0": + version: 11.0.0 + resolution: "env-ci@npm:11.0.0" + dependencies: + execa: "npm:^8.0.0" + java-properties: "npm:^1.0.2" + checksum: 10c0/8a1805c5011ec890db182705b02ed8883e13eac869195000ba8fc7feca78fa13c73d5219255c46c11de871173d0eb53c5f1c1a54a88d8920f271a1aea33a780c + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": version: 2.2.1 resolution: "env-paths@npm:2.2.1" checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 @@ -1359,6 +2486,137 @@ __metadata: languageName: node linkType: hard +"error-ex@npm:^1.3.1": + version: 1.3.2 + resolution: "error-ex@npm:1.3.2" + dependencies: + is-arrayish: "npm:^0.2.1" + checksum: 10c0/ba827f89369b4c93382cfca5a264d059dfefdaa56ecc5e338ffa58a6471f5ed93b71a20add1d52290a4873d92381174382658c885ac1a2305f7baca363ce9cce + languageName: node + linkType: hard + +"es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3, es-abstract@npm:^1.23.0": + version: 1.23.3 + resolution: "es-abstract@npm:1.23.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.1" + arraybuffer.prototype.slice: "npm:^1.0.3" + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.7" + data-view-buffer: "npm:^1.0.1" + data-view-byte-length: "npm:^1.0.1" + data-view-byte-offset: "npm:^1.0.0" + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + es-set-tostringtag: "npm:^2.0.3" + es-to-primitive: "npm:^1.2.1" + function.prototype.name: "npm:^1.1.6" + get-intrinsic: "npm:^1.2.4" + get-symbol-description: "npm:^1.0.2" + globalthis: "npm:^1.0.3" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + has-proto: "npm:^1.0.3" + has-symbols: "npm:^1.0.3" + hasown: "npm:^2.0.2" + internal-slot: "npm:^1.0.7" + is-array-buffer: "npm:^3.0.4" + is-callable: "npm:^1.2.7" + is-data-view: "npm:^1.0.1" + is-negative-zero: "npm:^2.0.3" + is-regex: "npm:^1.1.4" + is-shared-array-buffer: "npm:^1.0.3" + is-string: "npm:^1.0.7" + is-typed-array: "npm:^1.1.13" + is-weakref: "npm:^1.0.2" + object-inspect: "npm:^1.13.1" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.5" + regexp.prototype.flags: "npm:^1.5.2" + safe-array-concat: "npm:^1.1.2" + safe-regex-test: "npm:^1.0.3" + string.prototype.trim: "npm:^1.2.9" + string.prototype.trimend: "npm:^1.0.8" + string.prototype.trimstart: "npm:^1.0.8" + typed-array-buffer: "npm:^1.0.2" + typed-array-byte-length: "npm:^1.0.1" + typed-array-byte-offset: "npm:^1.0.2" + typed-array-length: "npm:^1.0.6" + unbox-primitive: "npm:^1.0.2" + which-typed-array: "npm:^1.1.15" + checksum: 10c0/d27e9afafb225c6924bee9971a7f25f20c314f2d6cb93a63cada4ac11dcf42040896a6c22e5fb8f2a10767055ed4ddf400be3b1eb12297d281726de470b75666 + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: "npm:^1.2.4" + checksum: 10c0/6bf3191feb7ea2ebda48b577f69bdfac7a2b3c9bcf97307f55fd6ef1bbca0b49f0c219a935aca506c993d8c5d8bddd937766cb760cd5e5a1071351f2df9f9aa4 + languageName: node + linkType: hard + +"es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0": + version: 1.0.0 + resolution: "es-object-atoms@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10c0/1fed3d102eb27ab8d983337bb7c8b159dd2a1e63ff833ec54eea1311c96d5b08223b433060ba240541ca8adba9eee6b0a60cdbf2f80634b784febc9cc8b687b4 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.0.3": + version: 2.0.3 + resolution: "es-set-tostringtag@npm:2.0.3" + dependencies: + get-intrinsic: "npm:^1.2.4" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.1" + checksum: 10c0/f22aff1585eb33569c326323f0b0d175844a1f11618b86e193b386f8be0ea9474cfbe46df39c45d959f7aa8f6c06985dc51dd6bce5401645ec5a74c4ceaa836a + languageName: node + linkType: hard + +"es-to-primitive@npm:^1.2.1": + version: 1.2.1 + resolution: "es-to-primitive@npm:1.2.1" + dependencies: + is-callable: "npm:^1.1.4" + is-date-object: "npm:^1.0.1" + is-symbol: "npm:^1.0.2" + checksum: 10c0/0886572b8dc075cb10e50c0af62a03d03a68e1e69c388bd4f10c0649ee41b1fbb24840a1b7e590b393011b5cdbe0144b776da316762653685432df37d6de60f1 + languageName: node + linkType: hard + +"escalade@npm:^3.1.1": + version: 3.1.2 + resolution: "escalade@npm:3.1.2" + checksum: 10c0/6b4adafecd0682f3aa1cd1106b8fff30e492c7015b178bc81b2d2f75106dabea6c6d6e8508fc491bd58e597c74abb0e8e2368f943ecb9393d4162e3c2f3cf287 + languageName: node + linkType: hard + +"escape-string-regexp@npm:5.0.0": + version: 5.0.0 + resolution: "escape-string-regexp@npm:5.0.0" + checksum: 10c0/6366f474c6f37a802800a435232395e04e9885919873e382b157ab7e8f0feb8fed71497f84a6f6a81a49aab41815522f5839112bd38026d203aea0c91622df95 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 + languageName: node + linkType: hard + "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -1507,6 +2765,23 @@ __metadata: languageName: node linkType: hard +"execa@npm:^8.0.0": + version: 8.0.1 + resolution: "execa@npm:8.0.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^3.0.0" + checksum: 10c0/2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af + languageName: node + linkType: hard + "exit-hook@npm:4.0.0": version: 4.0.0 resolution: "exit-hook@npm:4.0.0" @@ -1528,7 +2803,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9": +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" dependencies: @@ -1555,6 +2830,13 @@ __metadata: languageName: node linkType: hard +"fastest-levenshtein@npm:^1.0.16": + version: 1.0.16 + resolution: "fastest-levenshtein@npm:1.0.16" + checksum: 10c0/7e3d8ae812a7f4fdf8cad18e9cde436a39addf266a5986f653ea0d81e0de0900f50c0f27c6d5aff3f686bcb48acbd45be115ae2216f36a6a13a7dbbf5cad878b + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.17.1 resolution: "fastq@npm:1.17.1" @@ -1564,6 +2846,24 @@ __metadata: languageName: node linkType: hard +"figures@npm:^2.0.0": + version: 2.0.0 + resolution: "figures@npm:2.0.0" + dependencies: + escape-string-regexp: "npm:^1.0.5" + checksum: 10c0/5dc5a75fec3e7e04ae65d6ce51d28b3e70d4656c51b06996b6fdb2cb5b542df512e3b3c04482f5193a964edddafa5521479ff948fa84e12ff556e53e094ab4ce + languageName: node + linkType: hard + +"figures@npm:^6.0.0": + version: 6.1.0 + resolution: "figures@npm:6.1.0" + dependencies: + is-unicode-supported: "npm:^2.0.0" + checksum: 10c0/9159df4264d62ef447a3931537de92f5012210cf5135c35c010df50a2169377581378149abfe1eb238bd6acbba1c0d547b1f18e0af6eee49e30363cedaffcfe4 + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -1582,6 +2882,22 @@ __metadata: languageName: node linkType: hard +"find-up-simple@npm:^1.0.0": + version: 1.0.0 + resolution: "find-up-simple@npm:1.0.0" + checksum: 10c0/de1ad5e55c8c162f5600fe3297bb55a3da5cd9cb8c6755e463ec1d52c4c15a84e312a68397fb5962d13263b3dbd4ea294668c465ccacc41291d7cc97588769f9 + languageName: node + linkType: hard + +"find-up@npm:^2.0.0": + version: 2.1.0 + resolution: "find-up@npm:2.1.0" + dependencies: + locate-path: "npm:^2.0.0" + checksum: 10c0/c080875c9fe28eb1962f35cbe83c683796a0321899f1eed31a37577800055539815de13d53495049697d3ba313013344f843bb9401dd337a1b832be5edfc6840 + languageName: node + linkType: hard + "find-up@npm:^5.0.0": version: 5.0.0 resolution: "find-up@npm:5.0.0" @@ -1592,6 +2908,16 @@ __metadata: languageName: node linkType: hard +"find-versions@npm:^6.0.0": + version: 6.0.0 + resolution: "find-versions@npm:6.0.0" + dependencies: + semver-regex: "npm:^4.0.5" + super-regex: "npm:^1.0.0" + checksum: 10c0/1e38da3058f389c8657cd6f47fbcf12412051e7d2d14017594b8ca54ec239d19058f2d9dde80f27415726ab62822e32e3ed0a81141cfc206a3b8c8f0d87a5732 + languageName: node + linkType: hard + "flat-cache@npm:^4.0.0": version: 4.0.1 resolution: "flat-cache@npm:4.0.1" @@ -1619,6 +2945,15 @@ __metadata: languageName: node linkType: hard +"for-each@npm:^0.3.3": + version: 0.3.3 + resolution: "for-each@npm:0.3.3" + dependencies: + is-callable: "npm:^1.1.3" + checksum: 10c0/22330d8a2db728dbf003ec9182c2d421fbcd2969b02b4f97ec288721cda63eb28f2c08585ddccd0f77cb2930af8d958005c9e72f47141dc51816127a118f39aa + languageName: node + linkType: hard + "foreground-child@npm:^3.1.0": version: 3.1.1 resolution: "foreground-child@npm:3.1.1" @@ -1640,6 +2975,27 @@ __metadata: languageName: node linkType: hard +"from2@npm:^2.3.0": + version: 2.3.0 + resolution: "from2@npm:2.3.0" + dependencies: + inherits: "npm:^2.0.1" + readable-stream: "npm:^2.0.0" + checksum: 10c0/f87f7a2e4513244d551454a7f8324ef1f7837864a8701c536417286ec19ff4915606b1dfa8909a21b7591ebd8440ffde3642f7c303690b9a4d7c832d62248aa1 + languageName: node + linkType: hard + +"fs-extra@npm:^11.0.0": + version: 11.2.0 + resolution: "fs-extra@npm:11.2.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/d77a9a9efe60532d2e790e938c81a02c1b24904ef7a3efb3990b835514465ba720e99a6ea56fd5e2db53b4695319b644d76d5a0e9988a2beef80aa7b1da63398 + languageName: node + linkType: hard + "fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -1649,7 +3005,7 @@ __metadata: languageName: node linkType: hard -"fs-minipass@npm:^3.0.0": +"fs-minipass@npm:^3.0.0, fs-minipass@npm:^3.0.3": version: 3.0.3 resolution: "fs-minipass@npm:3.0.3" dependencies: @@ -1691,6 +3047,48 @@ __metadata: languageName: node linkType: hard +"function-timeout@npm:^1.0.1": + version: 1.0.1 + resolution: "function-timeout@npm:1.0.1" + checksum: 10c0/cd66b4c09444b6bdca3d43ced03432262bef4f1ea597f53d10505a77cae4c188531892c41ed7f22cffc9b34a7c84dae38436278297186072400d66233c752d82 + languageName: node + linkType: hard + +"function.prototype.name@npm:^1.1.6": + version: 1.1.6 + resolution: "function.prototype.name@npm:1.1.6" + dependencies: + call-bind: "npm:^1.0.2" + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + functions-have-names: "npm:^1.2.3" + checksum: 10c0/9eae11294905b62cb16874adb4fc687927cda3162285e0ad9612e6a1d04934005d46907362ea9cdb7428edce05a2f2c3dabc3b2d21e9fd343e9bb278230ad94b + languageName: node + linkType: hard + +"functions-have-names@npm:^1.2.3": + version: 1.2.3 + resolution: "functions-have-names@npm:1.2.3" + checksum: 10c0/33e77fd29bddc2d9bb78ab3eb854c165909201f88c75faa8272e35899e2d35a8a642a15e7420ef945e1f64a9670d6aa3ec744106b2aa42be68ca5114025954ca + languageName: node + linkType: hard + +"gauge@npm:^5.0.0": + version: 5.0.1 + resolution: "gauge@npm:5.0.1" + dependencies: + aproba: "npm:^1.0.3 || ^2.0.0" + color-support: "npm:^1.1.3" + console-control-strings: "npm:^1.1.0" + has-unicode: "npm:^2.0.1" + signal-exit: "npm:^4.0.1" + string-width: "npm:^4.2.3" + strip-ansi: "npm:^6.0.1" + wide-align: "npm:^1.1.5" + checksum: 10c0/845f9a2534356cd0e9c1ae590ed471bbe8d74c318915b92a34e8813b8d3441ca8e0eb0fa87a48081e70b63b84d398c5e66a13b8e8040181c10b9d77e9fe3287f + languageName: node + linkType: hard + "generate-function@npm:^2.3.1": version: 2.3.1 resolution: "generate-function@npm:2.3.1" @@ -1700,6 +3098,72 @@ __metadata: languageName: node linkType: hard +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": + version: 1.2.4 + resolution: "get-intrinsic@npm:1.2.4" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + has-proto: "npm:^1.0.1" + has-symbols: "npm:^1.0.3" + hasown: "npm:^2.0.0" + checksum: 10c0/0a9b82c16696ed6da5e39b1267104475c47e3a9bdbe8b509dfe1710946e38a87be70d759f4bb3cda042d76a41ef47fe769660f3b7c0d1f68750299344ffb15b7 + languageName: node + linkType: hard + +"get-stream@npm:^6.0.0": + version: 6.0.1 + resolution: "get-stream@npm:6.0.1" + checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 + languageName: node + linkType: hard + +"get-stream@npm:^7.0.0": + version: 7.0.1 + resolution: "get-stream@npm:7.0.1" + checksum: 10c0/d0e34acd2f65c80ec2bef1f8add0c36bd4819d06aedd221eba59382d314ae980ae25b68e0000145798a6f7e2f541417f78b44fdc2a3eb942b2b28cfcce69cc71 + languageName: node + linkType: hard + +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: 10c0/5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 + languageName: node + linkType: hard + +"get-symbol-description@npm:^1.0.2": + version: 1.0.2 + resolution: "get-symbol-description@npm:1.0.2" + dependencies: + call-bind: "npm:^1.0.5" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + checksum: 10c0/867be6d63f5e0eb026cb3b0ef695ec9ecf9310febb041072d2e142f260bd91ced9eeb426b3af98791d1064e324e653424afa6fd1af17dee373bea48ae03162bc + languageName: node + linkType: hard + +"git-log-parser@npm:^1.2.0": + version: 1.2.0 + resolution: "git-log-parser@npm:1.2.0" + dependencies: + argv-formatter: "npm:~1.0.0" + spawn-error-forwarder: "npm:~1.0.0" + split2: "npm:~1.0.0" + stream-combiner2: "npm:~1.1.1" + through2: "npm:~2.0.0" + traverse: "npm:~0.6.6" + checksum: 10c0/16cd5edab3fa7cd77761f6b81e9a60f9e7d30980bf9adc2b7a86e575923547dfa4b9dae42d71f2eeed2abe7a70c04205c96155a49f1c40745637728a03271a59 + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -1718,7 +3182,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10": +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.12": version: 10.3.12 resolution: "glob@npm:10.3.12" dependencies: @@ -1753,6 +3217,15 @@ __metadata: languageName: node linkType: hard +"globalthis@npm:^1.0.3": + version: 1.0.3 + resolution: "globalthis@npm:1.0.3" + dependencies: + define-properties: "npm:^1.1.3" + checksum: 10c0/0db6e9af102a5254630351557ac15e6909bc7459d3e3f6b001e59fe784c96d31108818f032d9095739355a88467459e6488ff16584ee6250cd8c27dec05af4b0 + languageName: node + linkType: hard + "globby@npm:^11.1.0": version: 11.1.0 resolution: "globby@npm:11.1.0" @@ -1767,7 +3240,37 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.2.6": +"globby@npm:^14.0.0": + version: 14.0.1 + resolution: "globby@npm:14.0.1" + dependencies: + "@sindresorhus/merge-streams": "npm:^2.1.0" + fast-glob: "npm:^3.3.2" + ignore: "npm:^5.2.4" + path-type: "npm:^5.0.0" + slash: "npm:^5.1.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10c0/749a6be91cf455c161ebb5c9130df3991cb9fd7568425db850a8279a6cf45acd031c5069395beb7aeb4dd606b64f0d6ff8116c93726178d8e6182fee58c2736d + languageName: node + linkType: hard + +"gopd@npm:^1.0.1": + version: 1.0.1 + resolution: "gopd@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.1.3" + checksum: 10c0/505c05487f7944c552cee72087bf1567debb470d4355b1335f2c262d218ebbff805cd3715448fe29b4b380bae6912561d0467233e4165830efd28da241418c63 + languageName: node + linkType: hard + +"graceful-fs@npm:4.2.10": + version: 4.2.10 + resolution: "graceful-fs@npm:4.2.10" + checksum: 10c0/4223a833e38e1d0d2aea630c2433cfb94ddc07dfc11d511dbd6be1d16688c5be848acc31f9a5d0d0ddbfb56d2ee5a6ae0278aceeb0ca6a13f27e06b9956fb952 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -1781,6 +3284,31 @@ __metadata: languageName: node linkType: hard +"handlebars@npm:^4.7.7": + version: 4.7.8 + resolution: "handlebars@npm:4.7.8" + dependencies: + minimist: "npm:^1.2.5" + neo-async: "npm:^2.6.2" + source-map: "npm:^0.6.1" + uglify-js: "npm:^3.1.4" + wordwrap: "npm:^1.0.0" + dependenciesMeta: + uglify-js: + optional: true + bin: + handlebars: bin/handlebars + checksum: 10c0/7aff423ea38a14bb379316f3857fe0df3c5d66119270944247f155ba1f08e07a92b340c58edaa00cfe985c21508870ee5183e0634dcb53dd405f35c93ef7f10d + languageName: node + linkType: hard + +"has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2": + version: 1.0.2 + resolution: "has-bigints@npm:1.0.2" + checksum: 10c0/724eb1485bfa3cdff6f18d95130aa190561f00b3fcf9f19dc640baf8176b5917c143b81ec2123f8cddb6c05164a198c94b13e1377c497705ccc8e1a80306e83b + languageName: node + linkType: hard + "has-flag@npm:^3.0.0": version: 3.0.0 resolution: "has-flag@npm:3.0.0" @@ -1795,7 +3323,46 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.0": +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + +"has-proto@npm:^1.0.1, has-proto@npm:^1.0.3": + version: 1.0.3 + resolution: "has-proto@npm:1.0.3" + checksum: 10c0/35a6989f81e9f8022c2f4027f8b48a552de714938765d019dbea6bb547bd49ce5010a3c7c32ec6ddac6e48fc546166a3583b128f5a7add8b058a6d8b4afec205 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": + version: 1.0.3 + resolution: "has-symbols@npm:1.0.3" + checksum: 10c0/e6922b4345a3f37069cdfe8600febbca791c94988c01af3394d86ca3360b4b93928bbf395859158f88099cb10b19d98e3bbab7c9ff2c1bd09cf665ee90afa2c3 + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + +"has-unicode@npm:^2.0.1": + version: 2.0.1 + resolution: "has-unicode@npm:2.0.1" + checksum: 10c0/ebdb2f4895c26bb08a8a100b62d362e49b2190bcfd84b76bc4be1a3bd4d254ec52d0dd9f2fbcc093fc5eb878b20c52146f9dfd33e2686ed28982187be593b47c + languageName: node + linkType: hard + +"hasown@npm:^2.0.0, hasown@npm:^2.0.1, hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" dependencies: @@ -1804,6 +3371,29 @@ __metadata: languageName: node linkType: hard +"highlight.js@npm:^10.7.1": + version: 10.7.3 + resolution: "highlight.js@npm:10.7.3" + checksum: 10c0/073837eaf816922427a9005c56c42ad8786473dc042332dfe7901aa065e92bc3d94ebf704975257526482066abb2c8677cc0326559bb8621e046c21c5991c434 + languageName: node + linkType: hard + +"hook-std@npm:^3.0.0": + version: 3.0.0 + resolution: "hook-std@npm:3.0.0" + checksum: 10c0/51841e049b130347acb59fb129253891d95e56e6fa268d0bcf95eaca5223f3ca2032b7f0af5feb0c0f61c8571f7af29339f185280ff28a624d3ebdcb6080540b + languageName: node + linkType: hard + +"hosted-git-info@npm:^7.0.0, hosted-git-info@npm:^7.0.1": + version: 7.0.1 + resolution: "hosted-git-info@npm:7.0.1" + dependencies: + lru-cache: "npm:^10.0.1" + checksum: 10c0/361c4254f717f06d581a5a90aa0156a945e662e05ebbb533c1fa9935f10886d8247db48cbbcf9667f02e519e6479bf16dcdcf3124c3030e76c4c3ca2c88ee9d3 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -1831,6 +3421,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 10c0/5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 + languageName: node + linkType: hard + "iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" @@ -1854,14 +3451,23 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.2.0, ignore@npm:^5.3.1": +"ignore-walk@npm:^6.0.4": + version: 6.0.4 + resolution: "ignore-walk@npm:6.0.4" + dependencies: + minimatch: "npm:^9.0.0" + checksum: 10c0/6dd2ea369f3d32d90cb26ca6647bc6e112ed483433270ed89b8055dd708d00777c2cbc85b93b43f53e2100851277fd1539796a758ae4c64b84445d4f1da5fd8f + languageName: node + linkType: hard + +"ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1": version: 5.3.1 resolution: "ignore@npm:5.3.1" checksum: 10c0/703f7f45ffb2a27fb2c5a8db0c32e7dee66b33a225d28e8db4e1be6474795f606686a6e3bcc50e1aa12f2042db4c9d4a7d60af3250511de74620fbed052ea4cd languageName: node linkType: hard -"import-fresh@npm:^3.2.1": +"import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" dependencies: @@ -1871,6 +3477,23 @@ __metadata: languageName: node linkType: hard +"import-from-esm@npm:^1.0.3, import-from-esm@npm:^1.3.1": + version: 1.3.3 + resolution: "import-from-esm@npm:1.3.3" + dependencies: + debug: "npm:^4.3.4" + import-meta-resolve: "npm:^4.0.0" + checksum: 10c0/4287ff7e7b8ba52f4547a03be44105ad2cdad1d4bf15ba4f629649ece587633b1c1f14784f1e0f5441d5ac8967f59a64d7017d88d09d34624ebf81af9c48b55e + languageName: node + linkType: hard + +"import-meta-resolve@npm:^4.0.0": + version: 4.0.0 + resolution: "import-meta-resolve@npm:4.0.0" + checksum: 10c0/709375e01f8c3a87b7870991ca29c630d71bb7e22b7bb0f622613173d87b41b4043b4a983800e6d38ab3867496a46f82d30df0cbc2e55792c91c23193eea67a1 + languageName: node + linkType: hard + "imurmurhash@npm:^0.1.4": version: 0.1.4 resolution: "imurmurhash@npm:0.1.4" @@ -1885,6 +3508,20 @@ __metadata: languageName: node linkType: hard +"indent-string@npm:^5.0.0": + version: 5.0.0 + resolution: "indent-string@npm:5.0.0" + checksum: 10c0/8ee77b57d92e71745e133f6f444d6fa3ed503ad0e1bcd7e80c8da08b42375c07117128d670589725ed07b1978065803fa86318c309ba45415b7fe13e7f170220 + languageName: node + linkType: hard + +"index-to-position@npm:^0.1.2": + version: 0.1.2 + resolution: "index-to-position@npm:0.1.2" + checksum: 10c0/7c91bde8bafc22684b74a7a24915bee4691cba48352ddb4ebe3b20a3a87bc0fa7a05f586137245ca8f92222a11f341f7631ff7f38cd78a523505d2d02dbfa257 + languageName: node + linkType: hard + "inflection@npm:^1.13.4": version: 1.13.4 resolution: "inflection@npm:1.13.4" @@ -1902,50 +3539,177 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.4": +"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.4, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 languageName: node linkType: hard -"ip-address@npm:^9.0.5": - version: 9.0.5 - resolution: "ip-address@npm:9.0.5" - dependencies: - jsbn: "npm:1.1.0" - sprintf-js: "npm:^1.1.3" - checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc +"ini@npm:^1.3.4, ini@npm:~1.3.0": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a languageName: node linkType: hard -"is-binary-path@npm:~2.1.0": - version: 2.1.0 - resolution: "is-binary-path@npm:2.1.0" - dependencies: - binary-extensions: "npm:^2.0.0" - checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 +"ini@npm:^4.1.2": + version: 4.1.2 + resolution: "ini@npm:4.1.2" + checksum: 10c0/e0ffe587038e26ca1debfece6f5e52fd17f4e65be59bb481bb24b89cd2be31a71f619465918da215916b4deba7d1134c228c58fe5e0db66a71a472dee9b8f99c languageName: node linkType: hard -"is-builtin-module@npm:^3.2.1": - version: 3.2.1 - resolution: "is-builtin-module@npm:3.2.1" +"init-package-json@npm:^6.0.2": + version: 6.0.2 + resolution: "init-package-json@npm:6.0.2" dependencies: - builtin-modules: "npm:^3.3.0" - checksum: 10c0/5a66937a03f3b18803381518f0ef679752ac18cdb7dd53b5e23ee8df8d440558737bd8dcc04d2aae555909d2ecb4a81b5c0d334d119402584b61e6a003e31af1 + "@npmcli/package-json": "npm:^5.0.0" + npm-package-arg: "npm:^11.0.0" + promzard: "npm:^1.0.0" + read: "npm:^3.0.1" + semver: "npm:^7.3.5" + validate-npm-package-license: "npm:^3.0.4" + validate-npm-package-name: "npm:^5.0.0" + checksum: 10c0/b2e6331afda7228782c4db24f000d9131bc77a691a0845c911bfbb4ed775073ec0d0ecfacacbe74636fa2ed53122221846dc0409e5773ebf8477c31d22522428 languageName: node linkType: hard -"is-core-module@npm:^2.13.0": - version: 2.13.1 - resolution: "is-core-module@npm:2.13.1" +"internal-slot@npm:^1.0.7": + version: 1.0.7 + resolution: "internal-slot@npm:1.0.7" + dependencies: + es-errors: "npm:^1.3.0" + hasown: "npm:^2.0.0" + side-channel: "npm:^1.0.4" + checksum: 10c0/f8b294a4e6ea3855fc59551bbf35f2b832cf01fd5e6e2a97f5c201a071cc09b49048f856e484b67a6c721da5e55736c5b6ddafaf19e2dbeb4a3ff1821680de6c + languageName: node + linkType: hard + +"into-stream@npm:^7.0.0": + version: 7.0.0 + resolution: "into-stream@npm:7.0.0" + dependencies: + from2: "npm:^2.3.0" + p-is-promise: "npm:^3.0.0" + checksum: 10c0/ac6975c0029bf969931781ab1534996b35068f5d51ccd55a00b601e2fc638cf040a42c9fb8e3c8f320509af9a56c9b11da8f1159f76db3ed8096779cce618c95 + languageName: node + linkType: hard + +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc + languageName: node + linkType: hard + +"ip-regex@npm:^5.0.0": + version: 5.0.0 + resolution: "ip-regex@npm:5.0.0" + checksum: 10c0/23f07cf393436627b3a91f7121eee5bc831522d07c95ddd13f5a6f7757698b08551480f12e5dbb3bf248724da135d54405c9687733dba7314f74efae593bdf06 + languageName: node + linkType: hard + +"is-array-buffer@npm:^3.0.4": + version: 3.0.4 + resolution: "is-array-buffer@npm:3.0.4" + dependencies: + call-bind: "npm:^1.0.2" + get-intrinsic: "npm:^1.2.1" + checksum: 10c0/42a49d006cc6130bc5424eae113e948c146f31f9d24460fc0958f855d9d810e6fd2e4519bf19aab75179af9c298ea6092459d8cafdec523cd19e529b26eab860 + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 + languageName: node + linkType: hard + +"is-bigint@npm:^1.0.1": + version: 1.0.4 + resolution: "is-bigint@npm:1.0.4" + dependencies: + has-bigints: "npm:^1.0.1" + checksum: 10c0/eb9c88e418a0d195ca545aff2b715c9903d9b0a5033bc5922fec600eb0c3d7b1ee7f882dbf2e0d5a6e694e42391be3683e4368737bd3c4a77f8ac293e7773696 + languageName: node + linkType: hard + +"is-binary-path@npm:~2.1.0": + version: 2.1.0 + resolution: "is-binary-path@npm:2.1.0" + dependencies: + binary-extensions: "npm:^2.0.0" + checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 + languageName: node + linkType: hard + +"is-boolean-object@npm:^1.1.0": + version: 1.1.2 + resolution: "is-boolean-object@npm:1.1.2" + dependencies: + call-bind: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/6090587f8a8a8534c0f816da868bc94f32810f08807aa72fa7e79f7e11c466d281486ffe7a788178809c2aa71fe3e700b167fe80dd96dad68026bfff8ebf39f7 + languageName: node + linkType: hard + +"is-builtin-module@npm:^3.2.1": + version: 3.2.1 + resolution: "is-builtin-module@npm:3.2.1" + dependencies: + builtin-modules: "npm:^3.3.0" + checksum: 10c0/5a66937a03f3b18803381518f0ef679752ac18cdb7dd53b5e23ee8df8d440558737bd8dcc04d2aae555909d2ecb4a81b5c0d334d119402584b61e6a003e31af1 + languageName: node + linkType: hard + +"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 10c0/ceebaeb9d92e8adee604076971dd6000d38d6afc40bb843ea8e45c5579b57671c3f3b50d7f04869618242c6cee08d1b67806a8cb8edaaaf7c0748b3720d6066f + languageName: node + linkType: hard + +"is-cidr@npm:^5.0.5": + version: 5.0.5 + resolution: "is-cidr@npm:5.0.5" + dependencies: + cidr-regex: "npm:^4.0.4" + checksum: 10c0/0eda3e735d965e5b2d1616b322002b48d307db3ff6a80feefe71984e0ddab201359946c913805f67543bdba52dc71c1cf8ec45aa22ff8f7fd1b4a3496a3ef958 + languageName: node + linkType: hard + +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.8.1": + version: 2.13.1 + resolution: "is-core-module@npm:2.13.1" dependencies: hasown: "npm:^2.0.0" checksum: 10c0/2cba9903aaa52718f11c4896dabc189bab980870aae86a62dc0d5cedb546896770ee946fb14c84b7adf0735f5eaea4277243f1b95f5cefa90054f92fbcac2518 languageName: node linkType: hard +"is-data-view@npm:^1.0.1": + version: 1.0.1 + resolution: "is-data-view@npm:1.0.1" + dependencies: + is-typed-array: "npm:^1.1.13" + checksum: 10c0/a3e6ec84efe303da859107aed9b970e018e2bee7ffcb48e2f8096921a493608134240e672a2072577e5f23a729846241d9634806e8a0e51d9129c56d5f65442d + languageName: node + linkType: hard + +"is-date-object@npm:^1.0.1": + version: 1.0.5 + resolution: "is-date-object@npm:1.0.5" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/eed21e5dcc619c48ccef804dfc83a739dbb2abee6ca202838ee1bd5f760fe8d8a93444f0d49012ad19bb7c006186e2884a1b92f6e1c056da7fd23d0a9ad5992e + languageName: node + linkType: hard + "is-docker@npm:^2.0.0, is-docker@npm:^2.1.1": version: 2.2.1 resolution: "is-docker@npm:2.2.1" @@ -1992,6 +3756,22 @@ __metadata: languageName: node linkType: hard +"is-negative-zero@npm:^2.0.3": + version: 2.0.3 + resolution: "is-negative-zero@npm:2.0.3" + checksum: 10c0/bcdcf6b8b9714063ffcfa9929c575ac69bfdabb8f4574ff557dfc086df2836cf07e3906f5bbc4f2a5c12f8f3ba56af640c843cdfc74da8caed86c7c7d66fd08e + languageName: node + linkType: hard + +"is-number-object@npm:^1.0.4": + version: 1.0.7 + resolution: "is-number-object@npm:1.0.7" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/aad266da1e530f1804a2b7bd2e874b4869f71c98590b3964f9d06cc9869b18f8d1f4778f838ecd2a11011bce20aeecb53cb269ba916209b79c24580416b74b1b + languageName: node + linkType: hard + "is-number@npm:^7.0.0": version: 7.0.0 resolution: "is-number@npm:7.0.0" @@ -1999,6 +3779,13 @@ __metadata: languageName: node linkType: hard +"is-obj@npm:^2.0.0": + version: 2.0.0 + resolution: "is-obj@npm:2.0.0" + checksum: 10c0/85044ed7ba8bd169e2c2af3a178cacb92a97aa75de9569d02efef7f443a824b5e153eba72b9ae3aca6f8ce81955271aa2dc7da67a8b720575d3e38104208cb4e + languageName: node + linkType: hard + "is-path-inside@npm:^3.0.3": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" @@ -2022,6 +3809,84 @@ __metadata: languageName: node linkType: hard +"is-regex@npm:^1.1.4": + version: 1.1.4 + resolution: "is-regex@npm:1.1.4" + dependencies: + call-bind: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/bb72aae604a69eafd4a82a93002058c416ace8cde95873589a97fc5dac96a6c6c78a9977d487b7b95426a8f5073969124dd228f043f9f604f041f32fcc465fc1 + languageName: node + linkType: hard + +"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.3": + version: 1.0.3 + resolution: "is-shared-array-buffer@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.7" + checksum: 10c0/adc11ab0acbc934a7b9e5e9d6c588d4ec6682f6fea8cda5180721704fa32927582ede5b123349e32517fdadd07958973d24716c80e7ab198970c47acc09e59c7 + languageName: node + linkType: hard + +"is-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "is-stream@npm:3.0.0" + checksum: 10c0/eb2f7127af02ee9aa2a0237b730e47ac2de0d4e76a4a905a50a11557f2339df5765eaea4ceb8029f1efa978586abe776908720bfcb1900c20c6ec5145f6f29d8 + languageName: node + linkType: hard + +"is-string@npm:^1.0.5, is-string@npm:^1.0.7": + version: 1.0.7 + resolution: "is-string@npm:1.0.7" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/905f805cbc6eedfa678aaa103ab7f626aac9ebbdc8737abb5243acaa61d9820f8edc5819106b8fcd1839e33db21de9f0116ae20de380c8382d16dc2a601921f6 + languageName: node + linkType: hard + +"is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": + version: 1.0.4 + resolution: "is-symbol@npm:1.0.4" + dependencies: + has-symbols: "npm:^1.0.2" + checksum: 10c0/9381dd015f7c8906154dbcbf93fad769de16b4b961edc94f88d26eb8c555935caa23af88bda0c93a18e65560f6d7cca0fd5a3f8a8e1df6f1abbb9bead4502ef7 + languageName: node + linkType: hard + +"is-text-path@npm:^2.0.0": + version: 2.0.0 + resolution: "is-text-path@npm:2.0.0" + dependencies: + text-extensions: "npm:^2.0.0" + checksum: 10c0/e3c470e1262a3a54aa0fca1c0300b2659a7aed155714be6b643f88822c03bcfa6659b491f7a05c5acd3c1a3d6d42bab47e1bdd35bcc3a25973c4f26b2928bc1a + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.13": + version: 1.1.13 + resolution: "is-typed-array@npm:1.1.13" + dependencies: + which-typed-array: "npm:^1.1.14" + checksum: 10c0/fa5cb97d4a80e52c2cc8ed3778e39f175a1a2ae4ddf3adae3187d69586a1fd57cfa0b095db31f66aa90331e9e3da79184cea9c6abdcd1abc722dc3c3edd51cca + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.0.0 + resolution: "is-unicode-supported@npm:2.0.0" + checksum: 10c0/3013dfb8265fe9f9a0d1e9433fc4e766595631a8d85d60876c457b4bedc066768dab1477c553d02e2f626d88a4e019162706e04263c94d74994ef636a33b5f94 + languageName: node + linkType: hard + +"is-weakref@npm:^1.0.2": + version: 1.0.2 + resolution: "is-weakref@npm:1.0.2" + dependencies: + call-bind: "npm:^1.0.2" + checksum: 10c0/1545c5d172cb690c392f2136c23eec07d8d78a7f57d0e41f10078aa4f5daf5d7f57b6513a67514ab4f073275ad00c9822fc8935e00229d0a2089e1c02685d4b1 + languageName: node + linkType: hard + "is-wsl@npm:^2.2.0": version: 2.2.0 resolution: "is-wsl@npm:2.2.0" @@ -2031,6 +3896,20 @@ __metadata: languageName: node linkType: hard +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd + languageName: node + linkType: hard + +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: 10c0/18b5be6669be53425f0b84098732670ed4e727e3af33bc7f948aac01782110eb9a18b3b329c5323bcdd3acdaae547ee077d3951317e7f133bff7105264b3003d + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -2045,6 +3924,19 @@ __metadata: languageName: node linkType: hard +"issue-parser@npm:^7.0.0": + version: 7.0.0 + resolution: "issue-parser@npm:7.0.0" + dependencies: + lodash.capitalize: "npm:^4.2.1" + lodash.escaperegexp: "npm:^4.1.2" + lodash.isplainobject: "npm:^4.0.6" + lodash.isstring: "npm:^4.0.1" + lodash.uniqby: "npm:^4.7.0" + checksum: 10c0/b234d6045871557f1a4adbd7e62aae568179d0fac5d619d30e8c1e0ba5fcd2d273a394d668a7a95dbcded1e5e57d2c4dc24772220bf05d8fe313f5455ab91a63 + languageName: node + linkType: hard + "jackspeak@npm:^2.3.6": version: 2.3.6 resolution: "jackspeak@npm:2.3.6" @@ -2058,6 +3950,13 @@ __metadata: languageName: node linkType: hard +"java-properties@npm:^1.0.2": + version: 1.0.2 + resolution: "java-properties@npm:1.0.2" + checksum: 10c0/be0f58c83b5a852f313de2ea57f7b8b7d46dc062b2ffe487d58838e7034d4660f4d22f2a96aae4daa622af6d734726c0d08b01396e59666ededbcfdc25a694d6 + languageName: node + linkType: hard + "js-md4@npm:^0.3.2": version: 0.3.2 resolution: "js-md4@npm:0.3.2" @@ -2065,6 +3964,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + "js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" @@ -2090,6 +3996,27 @@ __metadata: languageName: node linkType: hard +"json-parse-better-errors@npm:^1.0.1": + version: 1.0.2 + resolution: "json-parse-better-errors@npm:1.0.2" + checksum: 10c0/2f1287a7c833e397c9ddd361a78638e828fc523038bb3441fd4fc144cfd2c6cd4963ffb9e207e648cf7b692600f1e1e524e965c32df5152120910e4903a47dcb + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^2.3.0": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^3.0.0, json-parse-even-better-errors@npm:^3.0.1": + version: 3.0.1 + resolution: "json-parse-even-better-errors@npm:3.0.1" + checksum: 10c0/bc40600b14231dff1ff911d269c7ed89fbf3dbedf25cad3f47c10ff9cbb998ce03921372a17f27f3c7cfed76e679bc6c02a7b4cb2604b0ba68cd51ed16899492 + languageName: node + linkType: hard + "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -2104,6 +4031,40 @@ __metadata: languageName: node linkType: hard +"json-stringify-nice@npm:^1.1.4": + version: 1.1.4 + resolution: "json-stringify-nice@npm:1.1.4" + checksum: 10c0/13673b67ba9e7fde75a103cade0b0d2dd0d21cd3b918de8d8f6cd59d48ad8c78b0e85f6f4a5842073ddfc91ebdde5ef7c81c7f51945b96a33eaddc5d41324b87 + languageName: node + linkType: hard + +"json-stringify-safe@npm:^5.0.1": + version: 5.0.1 + resolution: "json-stringify-safe@npm:5.0.1" + checksum: 10c0/7dbf35cd0411d1d648dceb6d59ce5857ec939e52e4afc37601aa3da611f0987d5cee5b38d58329ceddf3ed48bd7215229c8d52059ab01f2444a338bf24ed0f37 + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/4f95b5e8a5622b1e9e8f33c96b7ef3158122f595998114d1e7f03985649ea99cb3cd99ce1ed1831ae94c8c8543ab45ebd044207612f31a56fd08462140e46865 + languageName: node + linkType: hard + +"jsonparse@npm:^1.2.0, jsonparse@npm:^1.3.1": + version: 1.3.1 + resolution: "jsonparse@npm:1.3.1" + checksum: 10c0/89bc68080cd0a0e276d4b5ab1b79cacd68f562467008d176dc23e16e97d4efec9e21741d92ba5087a8433526a45a7e6a9d5ef25408696c402ca1cfbc01a90bf0 + languageName: node + linkType: hard + "jsonwebtoken@npm:^9.0.0": version: 9.0.2 resolution: "jsonwebtoken@npm:9.0.2" @@ -2122,6 +4083,20 @@ __metadata: languageName: node linkType: hard +"just-diff-apply@npm:^5.2.0": + version: 5.5.0 + resolution: "just-diff-apply@npm:5.5.0" + checksum: 10c0/d7b85371f2a5a17a108467fda35dddd95264ab438ccec7837b67af5913c57ded7246039d1df2b5bc1ade034ccf815b56d69786c5f1e07383168a066007c796c0 + languageName: node + linkType: hard + +"just-diff@npm:^6.0.0": + version: 6.0.2 + resolution: "just-diff@npm:6.0.2" + checksum: 10c0/1931ca1f0cea4cc480172165c189a84889033ad7a60bee302268ba8ca9f222b43773fd5f272a23ee618d43d85d3048411f06b635571a198159e9a85bb2495f5c + languageName: node + linkType: hard + "jwa@npm:^1.4.1": version: 1.4.1 resolution: "jwa@npm:1.4.1" @@ -2183,6 +4158,170 @@ __metadata: languageName: node linkType: hard +"libnpmaccess@npm:^8.0.1": + version: 8.0.3 + resolution: "libnpmaccess@npm:8.0.3" + dependencies: + npm-package-arg: "npm:^11.0.1" + npm-registry-fetch: "npm:^16.2.0" + checksum: 10c0/1aac3a0873c5b2c38c0193fb6dcae8442606b197feed90b4788ee69b9cf8fa59d8b41337824f0e43fcf780f3974d2555804437d76ebd544740f1763ccea39f74 + languageName: node + linkType: hard + +"libnpmdiff@npm:^6.0.3": + version: 6.0.9 + resolution: "libnpmdiff@npm:6.0.9" + dependencies: + "@npmcli/arborist": "npm:^7.2.1" + "@npmcli/disparity-colors": "npm:^3.0.0" + "@npmcli/installed-package-contents": "npm:^2.0.2" + binary-extensions: "npm:^2.3.0" + diff: "npm:^5.1.0" + minimatch: "npm:^9.0.4" + npm-package-arg: "npm:^11.0.1" + pacote: "npm:^17.0.4" + tar: "npm:^6.2.1" + checksum: 10c0/49041d64b68afe5c9a39d545ce907b27fdee80fe09447f32864b4f544df7dc1dd4d5c92b2f363ac20ce126aeaa6faf167c868d5aa5fd08a51a2b89ff943d7e5c + languageName: node + linkType: hard + +"libnpmexec@npm:^7.0.4": + version: 7.0.10 + resolution: "libnpmexec@npm:7.0.10" + dependencies: + "@npmcli/arborist": "npm:^7.2.1" + "@npmcli/run-script": "npm:^7.0.2" + ci-info: "npm:^4.0.0" + npm-package-arg: "npm:^11.0.1" + npmlog: "npm:^7.0.1" + pacote: "npm:^17.0.4" + proc-log: "npm:^3.0.0" + read: "npm:^3.0.1" + read-package-json-fast: "npm:^3.0.2" + semver: "npm:^7.3.7" + walk-up-path: "npm:^3.0.1" + checksum: 10c0/e2d117d386c93be71a10ef369c106b5e00fd7449d42a314f018c0767504e45a15a9ceb7be3bf50c8b98e7d1d3083a665695d6da2c7fc921b06850a5d5f5cb121 + languageName: node + linkType: hard + +"libnpmfund@npm:^5.0.1": + version: 5.0.7 + resolution: "libnpmfund@npm:5.0.7" + dependencies: + "@npmcli/arborist": "npm:^7.2.1" + checksum: 10c0/4d55064c11b425b0544588f79b98553e1ec463ccdc4b8024a52d0f554d656e22d6728b047ea58fdaf0d8c9a75480a11858a58121b1f6cb7ae04ea9d2e54df808 + languageName: node + linkType: hard + +"libnpmhook@npm:^10.0.0": + version: 10.0.2 + resolution: "libnpmhook@npm:10.0.2" + dependencies: + aproba: "npm:^2.0.0" + npm-registry-fetch: "npm:^16.2.0" + checksum: 10c0/1fa30454303a49d9e2d89ee9c26741c319a367aaeadf837fcf137f3b6ee8ac75faa845b8c52ac7fe912328a4cf3e8be3b11b01b32408052faab291ea112ec054 + languageName: node + linkType: hard + +"libnpmorg@npm:^6.0.1": + version: 6.0.3 + resolution: "libnpmorg@npm:6.0.3" + dependencies: + aproba: "npm:^2.0.0" + npm-registry-fetch: "npm:^16.2.0" + checksum: 10c0/0f7158025df140a844858b6d1af6533ee0e5ad18008adffa148cdfca3fee0dda19c78c2089d623042e964a9b4d972fb178b35b95e1b5f98f58f5ef69d7bc2a9d + languageName: node + linkType: hard + +"libnpmpack@npm:^6.0.3": + version: 6.0.9 + resolution: "libnpmpack@npm:6.0.9" + dependencies: + "@npmcli/arborist": "npm:^7.2.1" + "@npmcli/run-script": "npm:^7.0.2" + npm-package-arg: "npm:^11.0.1" + pacote: "npm:^17.0.4" + checksum: 10c0/b2209c9c97361865f622f2d0dea5272dbdf4d10d1b4157af7b74fae507d4669237027a63968db0bc6cc0016abc7aca0d61de0bb1d32ab2f54ccb09a1fd74d803 + languageName: node + linkType: hard + +"libnpmpublish@npm:^9.0.2": + version: 9.0.5 + resolution: "libnpmpublish@npm:9.0.5" + dependencies: + ci-info: "npm:^4.0.0" + normalize-package-data: "npm:^6.0.0" + npm-package-arg: "npm:^11.0.1" + npm-registry-fetch: "npm:^16.2.0" + proc-log: "npm:^3.0.0" + semver: "npm:^7.3.7" + sigstore: "npm:^2.2.0" + ssri: "npm:^10.0.5" + checksum: 10c0/7d82286bce6636a9a80660774c96879265b121bfa841cbfb0afcca4ff84b4d44466520083bc6de6fd5c808534cedafcdf34809b5847dec8e3896131fc305cc5c + languageName: node + linkType: hard + +"libnpmsearch@npm:^7.0.0": + version: 7.0.2 + resolution: "libnpmsearch@npm:7.0.2" + dependencies: + npm-registry-fetch: "npm:^16.2.0" + checksum: 10c0/5e74dcc6344237a8f2eaf6b52ed4967a907267bf70f908459ecf7fdde29c1f7e43b9197463a84e896f342422adfbad1fab31034d1fe350bde2752998efdddc26 + languageName: node + linkType: hard + +"libnpmteam@npm:^6.0.0": + version: 6.0.2 + resolution: "libnpmteam@npm:6.0.2" + dependencies: + aproba: "npm:^2.0.0" + npm-registry-fetch: "npm:^16.2.0" + checksum: 10c0/ed51ba9224b4e75091159d2acdc1bb6a916e47889d81a19932580cdb90eea233c47c8c14549e4fac67ee7bbb2ca28e23a5dede3c1aea0267b10e9cee98a75cd1 + languageName: node + linkType: hard + +"libnpmversion@npm:^5.0.1": + version: 5.0.2 + resolution: "libnpmversion@npm:5.0.2" + dependencies: + "@npmcli/git": "npm:^5.0.3" + "@npmcli/run-script": "npm:^7.0.2" + json-parse-even-better-errors: "npm:^3.0.0" + proc-log: "npm:^3.0.0" + semver: "npm:^7.3.7" + checksum: 10c0/0abfa0589530233593953b43bf0f36c96b2448838c77abad054b3c4cea20b5128b1ee30e1e383645fbb9cf31587136ddee33fc854f8b5b01766a5fee2f9e2b6b + languageName: node + linkType: hard + +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d + languageName: node + linkType: hard + +"load-json-file@npm:^4.0.0": + version: 4.0.0 + resolution: "load-json-file@npm:4.0.0" + dependencies: + graceful-fs: "npm:^4.1.2" + parse-json: "npm:^4.0.0" + pify: "npm:^3.0.0" + strip-bom: "npm:^3.0.0" + checksum: 10c0/6b48f6a0256bdfcc8970be2c57f68f10acb2ee7e63709b386b2febb6ad3c86198f840889cdbe71d28f741cbaa2f23a7771206b138cd1bdd159564511ca37c1d5 + languageName: node + linkType: hard + +"locate-path@npm:^2.0.0": + version: 2.0.0 + resolution: "locate-path@npm:2.0.0" + dependencies: + p-locate: "npm:^2.0.0" + path-exists: "npm:^3.0.0" + checksum: 10c0/24efa0e589be6aa3c469b502f795126b26ab97afa378846cb508174211515633b770aa0ba610cab113caedab8d2a4902b061a08aaed5297c12ab6f5be4df0133 + languageName: node + linkType: hard + "locate-path@npm:^6.0.0": version: 6.0.0 resolution: "locate-path@npm:6.0.0" @@ -2192,6 +4331,27 @@ __metadata: languageName: node linkType: hard +"lodash-es@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash-es@npm:4.17.21" + checksum: 10c0/fb407355f7e6cd523a9383e76e6b455321f0f153a6c9625e21a8827d10c54c2a2341bd2ae8d034358b60e07325e1330c14c224ff582d04612a46a4f0479ff2f2 + languageName: node + linkType: hard + +"lodash.capitalize@npm:^4.2.1": + version: 4.2.1 + resolution: "lodash.capitalize@npm:4.2.1" + checksum: 10c0/b289326497c2e24d6b8afa2af2ca4e068ef6ef007ade36bfb6f70af77ce10ea3f090eeee947d5fdcf2db4bcfa4703c8c10a5857a2b39e308bddfd1d11ad35970 + languageName: node + linkType: hard + +"lodash.escaperegexp@npm:^4.1.2": + version: 4.1.2 + resolution: "lodash.escaperegexp@npm:4.1.2" + checksum: 10c0/484ad4067fa9119bb0f7c19a36ab143d0173a081314993fe977bd00cf2a3c6a487ce417a10f6bac598d968364f992153315f0dbe25c9e38e3eb7581dd333e087 + languageName: node + linkType: hard + "lodash.includes@npm:^4.3.0": version: 4.3.0 resolution: "lodash.includes@npm:4.3.0" @@ -2248,6 +4408,13 @@ __metadata: languageName: node linkType: hard +"lodash.uniqby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.uniqby@npm:4.7.0" + checksum: 10c0/c505c0de20ca759599a2ba38710e8fb95ff2d2028e24d86c901ef2c74be8056518571b9b754bfb75053b2818d30dd02243e4a4621a6940c206bbb3f7626db656 + languageName: node + linkType: hard + "lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -2320,6 +4487,45 @@ __metadata: languageName: node linkType: hard +"marked-terminal@npm:^7.0.0": + version: 7.0.0 + resolution: "marked-terminal@npm:7.0.0" + dependencies: + ansi-escapes: "npm:^6.2.0" + chalk: "npm:^5.3.0" + cli-highlight: "npm:^2.1.11" + cli-table3: "npm:^0.6.3" + node-emoji: "npm:^2.1.3" + supports-hyperlinks: "npm:^3.0.0" + peerDependencies: + marked: ">=1 <13" + checksum: 10c0/1d2410dca9e0cd29958ba1dd3fefc9cdff762617d01e10f1600cf443ee7862583643bbb675b3022d076c1a75b79a2c7b777290d10b44a7543798d40d3678c504 + languageName: node + linkType: hard + +"marked@npm:^12.0.0": + version: 12.0.1 + resolution: "marked@npm:12.0.1" + bin: + marked: bin/marked.js + checksum: 10c0/e5c2a4da12dc59351e3e8acd0ca36c11a49f9a538bd0c3bda2dc3c71868f168dd678907184308e6f458eafad0c14146d01841b0644b6ccfcab920c73b814f8d8 + languageName: node + linkType: hard + +"meow@npm:^12.0.1": + version: 12.1.1 + resolution: "meow@npm:12.1.1" + checksum: 10c0/a125ca99a32e2306e2f4cbe651a0d27f6eb67918d43a075f6e80b35e9bf372ebf0fc3a9fbc201cbbc9516444b6265fb3c9f80c5b7ebd32f548aa93eb7c28e088 + languageName: node + linkType: hard + +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 + languageName: node + linkType: hard + "merge2@npm:^1.3.0, merge2@npm:^1.4.1": version: 1.4.1 resolution: "merge2@npm:1.4.1" @@ -2327,7 +4533,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4": version: 4.0.5 resolution: "micromatch@npm:4.0.5" dependencies: @@ -2353,6 +4559,22 @@ __metadata: languageName: node linkType: hard +"mime@npm:^4.0.0": + version: 4.0.1 + resolution: "mime@npm:4.0.1" + bin: + mime: bin/cli.js + checksum: 10c0/8b89fb8d93dca1ce068d072c09faa8e04e85fb1e763197cbf8adaba0aa05eb795197cca332309f724cc2239d99c9c127eccb777d97efddb11aa9e9bcb9538818 + languageName: node + linkType: hard + +"mimic-fn@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-fn@npm:4.0.0" + checksum: 10c0/de9cc32be9996fd941e512248338e43407f63f6d497abe8441fa33447d922e927de54d4cc3c1a3c6d652857acd770389d5a3823f311a744132760ce2be15ccbf + languageName: node + linkType: hard + "minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -2371,7 +4593,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.1, minimatch@npm:^9.0.4": +"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4": version: 9.0.4 resolution: "minimatch@npm:9.0.4" dependencies: @@ -2380,6 +4602,13 @@ __metadata: languageName: node linkType: hard +"minimist@npm:^1.2.0, minimist@npm:^1.2.5": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + "minipass-collect@npm:^2.0.1": version: 2.0.1 resolution: "minipass-collect@npm:2.0.1" @@ -2413,6 +4642,16 @@ __metadata: languageName: node linkType: hard +"minipass-json-stream@npm:^1.0.1": + version: 1.0.1 + resolution: "minipass-json-stream@npm:1.0.1" + dependencies: + jsonparse: "npm:^1.3.1" + minipass: "npm:^3.0.0" + checksum: 10c0/9285cbbea801e7bd6a923e7fb66d9c47c8bad880e70b29f0b8ba220c283d065f47bfa887ef87fd1b735d39393ecd53bb13d40c260354e8fcf93d47cf4bf64e9c + languageName: node + linkType: hard + "minipass-pipeline@npm:^1.2.4": version: 1.2.4 resolution: "minipass-pipeline@npm:1.2.4" @@ -2496,13 +4735,20 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.1.1": +"ms@npm:^2.1.1, ms@npm:^2.1.2": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 languageName: node linkType: hard +"mute-stream@npm:^1.0.0": + version: 1.0.0 + resolution: "mute-stream@npm:1.0.0" + checksum: 10c0/dce2a9ccda171ec979a3b4f869a102b1343dee35e920146776780de182f16eae459644d187e38d59a3d37adf85685e1c17c38cf7bfda7e39a9880f7a1d10a74c + languageName: node + linkType: hard + "mysql2@npm:3.9.4": version: 3.9.4 resolution: "mysql2@npm:3.9.4" @@ -2519,6 +4765,17 @@ __metadata: languageName: node linkType: hard +"mz@npm:^2.4.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: "npm:^1.0.0" + object-assign: "npm:^4.0.1" + thenify-all: "npm:^1.0.0" + checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 + languageName: node + linkType: hard + "named-placeholders@npm:^1.1.3": version: 1.1.3 resolution: "named-placeholders@npm:1.1.3" @@ -2549,7 +4806,33 @@ __metadata: languageName: node linkType: hard -"node-gyp@npm:latest": +"neo-async@npm:^2.6.2": + version: 2.6.2 + resolution: "neo-async@npm:2.6.2" + checksum: 10c0/c2f5a604a54a8ec5438a342e1f356dff4bc33ccccdb6dc668d94fe8e5eccfc9d2c2eea6064b0967a767ba63b33763f51ccf2cd2441b461a7322656c1f06b3f5d + languageName: node + linkType: hard + +"nerf-dart@npm:^1.0.0": + version: 1.0.0 + resolution: "nerf-dart@npm:1.0.0" + checksum: 10c0/e19e17d7bd91dfcb1acd07cbdd8df1f0613f3408227538fe91793c6dfcf58e95b5f18b88b4a13e9b31587e89a119fd76d6df4b8d8c65564dd2c409d787819583 + languageName: node + linkType: hard + +"node-emoji@npm:^2.1.3": + version: 2.1.3 + resolution: "node-emoji@npm:2.1.3" + dependencies: + "@sindresorhus/is": "npm:^4.6.0" + char-regex: "npm:^1.0.2" + emojilib: "npm:^2.4.0" + skin-tone: "npm:^2.0.0" + checksum: 10c0/e688333373563aa8308df16111eee2b5837b53a51fb63bf8b7fbea2896327c5d24c9984eb0c8ca6ac155d4d9c194dcf1840d271033c1b588c7c45a3b65339ef7 + languageName: node + linkType: hard + +"node-gyp@npm:^10.0.0, node-gyp@npm:^10.1.0, node-gyp@npm:latest": version: 10.1.0 resolution: "node-gyp@npm:10.1.0" dependencies: @@ -2589,7 +4872,7 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^7.0.0": +"nopt@npm:^7.0.0, nopt@npm:^7.2.0": version: 7.2.0 resolution: "nopt@npm:7.2.0" dependencies: @@ -2611,6 +4894,18 @@ __metadata: languageName: node linkType: hard +"normalize-package-data@npm:^6.0.0": + version: 6.0.0 + resolution: "normalize-package-data@npm:6.0.0" + dependencies: + hosted-git-info: "npm:^7.0.0" + is-core-module: "npm:^2.8.1" + semver: "npm:^7.3.5" + validate-npm-package-license: "npm:^3.0.4" + checksum: 10c0/dbd7c712c1e016a4b682640a53b44e9290c9db7b94355c71234bafee1534bef4c5dc3970c30c7ee2c4990a3c07e963e15e211b61624d58eb857d867ec71d3bb6 + languageName: node + linkType: hard + "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": version: 3.0.0 resolution: "normalize-path@npm:3.0.0" @@ -2618,6 +4913,247 @@ __metadata: languageName: node linkType: hard +"normalize-url@npm:^8.0.0": + version: 8.0.1 + resolution: "normalize-url@npm:8.0.1" + checksum: 10c0/eb439231c4b84430f187530e6fdac605c5048ef4ec556447a10c00a91fc69b52d8d8298d9d608e68d3e0f7dc2d812d3455edf425e0f215993667c3183bcab1ef + languageName: node + linkType: hard + +"npm-audit-report@npm:^5.0.0": + version: 5.0.0 + resolution: "npm-audit-report@npm:5.0.0" + checksum: 10c0/a01ab5431cfba65b4c2d9da145dd9ebde517c190a75fbeec9f3a35f3c125cf95dc32e6b53c0a522c7275b411bf91eb088cd1975c437db9220f1a338a17cbfa77 + languageName: node + linkType: hard + +"npm-bundled@npm:^3.0.0": + version: 3.0.0 + resolution: "npm-bundled@npm:3.0.0" + dependencies: + npm-normalize-package-bin: "npm:^3.0.0" + checksum: 10c0/65fcc621ba6e183be2715e3bbbf29d85e65e986965f06ee5e96a293d62dfad59ee57a9dcdd1c591eab156e03d58b3c35926b4211ce792d683458e15bb9f642c7 + languageName: node + linkType: hard + +"npm-install-checks@npm:^6.0.0, npm-install-checks@npm:^6.2.0, npm-install-checks@npm:^6.3.0": + version: 6.3.0 + resolution: "npm-install-checks@npm:6.3.0" + dependencies: + semver: "npm:^7.1.1" + checksum: 10c0/b046ef1de9b40f5d3a9831ce198e1770140a1c3f253dae22eb7b06045191ef79f18f1dcc15a945c919b3c161426861a28050abd321bf439190185794783b6452 + languageName: node + linkType: hard + +"npm-normalize-package-bin@npm:^3.0.0": + version: 3.0.1 + resolution: "npm-normalize-package-bin@npm:3.0.1" + checksum: 10c0/f1831a7f12622840e1375c785c3dab7b1d82dd521211c17ee5e9610cd1a34d8b232d3fdeebf50c170eddcb321d2c644bf73dbe35545da7d588c6b3fa488db0a5 + languageName: node + linkType: hard + +"npm-package-arg@npm:^11.0.0, npm-package-arg@npm:^11.0.1": + version: 11.0.2 + resolution: "npm-package-arg@npm:11.0.2" + dependencies: + hosted-git-info: "npm:^7.0.0" + proc-log: "npm:^4.0.0" + semver: "npm:^7.3.5" + validate-npm-package-name: "npm:^5.0.0" + checksum: 10c0/d730572e128980db45c97c184a454cb565283bf849484bf92e3b4e8ec2d08a21bd4b2cba9467466853add3e8c7d81e5de476904ac241f3ae63e6905dfc8196d4 + languageName: node + linkType: hard + +"npm-packlist@npm:^8.0.0": + version: 8.0.2 + resolution: "npm-packlist@npm:8.0.2" + dependencies: + ignore-walk: "npm:^6.0.4" + checksum: 10c0/ac3140980b1475c2e9acd3d0ca1acd0f8660c357aed357f1a4ebff2270975e0280a3b1c4938e2f16bd68217853ceb5725cf8779ec3752dfcc546582751ceedff + languageName: node + linkType: hard + +"npm-pick-manifest@npm:^9.0.0": + version: 9.0.0 + resolution: "npm-pick-manifest@npm:9.0.0" + dependencies: + npm-install-checks: "npm:^6.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + npm-package-arg: "npm:^11.0.0" + semver: "npm:^7.3.5" + checksum: 10c0/930859b70fb7b8cd8aee1c9819c2fbe95db5ae246398fbd6eaa819793675e36be97da2b4d19e1b56a913a016f7a0a33070cd3ed363ad522d5dbced9c0d94d037 + languageName: node + linkType: hard + +"npm-profile@npm:^9.0.0": + version: 9.0.1 + resolution: "npm-profile@npm:9.0.1" + dependencies: + npm-registry-fetch: "npm:^16.0.0" + proc-log: "npm:^4.0.0" + checksum: 10c0/afa17fa4d494a59a7afb3e0a9d827132c8f9f8b6973f21c5d66d48d882c757b9ddca727f6506a6633e20a541a38366e52177a582908b68729af5c957d727e4e1 + languageName: node + linkType: hard + +"npm-registry-fetch@npm:^16.0.0, npm-registry-fetch@npm:^16.2.0": + version: 16.2.1 + resolution: "npm-registry-fetch@npm:16.2.1" + dependencies: + "@npmcli/redact": "npm:^1.1.0" + make-fetch-happen: "npm:^13.0.0" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^3.0.0" + minipass-json-stream: "npm:^1.0.1" + minizlib: "npm:^2.1.2" + npm-package-arg: "npm:^11.0.0" + proc-log: "npm:^4.0.0" + checksum: 10c0/bccffc291771d55056a6ebedb7aaf431cecc663286e060dc2936e8e0deee454a4a71654f772afcaa44f0d74a2c02403d8b45486a0aa2dd6d2bd8c09c9134eeb9 + languageName: node + linkType: hard + +"npm-run-path@npm:^5.1.0": + version: 5.3.0 + resolution: "npm-run-path@npm:5.3.0" + dependencies: + path-key: "npm:^4.0.0" + checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba + languageName: node + linkType: hard + +"npm-user-validate@npm:^2.0.0": + version: 2.0.0 + resolution: "npm-user-validate@npm:2.0.0" + checksum: 10c0/18bb65b746e0e052371db68f260693ee4db82828494b09c16f9ecd686ecf06bb217c605886d4c31b5c42350abc2162244be60e5eccd6133326522f36abf58c9f + languageName: node + linkType: hard + +"npm@npm:^10.5.0": + version: 10.5.2 + resolution: "npm@npm:10.5.2" + dependencies: + "@isaacs/string-locale-compare": "npm:^1.1.0" + "@npmcli/arborist": "npm:^7.2.1" + "@npmcli/config": "npm:^8.0.2" + "@npmcli/fs": "npm:^3.1.0" + "@npmcli/map-workspaces": "npm:^3.0.6" + "@npmcli/package-json": "npm:^5.0.2" + "@npmcli/promise-spawn": "npm:^7.0.1" + "@npmcli/redact": "npm:^1.1.0" + "@npmcli/run-script": "npm:^7.0.4" + "@sigstore/tuf": "npm:^2.3.2" + abbrev: "npm:^2.0.0" + archy: "npm:~1.0.0" + cacache: "npm:^18.0.2" + chalk: "npm:^5.3.0" + ci-info: "npm:^4.0.0" + cli-columns: "npm:^4.0.0" + cli-table3: "npm:^0.6.4" + columnify: "npm:^1.6.0" + fastest-levenshtein: "npm:^1.0.16" + fs-minipass: "npm:^3.0.3" + glob: "npm:^10.3.12" + graceful-fs: "npm:^4.2.11" + hosted-git-info: "npm:^7.0.1" + ini: "npm:^4.1.2" + init-package-json: "npm:^6.0.2" + is-cidr: "npm:^5.0.5" + json-parse-even-better-errors: "npm:^3.0.1" + libnpmaccess: "npm:^8.0.1" + libnpmdiff: "npm:^6.0.3" + libnpmexec: "npm:^7.0.4" + libnpmfund: "npm:^5.0.1" + libnpmhook: "npm:^10.0.0" + libnpmorg: "npm:^6.0.1" + libnpmpack: "npm:^6.0.3" + libnpmpublish: "npm:^9.0.2" + libnpmsearch: "npm:^7.0.0" + libnpmteam: "npm:^6.0.0" + libnpmversion: "npm:^5.0.1" + make-fetch-happen: "npm:^13.0.0" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.0.4" + minipass-pipeline: "npm:^1.2.4" + ms: "npm:^2.1.2" + node-gyp: "npm:^10.1.0" + nopt: "npm:^7.2.0" + normalize-package-data: "npm:^6.0.0" + npm-audit-report: "npm:^5.0.0" + npm-install-checks: "npm:^6.3.0" + npm-package-arg: "npm:^11.0.1" + npm-pick-manifest: "npm:^9.0.0" + npm-profile: "npm:^9.0.0" + npm-registry-fetch: "npm:^16.2.0" + npm-user-validate: "npm:^2.0.0" + npmlog: "npm:^7.0.1" + p-map: "npm:^4.0.0" + pacote: "npm:^17.0.6" + parse-conflict-json: "npm:^3.0.1" + proc-log: "npm:^3.0.0" + qrcode-terminal: "npm:^0.12.0" + read: "npm:^3.0.1" + semver: "npm:^7.6.0" + spdx-expression-parse: "npm:^4.0.0" + ssri: "npm:^10.0.5" + supports-color: "npm:^9.4.0" + tar: "npm:^6.2.1" + text-table: "npm:~0.2.0" + tiny-relative-date: "npm:^1.3.0" + treeverse: "npm:^3.0.0" + validate-npm-package-name: "npm:^5.0.0" + which: "npm:^4.0.0" + write-file-atomic: "npm:^5.0.1" + bin: + npm: bin/npm-cli.js + npx: bin/npx-cli.js + checksum: 10c0/1251a99a4d53e24cec283a945e5932b4172535d219956c30f6cf11a4053053efd21573d65994090011aa54c42c18e37df239d4d42fea1988990c434f71431351 + languageName: node + linkType: hard + +"npmlog@npm:^7.0.1": + version: 7.0.1 + resolution: "npmlog@npm:7.0.1" + dependencies: + are-we-there-yet: "npm:^4.0.0" + console-control-strings: "npm:^1.1.0" + gauge: "npm:^5.0.0" + set-blocking: "npm:^2.0.0" + checksum: 10c0/d4e6a2aaa7b5b5d2e2ed8f8ac3770789ca0691a49f3576b6a8c97d560a4c3305d2c233a9173d62be737e6e4506bf9e89debd6120a3843c1d37315c34f90fef71 + languageName: node + linkType: hard + +"object-assign@npm:^4.0.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.1": + version: 1.13.1 + resolution: "object-inspect@npm:1.13.1" + checksum: 10c0/fad603f408e345c82e946abdf4bfd774260a5ed3e5997a0b057c44153ac32c7271ff19e3a5ae39c858da683ba045ccac2f65245c12763ce4e8594f818f4a648d + languageName: node + linkType: hard + +"object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: 10c0/b11f7ccdbc6d406d1f186cdadb9d54738e347b2692a14439ca5ac70c225fa6db46db809711b78589866d47b25fc3e8dee0b4c722ac751e11180f9380e3d8601d + languageName: node + linkType: hard + +"object.assign@npm:^4.1.5": + version: 4.1.5 + resolution: "object.assign@npm:4.1.5" + dependencies: + call-bind: "npm:^1.0.5" + define-properties: "npm:^1.2.1" + has-symbols: "npm:^1.0.3" + object-keys: "npm:^1.1.1" + checksum: 10c0/60108e1fa2706f22554a4648299b0955236c62b3685c52abf4988d14fffb0e7731e00aa8c6448397e3eb63d087dcc124a9f21e1980f36d0b2667f3c18bacd469 + languageName: node + linkType: hard + "obuf@npm:~1.1.2": version: 1.1.2 resolution: "obuf@npm:1.1.2" @@ -2634,6 +5170,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^6.0.0": + version: 6.0.0 + resolution: "onetime@npm:6.0.0" + dependencies: + mimic-fn: "npm:^4.0.0" + checksum: 10c0/4eef7c6abfef697dd4479345a4100c382d73c149d2d56170a54a07418c50816937ad09500e1ed1e79d235989d073a9bade8557122aee24f0576ecde0f392bb6c + languageName: node + linkType: hard + "open@npm:^8.0.0": version: 8.4.2 resolution: "open@npm:8.4.2" @@ -2645,53 +5190,217 @@ __metadata: languageName: node linkType: hard -"optionator@npm:^0.9.3": - version: 0.9.3 - resolution: "optionator@npm:0.9.3" +"optionator@npm:^0.9.3": + version: 0.9.3 + resolution: "optionator@npm:0.9.3" + dependencies: + "@aashutoshrathi/word-wrap": "npm:^1.2.3" + deep-is: "npm:^0.1.3" + fast-levenshtein: "npm:^2.0.6" + levn: "npm:^0.4.1" + prelude-ls: "npm:^1.2.1" + type-check: "npm:^0.4.0" + checksum: 10c0/66fba794d425b5be51353035cf3167ce6cfa049059cbb93229b819167687e0f48d2bc4603fcb21b091c99acb516aae1083624675b15c4765b2e4693a085e959c + languageName: node + linkType: hard + +"p-each-series@npm:^3.0.0": + version: 3.0.0 + resolution: "p-each-series@npm:3.0.0" + checksum: 10c0/695acfd295788a9d6fc68e86a0d205e7bffc17e0e577922d9ed3ae1d2c52566b985637f85af79484ce6fa4b3c1214f2bc75e9bc14974d0ea19f61b13e5ea0c4e + languageName: node + linkType: hard + +"p-filter@npm:^4.0.0": + version: 4.1.0 + resolution: "p-filter@npm:4.1.0" + dependencies: + p-map: "npm:^7.0.1" + checksum: 10c0/aaa663a74e7d97846377f1b7f7713692f95ca3320f0e6f7f2f06db073926bd8ef7b452d0eefc102c6c23f7482339fc52ea487aec2071dc01cae054665f3f004e + languageName: node + linkType: hard + +"p-is-promise@npm:^3.0.0": + version: 3.0.0 + resolution: "p-is-promise@npm:3.0.0" + checksum: 10c0/17a52c7a59a31a435a4721a7110faeccb7cc9179cf9cd00016b7a9a7156e2c2ed9d8e2efc0142acab74d5064fbb443eaeaf67517cf3668f2a7c93a7effad5bb9 + languageName: node + linkType: hard + +"p-limit@npm:^1.1.0": + version: 1.3.0 + resolution: "p-limit@npm:1.3.0" + dependencies: + p-try: "npm:^1.0.0" + checksum: 10c0/5c1b1d53d180b2c7501efb04b7c817448e10efe1ba46f4783f8951994d5027e4cd88f36ad79af50546682594c4ebd11702ac4b9364c47f8074890e2acad0edee + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + languageName: node + linkType: hard + +"p-locate@npm:^2.0.0": + version: 2.0.0 + resolution: "p-locate@npm:2.0.0" + dependencies: + p-limit: "npm:^1.1.0" + checksum: 10c0/82da4be88fb02fd29175e66021610c881938d3cc97c813c71c1a605fac05617d57fd5d3b337494a6106c0edb2a37c860241430851411f1b265108cead34aee67 + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: "npm:^3.0.2" + checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a + languageName: node + linkType: hard + +"p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: "npm:^3.0.0" + checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75 + languageName: node + linkType: hard + +"p-map@npm:^7.0.1": + version: 7.0.2 + resolution: "p-map@npm:7.0.2" + checksum: 10c0/e10548036648d1c043153f9997112fe5a7de54a319210238628f8ea22ee36587fd6ee740811f88b60bbf29d932e23ae35df7fced40df477116c84c18e797047e + languageName: node + linkType: hard + +"p-reduce@npm:^3.0.0": + version: 3.0.0 + resolution: "p-reduce@npm:3.0.0" + checksum: 10c0/794cd6c98ad246f6f41fa4b925e56c7d8759b92f67712f5f735418dc7b47cd9aadaecbbbedaea2df879fd9c5d7622ed0b22a2c090d2ec349cf0578485a660196 + languageName: node + linkType: hard + +"p-try@npm:^1.0.0": + version: 1.0.0 + resolution: "p-try@npm:1.0.0" + checksum: 10c0/757ba31de5819502b80c447826fac8be5f16d3cb4fbf9bc8bc4971dba0682e84ac33e4b24176ca7058c69e29f64f34d8d9e9b08e873b7b7bb0aa89d620fa224a + languageName: node + linkType: hard + +"pacote@npm:^17.0.0, pacote@npm:^17.0.4, pacote@npm:^17.0.6": + version: 17.0.7 + resolution: "pacote@npm:17.0.7" + dependencies: + "@npmcli/git": "npm:^5.0.0" + "@npmcli/installed-package-contents": "npm:^2.0.1" + "@npmcli/promise-spawn": "npm:^7.0.0" + "@npmcli/run-script": "npm:^7.0.0" + cacache: "npm:^18.0.0" + fs-minipass: "npm:^3.0.0" + minipass: "npm:^7.0.2" + npm-package-arg: "npm:^11.0.0" + npm-packlist: "npm:^8.0.0" + npm-pick-manifest: "npm:^9.0.0" + npm-registry-fetch: "npm:^16.0.0" + proc-log: "npm:^4.0.0" + promise-retry: "npm:^2.0.1" + read-package-json: "npm:^7.0.0" + read-package-json-fast: "npm:^3.0.0" + sigstore: "npm:^2.2.0" + ssri: "npm:^10.0.0" + tar: "npm:^6.1.11" + bin: + pacote: lib/bin.js + checksum: 10c0/05730d3233918e4d89a4b9f8b436cddbe5081a4922c26c8af7d8f7db3adc79b211edd0e1ef2fd1c5b280811fd93a4486d76188fe75f3172a09d864f099d61066 + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 + languageName: node + linkType: hard + +"parse-conflict-json@npm:^3.0.0, parse-conflict-json@npm:^3.0.1": + version: 3.0.1 + resolution: "parse-conflict-json@npm:3.0.1" + dependencies: + json-parse-even-better-errors: "npm:^3.0.0" + just-diff: "npm:^6.0.0" + just-diff-apply: "npm:^5.2.0" + checksum: 10c0/610b37181229ce3e945125c3a9548ec24d1de2d697a7ea3ef0f2660cccc6613715c2ba4bdbaf37c565133d6b61758703618a2c63d1ee29f97fd33c70a8aae323 + languageName: node + linkType: hard + +"parse-json@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-json@npm:4.0.0" + dependencies: + error-ex: "npm:^1.3.1" + json-parse-better-errors: "npm:^1.0.1" + checksum: 10c0/8d80790b772ccb1bcea4e09e2697555e519d83d04a77c2b4237389b813f82898943a93ffff7d0d2406203bdd0c30dcf95b1661e3a53f83d0e417f053957bef32 + languageName: node + linkType: hard + +"parse-json@npm:^5.2.0": + version: 5.2.0 + resolution: "parse-json@npm:5.2.0" dependencies: - "@aashutoshrathi/word-wrap": "npm:^1.2.3" - deep-is: "npm:^0.1.3" - fast-levenshtein: "npm:^2.0.6" - levn: "npm:^0.4.1" - prelude-ls: "npm:^1.2.1" - type-check: "npm:^0.4.0" - checksum: 10c0/66fba794d425b5be51353035cf3167ce6cfa049059cbb93229b819167687e0f48d2bc4603fcb21b091c99acb516aae1083624675b15c4765b2e4693a085e959c + "@babel/code-frame": "npm:^7.0.0" + error-ex: "npm:^1.3.1" + json-parse-even-better-errors: "npm:^2.3.0" + lines-and-columns: "npm:^1.1.6" + checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 languageName: node linkType: hard -"p-limit@npm:^3.0.2": - version: 3.1.0 - resolution: "p-limit@npm:3.1.0" +"parse-json@npm:^8.0.0": + version: 8.1.0 + resolution: "parse-json@npm:8.1.0" dependencies: - yocto-queue: "npm:^0.1.0" - checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + "@babel/code-frame": "npm:^7.22.13" + index-to-position: "npm:^0.1.2" + type-fest: "npm:^4.7.1" + checksum: 10c0/39a49acafc1c41a763df2599a826eb77873a44b098a5f2ba548843229b334a16ff9d613d0381328e58031b0afaabc18ed2a01337a6522911ac7a81828df58bcb languageName: node linkType: hard -"p-locate@npm:^5.0.0": - version: 5.0.0 - resolution: "p-locate@npm:5.0.0" +"parse5-htmlparser2-tree-adapter@npm:^6.0.0": + version: 6.0.1 + resolution: "parse5-htmlparser2-tree-adapter@npm:6.0.1" dependencies: - p-limit: "npm:^3.0.2" - checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a + parse5: "npm:^6.0.1" + checksum: 10c0/dfa5960e2aaf125707e19a4b1bc333de49232eba5a6ffffb95d313a7d6087c3b7a274b58bee8d3bd41bdf150638815d1d601a42bbf2a0345208c3c35b1279556 languageName: node linkType: hard -"p-map@npm:^4.0.0": - version: 4.0.0 - resolution: "p-map@npm:4.0.0" - dependencies: - aggregate-error: "npm:^3.0.0" - checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75 +"parse5@npm:^5.1.1": + version: 5.1.1 + resolution: "parse5@npm:5.1.1" + checksum: 10c0/b0f87a77a7fea5f242e3d76917c983bbea47703b9371801d51536b78942db6441cbda174bf84eb30e47315ddc6f8a0b57d68e562c790154430270acd76c1fa03 languageName: node linkType: hard -"parent-module@npm:^1.0.0": - version: 1.0.1 - resolution: "parent-module@npm:1.0.1" - dependencies: - callsites: "npm:^3.0.0" - checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 +"parse5@npm:^6.0.1": + version: 6.0.1 + resolution: "parse5@npm:6.0.1" + checksum: 10c0/595821edc094ecbcfb9ddcb46a3e1fe3a718540f8320eff08b8cf6742a5114cce2d46d45f95c26191c11b184dcaf4e2960abcd9c5ed9eb9393ac9a37efcfdecb + languageName: node + linkType: hard + +"path-exists@npm:^3.0.0": + version: 3.0.0 + resolution: "path-exists@npm:3.0.0" + checksum: 10c0/17d6a5664bc0a11d48e2b2127d28a0e58822c6740bde30403f08013da599182289c56518bec89407e3f31d3c2b6b296a4220bc3f867f0911fee6952208b04167 languageName: node linkType: hard @@ -2709,6 +5418,13 @@ __metadata: languageName: node linkType: hard +"path-key@npm:^4.0.0": + version: 4.0.0 + resolution: "path-key@npm:4.0.0" + checksum: 10c0/794efeef32863a65ac312f3c0b0a99f921f3e827ff63afa5cb09a377e202c262b671f7b3832a4e64731003fa94af0263713962d317b9887bd1e0c48a342efba3 + languageName: node + linkType: hard + "path-parse@npm:^1.0.7": version: 1.0.7 resolution: "path-parse@npm:1.0.7" @@ -2733,6 +5449,13 @@ __metadata: languageName: node linkType: hard +"path-type@npm:^5.0.0": + version: 5.0.0 + resolution: "path-type@npm:5.0.0" + checksum: 10c0/e8f4b15111bf483900c75609e5e74e3fcb79f2ddb73e41470028fcd3e4b5162ec65da9907be077ee5012c18801ff7fffb35f9f37a077f3f81d85a0b7d6578efd + languageName: node + linkType: hard + "pg-cloudflare@npm:^1.1.1": version: 1.1.1 resolution: "pg-cloudflare@npm:1.1.1" @@ -2836,6 +5559,13 @@ __metadata: languageName: node linkType: hard +"picocolors@npm:^1.0.0": + version: 1.0.0 + resolution: "picocolors@npm:1.0.0" + checksum: 10c0/20a5b249e331c14479d94ec6817a182fd7a5680debae82705747b2db7ec50009a5f6648d0621c561b0572703f84dbef0858abcbd5856d3c5511426afcb1961f7 + languageName: node + linkType: hard + "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -2843,6 +5573,40 @@ __metadata: languageName: node linkType: hard +"pify@npm:^3.0.0": + version: 3.0.0 + resolution: "pify@npm:3.0.0" + checksum: 10c0/fead19ed9d801f1b1fcd0638a1ac53eabbb0945bf615f2f8806a8b646565a04a1b0e7ef115c951d225f042cca388fdc1cd3add46d10d1ed6951c20bd2998af10 + languageName: node + linkType: hard + +"pkg-conf@npm:^2.1.0": + version: 2.1.0 + resolution: "pkg-conf@npm:2.1.0" + dependencies: + find-up: "npm:^2.0.0" + load-json-file: "npm:^4.0.0" + checksum: 10c0/e1474a4f7714ee78204b4a7f2316dec9e59887762bdc126ebd0eb701bbde7c6a6da65c4dc9c2a7c1eaeee49914009bf4a4368f5d9894c596ddf812ff982fdb05 + languageName: node + linkType: hard + +"possible-typed-array-names@npm:^1.0.0": + version: 1.0.0 + resolution: "possible-typed-array-names@npm:1.0.0" + checksum: 10c0/d9aa22d31f4f7680e20269db76791b41c3a32c01a373e25f8a4813b4d45f7456bfc2b6d68f752dc4aab0e0bb0721cb3d76fb678c9101cb7a16316664bc2c73fd + languageName: node + linkType: hard + +"postcss-selector-parser@npm:^6.0.10": + version: 6.0.16 + resolution: "postcss-selector-parser@npm:6.0.16" + dependencies: + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10c0/0e11657cb3181aaf9ff67c2e59427c4df496b4a1b6a17063fae579813f80af79d444bf38f82eeb8b15b4679653fd3089e66ef0283f9aab01874d885e6cf1d2cf + languageName: node + linkType: hard + "postgres-array@npm:~2.0.0": version: 2.0.0 resolution: "postgres-array@npm:2.0.0" @@ -2924,6 +5688,20 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^4.0.0": + version: 4.0.0 + resolution: "proc-log@npm:4.0.0" + checksum: 10c0/667dd90246d990167ff48026f9e8ab2086edbaebd4d3fb1bc21fbb2edf60a0a00a9e5bd2e2fc58c51522ce39a772e4a1437bfcdcd6f1a402b943ef39571bf710 + languageName: node + linkType: hard + +"process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 10c0/bec089239487833d46b59d80327a1605e1c5287eaad770a291add7f45fda1bb5e28b38e0e061add0a1d0ee0984788ce74fa394d345eed1c420cacf392c554367 + languageName: node + linkType: hard + "process@npm:^0.11.10": version: 0.11.10 resolution: "process@npm:0.11.10" @@ -2931,6 +5709,27 @@ __metadata: languageName: node linkType: hard +"promise-all-reject-late@npm:^1.0.0": + version: 1.0.1 + resolution: "promise-all-reject-late@npm:1.0.1" + checksum: 10c0/f1af0c7b0067e84d64751148ee5bb6c3e84f4a4d1316d6fe56261e1d2637cf71b49894bcbd2c6daf7d45afb1bc99efc3749be277c3e0518b70d0c5a29d037011 + languageName: node + linkType: hard + +"promise-call-limit@npm:^3.0.1": + version: 3.0.1 + resolution: "promise-call-limit@npm:3.0.1" + checksum: 10c0/2bf66a7238b9986c9b1ae0b3575c1446485b85b4befd9ee359d8386d26050d053cb2aaa57e0fc5d91e230a77e29ad546640b3afe3eb86bcfc204aa0d330f49b4 + languageName: node + linkType: hard + +"promise-inflight@npm:^1.0.1": + version: 1.0.1 + resolution: "promise-inflight@npm:1.0.1" + checksum: 10c0/d179d148d98fbff3d815752fa9a08a87d3190551d1420f17c4467f628214db12235ae068d98cd001f024453676d8985af8f28f002345646c4ece4600a79620bc + languageName: node + linkType: hard + "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -2941,6 +5740,22 @@ __metadata: languageName: node linkType: hard +"promzard@npm:^1.0.0": + version: 1.0.1 + resolution: "promzard@npm:1.0.1" + dependencies: + read: "npm:^3.0.1" + checksum: 10c0/8445442ff1ff71a2ac2d91ca6a0908631cf6573745298afe52283af23ec00c2dc6276ac4e75cd9bb521c126d33d268c5e5682c93eda492a5dcca8a76e0f671b3 + languageName: node + linkType: hard + +"proto-list@npm:~1.2.1": + version: 1.2.4 + resolution: "proto-list@npm:1.2.4" + checksum: 10c0/b9179f99394ec8a68b8afc817690185f3b03933f7b46ce2e22c1930dc84b60d09f5ad222beab4e59e58c6c039c7f7fcf620397235ef441a356f31f9744010e12 + languageName: node + linkType: hard + "proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -2962,6 +5777,15 @@ __metadata: languageName: node linkType: hard +"qrcode-terminal@npm:^0.12.0": + version: 0.12.0 + resolution: "qrcode-terminal@npm:0.12.0" + bin: + qrcode-terminal: ./bin/qrcode-terminal.js + checksum: 10c0/1d8996a743d6c95e22056bd45fe958c306213adc97d7ef8cf1e03bc1aeeb6f27180a747ec3d761141921351eb1e3ca688f7b673ab54cdae9fa358dffaa49563c + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -2978,6 +5802,108 @@ __metadata: languageName: node linkType: hard +"rc@npm:^1.2.8": + version: 1.2.8 + resolution: "rc@npm:1.2.8" + dependencies: + deep-extend: "npm:^0.6.0" + ini: "npm:~1.3.0" + minimist: "npm:^1.2.0" + strip-json-comments: "npm:~2.0.1" + bin: + rc: ./cli.js + checksum: 10c0/24a07653150f0d9ac7168e52943cc3cb4b7a22c0e43c7dff3219977c2fdca5a2760a304a029c20811a0e79d351f57d46c9bde216193a0f73978496afc2b85b15 + languageName: node + linkType: hard + +"read-cmd-shim@npm:^4.0.0": + version: 4.0.0 + resolution: "read-cmd-shim@npm:4.0.0" + checksum: 10c0/e62db17ec9708f1e7c6a31f0a46d43df2069d85cf0df3b9d1d99e5ed36e29b1e8b2f8a427fd8bbb9bc40829788df1471794f9b01057e4b95ed062806e4df5ba9 + languageName: node + linkType: hard + +"read-package-json-fast@npm:^3.0.0, read-package-json-fast@npm:^3.0.2": + version: 3.0.2 + resolution: "read-package-json-fast@npm:3.0.2" + dependencies: + json-parse-even-better-errors: "npm:^3.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + checksum: 10c0/37787e075f0260a92be0428687d9020eecad7ece3bda37461c2219e50d1ec183ab6ba1d9ada193691435dfe119a42c8a5b5b5463f08c8ddbc3d330800b265318 + languageName: node + linkType: hard + +"read-package-json@npm:^7.0.0": + version: 7.0.0 + resolution: "read-package-json@npm:7.0.0" + dependencies: + glob: "npm:^10.2.2" + json-parse-even-better-errors: "npm:^3.0.0" + normalize-package-data: "npm:^6.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + checksum: 10c0/a2d373d0f87613fe86ec49c7e4bcdaf2a14967c258c6ccfd9585dec8b21e3d5bfe422c460648fb30e8c93fc13579da0d9c9c65adc5ec4e95ec888d99e4bccc79 + languageName: node + linkType: hard + +"read-package-up@npm:^11.0.0": + version: 11.0.0 + resolution: "read-package-up@npm:11.0.0" + dependencies: + find-up-simple: "npm:^1.0.0" + read-pkg: "npm:^9.0.0" + type-fest: "npm:^4.6.0" + checksum: 10c0/ffee09613c2b3c3ff7e7b5e838aa01f33cba5c6dfa14f87bf6f64ed27e32678e5550e712fd7e3f3105a05c43aa774d084af04ee86d3044978edb69f30ee4505a + languageName: node + linkType: hard + +"read-pkg-up@npm:^11.0.0": + version: 11.0.0 + resolution: "read-pkg-up@npm:11.0.0" + dependencies: + find-up-simple: "npm:^1.0.0" + read-pkg: "npm:^9.0.0" + type-fest: "npm:^4.6.0" + checksum: 10c0/9dfe7b1088d22804e275c235e21d64acdfb81edb73373c9ef2707aae2db8309fd35f6de90f569f0159411c25972c5a321ae6cb6a54ec01e449ce9df0a0b2397a + languageName: node + linkType: hard + +"read-pkg@npm:^9.0.0": + version: 9.0.1 + resolution: "read-pkg@npm:9.0.1" + dependencies: + "@types/normalize-package-data": "npm:^2.4.3" + normalize-package-data: "npm:^6.0.0" + parse-json: "npm:^8.0.0" + type-fest: "npm:^4.6.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10c0/f3e27549dcdb18335597f4125a3d093a40ab0a18c16a6929a1575360ed5d8679b709b4a672730d9abf6aa8537a7f02bae0b4b38626f99409255acbd8f72f9964 + languageName: node + linkType: hard + +"read@npm:^3.0.1": + version: 3.0.1 + resolution: "read@npm:3.0.1" + dependencies: + mute-stream: "npm:^1.0.0" + checksum: 10c0/af524994ff7cf94aa3ebd268feac509da44e58be7ed2a02775b5ee6a7d157b93b919e8c5ead91333f86a21fbb487dc442760bc86354c18b84d334b8cec33723a + languageName: node + linkType: hard + +"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.2, readable-stream@npm:~2.3.6": + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" + dependencies: + core-util-is: "npm:~1.0.0" + inherits: "npm:~2.0.3" + isarray: "npm:~1.0.0" + process-nextick-args: "npm:~2.0.0" + safe-buffer: "npm:~5.1.1" + string_decoder: "npm:~1.1.1" + util-deprecate: "npm:~1.0.1" + checksum: 10c0/7efdb01f3853bc35ac62ea25493567bf588773213f5f4a79f9c365e1ad13bab845ac0dae7bc946270dc40c3929483228415e92a3fc600cc7e4548992f41ee3fa + languageName: node + linkType: hard + "readable-stream@npm:^4.2.0": version: 4.5.2 resolution: "readable-stream@npm:4.5.2" @@ -3007,6 +5933,34 @@ __metadata: languageName: node linkType: hard +"regexp.prototype.flags@npm:^1.5.2": + version: 1.5.2 + resolution: "regexp.prototype.flags@npm:1.5.2" + dependencies: + call-bind: "npm:^1.0.6" + define-properties: "npm:^1.2.1" + es-errors: "npm:^1.3.0" + set-function-name: "npm:^2.0.1" + checksum: 10c0/0f3fc4f580d9c349f8b560b012725eb9c002f36daa0041b3fbf6f4238cb05932191a4d7d5db3b5e2caa336d5150ad0402ed2be81f711f9308fe7e1a9bf9bd552 + languageName: node + linkType: hard + +"registry-auth-token@npm:^5.0.0": + version: 5.0.2 + resolution: "registry-auth-token@npm:5.0.2" + dependencies: + "@pnpm/npm-conf": "npm:^2.1.0" + checksum: 10c0/20fc2225681cc54ae7304b31ebad5a708063b1949593f02dfe5fb402bc1fc28890cecec6497ea396ba86d6cca8a8480715926dfef8cf1f2f11e6f6cc0a1b4bde + languageName: node + linkType: hard + +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" @@ -3014,6 +5968,13 @@ __metadata: languageName: node linkType: hard +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + "resolve@npm:^1.22.1": version: 1.22.8 resolution: "resolve@npm:1.22.8" @@ -3139,6 +6100,18 @@ __metadata: languageName: node linkType: hard +"safe-array-concat@npm:^1.1.2": + version: 1.1.2 + resolution: "safe-array-concat@npm:1.1.2" + dependencies: + call-bind: "npm:^1.0.7" + get-intrinsic: "npm:^1.2.4" + has-symbols: "npm:^1.0.3" + isarray: "npm:^2.0.5" + checksum: 10c0/12f9fdb01c8585e199a347eacc3bae7b5164ae805cdc8c6707199dbad5b9e30001a50a43c4ee24dc9ea32dbb7279397850e9208a7e217f4d8b1cf5d90129dec9 + languageName: node + linkType: hard + "safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" @@ -3146,13 +6119,24 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:~5.1.1": +"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" checksum: 10c0/780ba6b5d99cc9a40f7b951d47152297d0e260f0df01472a1b99d4889679a4b94a13d644f7dbc4f022572f09ae9005fa2fbb93bbbd83643316f365a3e9a45b21 languageName: node linkType: hard +"safe-regex-test@npm:^1.0.3": + version: 1.0.3 + resolution: "safe-regex-test@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" + is-regex: "npm:^1.1.4" + checksum: 10c0/900bf7c98dc58f08d8523b7012b468e4eb757afa624f198902c0643d7008ba777b0bdc35810ba0b758671ce887617295fb742b3f3968991b178ceca54cb07603 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -3160,7 +6144,62 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": +"semantic-release@npm:23.0.8": + version: 23.0.8 + resolution: "semantic-release@npm:23.0.8" + dependencies: + "@semantic-release/commit-analyzer": "npm:^12.0.0" + "@semantic-release/error": "npm:^4.0.0" + "@semantic-release/github": "npm:^10.0.0" + "@semantic-release/npm": "npm:^12.0.0" + "@semantic-release/release-notes-generator": "npm:^13.0.0" + aggregate-error: "npm:^5.0.0" + cosmiconfig: "npm:^9.0.0" + debug: "npm:^4.0.0" + env-ci: "npm:^11.0.0" + execa: "npm:^8.0.0" + figures: "npm:^6.0.0" + find-versions: "npm:^6.0.0" + get-stream: "npm:^6.0.0" + git-log-parser: "npm:^1.2.0" + hook-std: "npm:^3.0.0" + hosted-git-info: "npm:^7.0.0" + import-from-esm: "npm:^1.3.1" + lodash-es: "npm:^4.17.21" + marked: "npm:^12.0.0" + marked-terminal: "npm:^7.0.0" + micromatch: "npm:^4.0.2" + p-each-series: "npm:^3.0.0" + p-reduce: "npm:^3.0.0" + read-package-up: "npm:^11.0.0" + resolve-from: "npm:^5.0.0" + semver: "npm:^7.3.2" + semver-diff: "npm:^4.0.0" + signale: "npm:^1.2.1" + yargs: "npm:^17.5.1" + bin: + semantic-release: bin/semantic-release.js + checksum: 10c0/202b84def459d60d6c5cb2542b1f509a2b67dd1fb81312b1871ae3ba4c0dfcd81fb25ea60b93b381c46682633ec4f397e4188490cbbe68ab16967044d059a0cf + languageName: node + linkType: hard + +"semver-diff@npm:^4.0.0": + version: 4.0.0 + resolution: "semver-diff@npm:4.0.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/3ed1bb22f39b4b6e98785bb066e821eabb9445d3b23e092866c50e7df8b9bd3eda617b242f81db4159586e0e39b0deb908dd160a24f783bd6f52095b22cd68ea + languageName: node + linkType: hard + +"semver-regex@npm:^4.0.5": + version: 4.0.5 + resolution: "semver-regex@npm:4.0.5" + checksum: 10c0/c270eda133691dfaab90318df995e96222e4c26c47b17f7c8bd5e5fe88b81ed67b59695fe27546e0314b0f0423c7faed1f93379ad9db47c816df2ddf770918ff + languageName: node + linkType: hard + +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": version: 7.6.0 resolution: "semver@npm:7.6.0" dependencies: @@ -3237,6 +6276,39 @@ __metadata: languageName: node linkType: hard +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 10c0/9f8c1b2d800800d0b589de1477c753492de5c1548d4ade52f57f1d1f5e04af5481554d75ce5e5c43d4004b80a3eb714398d6907027dc0534177b7539119f4454 + languageName: node + linkType: hard + +"set-function-length@npm:^1.2.1": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + +"set-function-name@npm:^2.0.1": + version: 2.0.2 + resolution: "set-function-name@npm:2.0.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + functions-have-names: "npm:^1.2.3" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/fce59f90696c450a8523e754abb305e2b8c73586452619c2bad5f7bf38c7b6b4651895c9db895679c5bef9554339cf3ef1c329b66ece3eda7255785fbe299316 + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -3253,13 +6325,50 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1": +"side-channel@npm:^1.0.4": + version: 1.0.6 + resolution: "side-channel@npm:1.0.6" + dependencies: + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + object-inspect: "npm:^1.13.1" + checksum: 10c0/d2afd163dc733cc0a39aa6f7e39bf0c436293510dbccbff446733daeaf295857dbccf94297092ec8c53e2503acac30f0b78830876f0485991d62a90e9cad305f + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 languageName: node linkType: hard +"signale@npm:^1.2.1": + version: 1.4.0 + resolution: "signale@npm:1.4.0" + dependencies: + chalk: "npm:^2.3.2" + figures: "npm:^2.0.0" + pkg-conf: "npm:^2.1.0" + checksum: 10c0/3b637421368a30805da3948f82350cb9959ddfb19073f44609495384b98baba1c62b1c5c094db57000836c8bc84c6c05c979aa7e072ceeaaf0032d7991b329c7 + languageName: node + linkType: hard + +"sigstore@npm:^2.2.0": + version: 2.3.0 + resolution: "sigstore@npm:2.3.0" + dependencies: + "@sigstore/bundle": "npm:^2.3.1" + "@sigstore/core": "npm:^1.0.0" + "@sigstore/protobuf-specs": "npm:^0.3.1" + "@sigstore/sign": "npm:^2.3.0" + "@sigstore/tuf": "npm:^2.3.1" + "@sigstore/verify": "npm:^1.2.0" + checksum: 10c0/13271fc0d0960a61994faf1a9c165429e74b09d090fb3f9dbe63b8c4ce5e275ade8abf5c72b738684888a8b87538ec2c4691d7a06c6023c0f2ff8f1aea104f2d + languageName: node + linkType: hard + "simple-git@npm:3.24.0": version: 3.24.0 resolution: "simple-git@npm:3.24.0" @@ -3280,6 +6389,15 @@ __metadata: languageName: node linkType: hard +"skin-tone@npm:^2.0.0": + version: 2.0.0 + resolution: "skin-tone@npm:2.0.0" + dependencies: + unicode-emoji-modifier-base: "npm:^1.0.0" + checksum: 10c0/82d4c2527864f9cbd6cb7f3c4abb31e2224752234d5013b881d3e34e9ab543545b05206df5a17d14b515459fcb265ce409f9cfe443903176b0360cd20e4e4ba5 + languageName: node + linkType: hard + "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -3287,6 +6405,13 @@ __metadata: languageName: node linkType: hard +"slash@npm:^5.1.0": + version: 5.1.0 + resolution: "slash@npm:5.1.0" + checksum: 10c0/eb48b815caf0bdc390d0519d41b9e0556a14380f6799c72ba35caf03544d501d18befdeeef074bc9c052acf69654bc9e0d79d7f1de0866284137a40805299eb3 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -3316,36 +6441,96 @@ __metadata: version: 2.8.1 resolution: "socks@npm:2.8.1" dependencies: - ip-address: "npm:^9.0.5" - smart-buffer: "npm:^4.2.0" - checksum: 10c0/ac77b515c260473cc7c4452f09b20939e22510ce3ae48385c516d1d5784374d5cc75be3cb18ff66cc985a7f4f2ef8fef84e984c5ec70aad58355ed59241f40a8 + ip-address: "npm:^9.0.5" + smart-buffer: "npm:^4.2.0" + checksum: 10c0/ac77b515c260473cc7c4452f09b20939e22510ce3ae48385c516d1d5784374d5cc75be3cb18ff66cc985a7f4f2ef8fef84e984c5ec70aad58355ed59241f40a8 + languageName: node + linkType: hard + +"source-map-support@npm:~0.5.20": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0, source-map@npm:^0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + +"spawn-error-forwarder@npm:~1.0.0": + version: 1.0.0 + resolution: "spawn-error-forwarder@npm:1.0.0" + checksum: 10c0/531cb73404af88b5400f9b7a976836b9f09cb48e4c0c79784ad80001ea942eb256e311f14cc7d171539cd1a86297c1c5461177c3fa736ac30627f5f8a6b06db6 + languageName: node + linkType: hard + +"spdx-correct@npm:^3.0.0": + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" + dependencies: + spdx-expression-parse: "npm:^3.0.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/49208f008618b9119208b0dadc9208a3a55053f4fd6a0ae8116861bd22696fc50f4142a35ebfdb389e05ccf2de8ad142573fefc9e26f670522d899f7b2fe7386 + languageName: node + linkType: hard + +"spdx-exceptions@npm:^2.1.0": + version: 2.5.0 + resolution: "spdx-exceptions@npm:2.5.0" + checksum: 10c0/37217b7762ee0ea0d8b7d0c29fd48b7e4dfb94096b109d6255b589c561f57da93bf4e328c0290046115961b9209a8051ad9f525e48d433082fc79f496a4ea940 + languageName: node + linkType: hard + +"spdx-expression-parse@npm:^3.0.0": + version: 3.0.1 + resolution: "spdx-expression-parse@npm:3.0.1" + dependencies: + spdx-exceptions: "npm:^2.1.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/6f8a41c87759fa184a58713b86c6a8b028250f158159f1d03ed9d1b6ee4d9eefdc74181c8ddc581a341aa971c3e7b79e30b59c23b05d2436d5de1c30bdef7171 languageName: node linkType: hard -"source-map-support@npm:~0.5.20": - version: 0.5.21 - resolution: "source-map-support@npm:0.5.21" +"spdx-expression-parse@npm:^4.0.0": + version: 4.0.0 + resolution: "spdx-expression-parse@npm:4.0.0" dependencies: - buffer-from: "npm:^1.0.0" - source-map: "npm:^0.6.0" - checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + spdx-exceptions: "npm:^2.1.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/965c487e77f4fb173f1c471f3eef4eb44b9f0321adc7f93d95e7620da31faa67d29356eb02523cd7df8a7fc1ec8238773cdbf9e45bd050329d2b26492771b736 languageName: node linkType: hard -"source-map@npm:^0.6.0": - version: 0.6.1 - resolution: "source-map@npm:0.6.1" - checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 +"spdx-license-ids@npm:^3.0.0": + version: 3.0.17 + resolution: "spdx-license-ids@npm:3.0.17" + checksum: 10c0/ddf9477b5afc70f1a7d3bf91f0b8e8a1c1b0fa65d2d9a8b5c991b1a2ba91b693d8b9749700119d5ce7f3fbf307ac421087ff43d321db472605e98a5804f80eac languageName: node linkType: hard -"split2@npm:^4.1.0": +"split2@npm:^4.0.0, split2@npm:^4.1.0": version: 4.2.0 resolution: "split2@npm:4.2.0" checksum: 10c0/b292beb8ce9215f8c642bb68be6249c5a4c7f332fc8ecadae7be5cbdf1ea95addc95f0459ef2e7ad9d45fd1064698a097e4eb211c83e772b49bc0ee423e91534 languageName: node linkType: hard +"split2@npm:~1.0.0": + version: 1.0.0 + resolution: "split2@npm:1.0.0" + dependencies: + through2: "npm:~2.0.0" + checksum: 10c0/5923936c492ebbdfed66705a25a1d53eb98d2cff740421f4b558842fdf731f108872c24fe13fa091feef8b564543bdf25c967c03fce6ea09b7119b9d3ed07eda + languageName: node + linkType: hard + "sprintf-js@npm:^1.1.3": version: 1.1.3 resolution: "sprintf-js@npm:1.1.3" @@ -3360,7 +6545,7 @@ __metadata: languageName: node linkType: hard -"ssri@npm:^10.0.0": +"ssri@npm:^10.0.0, ssri@npm:^10.0.5": version: 10.0.5 resolution: "ssri@npm:10.0.5" dependencies: @@ -3376,7 +6561,17 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": +"stream-combiner2@npm:~1.1.1": + version: 1.1.1 + resolution: "stream-combiner2@npm:1.1.1" + dependencies: + duplexer2: "npm:~0.1.0" + readable-stream: "npm:^2.0.2" + checksum: 10c0/96a14ae94493aad307176d0c0a795446cedf6c49d11d08e5d0a56bcf9f22352b0dd148b0497c8456f08b00da0867288e9750bf0286b71f6b621c0f2ba6768758 + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -3398,6 +6593,40 @@ __metadata: languageName: node linkType: hard +"string.prototype.trim@npm:^1.2.9": + version: 1.2.9 + resolution: "string.prototype.trim@npm:1.2.9" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.0" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/dcef1a0fb61d255778155006b372dff8cc6c4394bc39869117e4241f41a2c52899c0d263ffc7738a1f9e61488c490b05c0427faa15151efad721e1a9fb2663c2 + languageName: node + linkType: hard + +"string.prototype.trimend@npm:^1.0.8": + version: 1.0.8 + resolution: "string.prototype.trimend@npm:1.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/0a0b54c17c070551b38e756ae271865ac6cc5f60dabf2e7e343cceae7d9b02e1a1120a824e090e79da1b041a74464e8477e2da43e2775c85392be30a6f60963c + languageName: node + linkType: hard + +"string.prototype.trimstart@npm:^1.0.8": + version: 1.0.8 + resolution: "string.prototype.trimstart@npm:1.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/d53af1899959e53c83b64a5fd120be93e067da740e7e75acb433849aa640782fb6c7d4cd5b84c954c84413745a3764df135a8afeb22908b86a835290788d8366 + languageName: node + linkType: hard + "string_decoder@npm:^1.3.0": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" @@ -3407,6 +6636,15 @@ __metadata: languageName: node linkType: hard +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: "npm:~5.1.0" + checksum: 10c0/b4f89f3a92fd101b5653ca3c99550e07bdf9e13b35037e9e2a1c7b47cec4e55e06ff3fc468e314a0b5e80bfbaf65c1ca5a84978764884ae9413bec1fc6ca924e + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -3425,6 +6663,20 @@ __metadata: languageName: node linkType: hard +"strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-bom@npm:3.0.0" + checksum: 10c0/51201f50e021ef16672593d7434ca239441b7b760e905d9f33df6e4f3954ff54ec0e0a06f100d028af0982d6f25c35cd5cda2ce34eaebccd0250b8befb90d8f1 + languageName: node + linkType: hard + +"strip-final-newline@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-final-newline@npm:3.0.0" + checksum: 10c0/a771a17901427bac6293fd416db7577e2bc1c34a19d38351e9d5478c3c415f523f391003b42ed475f27e33a78233035df183525395f731d3bfb8cdcbd4da08ce + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -3432,7 +6684,24 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^5.5.0": +"strip-json-comments@npm:~2.0.1": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43 + languageName: node + linkType: hard + +"super-regex@npm:^1.0.0": + version: 1.0.0 + resolution: "super-regex@npm:1.0.0" + dependencies: + function-timeout: "npm:^1.0.1" + time-span: "npm:^5.1.0" + checksum: 10c0/9727b57702308af74be90ed92d4612eed6c8b03fdf25efe1a3455e40d7145246516638bcabf3538e9e9c706d8ecb233e4888e0223283543fb2836d4d7acb6200 + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0, supports-color@npm:^5.5.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" dependencies: @@ -3441,7 +6710,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^7.1.0": +"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" dependencies: @@ -3450,6 +6719,23 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:^9.4.0": + version: 9.4.0 + resolution: "supports-color@npm:9.4.0" + checksum: 10c0/6c24e6b2b64c6a60e5248490cfa50de5924da32cf09ae357ad8ebbf305cc5d2717ba705a9d4cb397d80bbf39417e8fdc8d7a0ce18bd0041bf7b5b456229164e4 + languageName: node + linkType: hard + +"supports-hyperlinks@npm:^3.0.0": + version: 3.0.0 + resolution: "supports-hyperlinks@npm:3.0.0" + dependencies: + has-flag: "npm:^4.0.0" + supports-color: "npm:^7.0.0" + checksum: 10c0/36aaa55e67645dded8e0f846fd81d7dd05ce82ea81e62347f58d86213577eb627b2b45298656ce7a70e7155e39f071d0d3f83be91e112aed801ebaa8db1ef1d0 + languageName: node + linkType: hard + "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -3457,7 +6743,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.1.11, tar@npm:^6.1.2": +"tar@npm:^6.1.11, tar@npm:^6.1.2, tar@npm:^6.2.1": version: 6.2.1 resolution: "tar@npm:6.2.1" dependencies: @@ -3488,6 +6774,25 @@ __metadata: languageName: node linkType: hard +"temp-dir@npm:^3.0.0": + version: 3.0.0 + resolution: "temp-dir@npm:3.0.0" + checksum: 10c0/a86978a400984cd5f315b77ebf3fe53bb58c61f192278cafcb1f3fb32d584a21dc8e08b93171d7874b7cc972234d3455c467306cc1bfc4524b622e5ad3bfd671 + languageName: node + linkType: hard + +"tempy@npm:^3.0.0": + version: 3.1.0 + resolution: "tempy@npm:3.1.0" + dependencies: + is-stream: "npm:^3.0.0" + temp-dir: "npm:^3.0.0" + type-fest: "npm:^2.12.2" + unique-string: "npm:^3.0.0" + checksum: 10c0/b88e70baa8d935ba8f0e0372b59ad1a961121f098da5fb4a6e05bec98ec32a49026b553532fb75c1c102ec782fd4c6a6bde0d46cbe87013fa324451ce476fb76 + languageName: node + linkType: hard + "terser@npm:^5.17.4": version: 5.30.3 resolution: "terser@npm:5.30.3" @@ -3502,13 +6807,71 @@ __metadata: languageName: node linkType: hard -"text-table@npm:^0.2.0": +"text-extensions@npm:^2.0.0": + version: 2.4.0 + resolution: "text-extensions@npm:2.4.0" + checksum: 10c0/6790e7ee72ad4d54f2e96c50a13e158bb57ce840dddc770e80960ed1550115c57bdc2cee45d5354d7b4f269636f5ca06aab4d6e0281556c841389aa837b23fcb + languageName: node + linkType: hard + +"text-table@npm:^0.2.0, text-table@npm:~0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" checksum: 10c0/02805740c12851ea5982686810702e2f14369a5f4c5c40a836821e3eefc65ffeec3131ba324692a37608294b0fd8c1e55a2dd571ffed4909822787668ddbee5c languageName: node linkType: hard +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: "npm:>= 3.1.0 < 4" + checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: "npm:^1.0.0" + checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + +"through2@npm:~2.0.0": + version: 2.0.5 + resolution: "through2@npm:2.0.5" + dependencies: + readable-stream: "npm:~2.3.6" + xtend: "npm:~4.0.1" + checksum: 10c0/cbfe5b57943fa12b4f8c043658c2a00476216d79c014895cef1ac7a1d9a8b31f6b438d0e53eecbb81054b93128324a82ecd59ec1a4f91f01f7ac113dcb14eade + languageName: node + linkType: hard + +"through@npm:>=2.2.7 <3": + version: 2.3.8 + resolution: "through@npm:2.3.8" + checksum: 10c0/4b09f3774099de0d4df26d95c5821a62faee32c7e96fb1f4ebd54a2d7c11c57fe88b0a0d49cf375de5fee5ae6bf4eb56dbbf29d07366864e2ee805349970d3cc + languageName: node + linkType: hard + +"time-span@npm:^5.1.0": + version: 5.1.0 + resolution: "time-span@npm:5.1.0" + dependencies: + convert-hrtime: "npm:^5.0.0" + checksum: 10c0/37b8284c53f4ee320377512ac19e3a034f2b025f5abd6959b8c1d0f69e0f06ab03681df209f2e452d30129e7b1f25bf573fb0f29d57e71f9b4a6b5b99f4c4b9e + languageName: node + linkType: hard + +"tiny-relative-date@npm:^1.3.0": + version: 1.3.0 + resolution: "tiny-relative-date@npm:1.3.0" + checksum: 10c0/70a0818793bd00345771a4ddfa9e339c102f891766c5ebce6a011905a1a20e30212851c9ffb11b52b79e2445be32bc21d164c4c6d317aef730766b2a61008f30 + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -3536,6 +6899,24 @@ __metadata: languageName: node linkType: hard +"traverse@npm:~0.6.6": + version: 0.6.9 + resolution: "traverse@npm:0.6.9" + dependencies: + gopd: "npm:^1.0.1" + typedarray.prototype.slice: "npm:^1.0.3" + which-typed-array: "npm:^1.1.15" + checksum: 10c0/6809ef684b04cd6985a4470f93bf794ad417f04bb1c43a6b1166fe1c94506118c7a7a87c34545fe15918f4e1fe29ced7a5813d8455932042f4ccc5981634139d + languageName: node + linkType: hard + +"treeverse@npm:^3.0.0": + version: 3.0.0 + resolution: "treeverse@npm:3.0.0" + checksum: 10c0/286479b9c05a8fb0538ee7d67a5502cea7704f258057c784c9c1118a2f598788b2c0f7a8d89e74648af88af0225b31766acecd78e6060736f09b21dd3fa255db + languageName: node + linkType: hard + "ts-api-utils@npm:^1.3.0": version: 1.3.0 resolution: "ts-api-utils@npm:1.3.0" @@ -3552,6 +6933,17 @@ __metadata: languageName: node linkType: hard +"tuf-js@npm:^2.2.0": + version: 2.2.0 + resolution: "tuf-js@npm:2.2.0" + dependencies: + "@tufjs/models": "npm:2.0.0" + debug: "npm:^4.3.4" + make-fetch-happen: "npm:^13.0.0" + checksum: 10c0/9a11793feed2aa798c1a50107a0f031034b4a670016684e0d0b7d97be3fff7f98f53783c30120bce795c16d58f1b951410bb673aae92cc2437d641cc7457e215 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -3561,6 +6953,93 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^1.0.1": + version: 1.4.0 + resolution: "type-fest@npm:1.4.0" + checksum: 10c0/a3c0f4ee28ff6ddf800d769eafafcdeab32efa38763c1a1b8daeae681920f6e345d7920bf277245235561d8117dab765cb5f829c76b713b4c9de0998a5397141 + languageName: node + linkType: hard + +"type-fest@npm:^2.12.2": + version: 2.19.0 + resolution: "type-fest@npm:2.19.0" + checksum: 10c0/a5a7ecf2e654251613218c215c7493574594951c08e52ab9881c9df6a6da0aeca7528c213c622bc374b4e0cb5c443aa3ab758da4e3c959783ce884c3194e12cb + languageName: node + linkType: hard + +"type-fest@npm:^4.6.0, type-fest@npm:^4.7.1": + version: 4.15.0 + resolution: "type-fest@npm:4.15.0" + checksum: 10c0/677a7da18dbbe07668bdf47fcb976fdcbea77e118156a7245182fdf65633d69228ce773b0022d507636945a8f22b593e09a3c08f695d2aecf96f035ff0186825 + languageName: node + linkType: hard + +"typed-array-buffer@npm:^1.0.2": + version: 1.0.2 + resolution: "typed-array-buffer@npm:1.0.2" + dependencies: + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + is-typed-array: "npm:^1.1.13" + checksum: 10c0/9e043eb38e1b4df4ddf9dde1aa64919ae8bb909571c1cc4490ba777d55d23a0c74c7d73afcdd29ec98616d91bb3ae0f705fad4421ea147e1daf9528200b562da + languageName: node + linkType: hard + +"typed-array-byte-length@npm:^1.0.1": + version: 1.0.1 + resolution: "typed-array-byte-length@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-proto: "npm:^1.0.3" + is-typed-array: "npm:^1.1.13" + checksum: 10c0/fcebeffb2436c9f355e91bd19e2368273b88c11d1acc0948a2a306792f1ab672bce4cfe524ab9f51a0505c9d7cd1c98eff4235c4f6bfef6a198f6cfc4ff3d4f3 + languageName: node + linkType: hard + +"typed-array-byte-offset@npm:^1.0.2": + version: 1.0.2 + resolution: "typed-array-byte-offset@npm:1.0.2" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-proto: "npm:^1.0.3" + is-typed-array: "npm:^1.1.13" + checksum: 10c0/d2628bc739732072e39269389a758025f75339de2ed40c4f91357023c5512d237f255b633e3106c461ced41907c1bf9a533c7e8578066b0163690ca8bc61b22f + languageName: node + linkType: hard + +"typed-array-length@npm:^1.0.6": + version: 1.0.6 + resolution: "typed-array-length@npm:1.0.6" + dependencies: + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-proto: "npm:^1.0.3" + is-typed-array: "npm:^1.1.13" + possible-typed-array-names: "npm:^1.0.0" + checksum: 10c0/74253d7dc488eb28b6b2711cf31f5a9dcefc9c41b0681fd1c178ed0a1681b4468581a3626d39cd4df7aee3d3927ab62be06aa9ca74e5baf81827f61641445b77 + languageName: node + linkType: hard + +"typedarray.prototype.slice@npm:^1.0.3": + version: 1.0.3 + resolution: "typedarray.prototype.slice@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.0" + es-errors: "npm:^1.3.0" + typed-array-buffer: "npm:^1.0.2" + typed-array-byte-offset: "npm:^1.0.2" + checksum: 10c0/6ac110a8b58a1ccb086242f09d1ce9c7ba2885924e816364a7879083b983d4096f19aab6f9aa8c0ce5ddd3d8ae3f3ba5581e10fa6838880f296a0c54c26f424b + languageName: node + linkType: hard + "typescript-eslint@npm:7.6.0": version: 7.6.0 resolution: "typescript-eslint@npm:7.6.0" @@ -3597,6 +7076,27 @@ __metadata: languageName: node linkType: hard +"uglify-js@npm:^3.1.4": + version: 3.17.4 + resolution: "uglify-js@npm:3.17.4" + bin: + uglifyjs: bin/uglifyjs + checksum: 10c0/8b7fcdca69deb284fed7d2025b73eb747ce37f9aca6af53422844f46427152d5440601b6e2a033e77856a2f0591e4167153d5a21b68674ad11f662034ec13ced + languageName: node + linkType: hard + +"unbox-primitive@npm:^1.0.2": + version: 1.0.2 + resolution: "unbox-primitive@npm:1.0.2" + dependencies: + call-bind: "npm:^1.0.2" + has-bigints: "npm:^1.0.2" + has-symbols: "npm:^1.0.3" + which-boxed-primitive: "npm:^1.0.2" + checksum: 10c0/81ca2e81134167cc8f75fa79fbcc8a94379d6c61de67090986a2273850989dd3bae8440c163121b77434b68263e34787a675cbdcb34bb2f764c6b9c843a11b66 + languageName: node + linkType: hard + "undefsafe@npm:^2.0.5": version: 2.0.5 resolution: "undefsafe@npm:2.0.5" @@ -3611,6 +7111,20 @@ __metadata: languageName: node linkType: hard +"unicode-emoji-modifier-base@npm:^1.0.0": + version: 1.0.0 + resolution: "unicode-emoji-modifier-base@npm:1.0.0" + checksum: 10c0/b37623fcf0162186debd20f116483e035a2d5b905b932a2c472459d9143d446ebcbefb2a494e2fe4fa7434355396e2a95ec3fc1f0c29a3bc8f2c827220e79c66 + languageName: node + linkType: hard + +"unicorn-magic@npm:^0.1.0": + version: 0.1.0 + resolution: "unicorn-magic@npm:0.1.0" + checksum: 10c0/e4ed0de05b0a05e735c7d8a2930881e5efcfc3ec897204d5d33e7e6247f4c31eac92e383a15d9a6bccb7319b4271ee4bea946e211bf14951fec6ff2cbbb66a92 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -3629,6 +7143,29 @@ __metadata: languageName: node linkType: hard +"unique-string@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-string@npm:3.0.0" + dependencies: + crypto-random-string: "npm:^4.0.0" + checksum: 10c0/b35ea034b161b2a573666ec16c93076b4b6106b8b16c2415808d747ab3a0566b5db0c4be231d4b11cfbc16d7fd915c9d8a45884bff0e2db11b799775b2e1e017 + languageName: node + linkType: hard + +"universal-user-agent@npm:^7.0.0, universal-user-agent@npm:^7.0.2": + version: 7.0.2 + resolution: "universal-user-agent@npm:7.0.2" + checksum: 10c0/e60517ee929813e6b3ac0ceb3c66deccafadc71341edca160279ff046319c684fd7090a60d63aa61cd34a06c2d2acebeb8c2f8d364244ae7bf8ab788e20cd8c8 + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a + languageName: node + linkType: hard + "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -3638,6 +7175,20 @@ __metadata: languageName: node linkType: hard +"url-join@npm:^5.0.0": + version: 5.0.0 + resolution: "url-join@npm:5.0.0" + checksum: 10c0/ed2b166b4b5a98adcf6828a48b6bd6df1dac4c8a464a73cf4d8e2457ed410dd8da6be0d24855b86026cd7f5c5a3657c1b7b2c7a7c5b8870af17635a41387b04c + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 + languageName: node + linkType: hard + "uuid@npm:^8.3.0, uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" @@ -3647,6 +7198,25 @@ __metadata: languageName: node linkType: hard +"validate-npm-package-license@npm:^3.0.4": + version: 3.0.4 + resolution: "validate-npm-package-license@npm:3.0.4" + dependencies: + spdx-correct: "npm:^3.0.0" + spdx-expression-parse: "npm:^3.0.0" + checksum: 10c0/7b91e455a8de9a0beaa9fe961e536b677da7f48c9a493edf4d4d4a87fd80a7a10267d438723364e432c2fcd00b5650b5378275cded362383ef570276e6312f4f + languageName: node + linkType: hard + +"validate-npm-package-name@npm:^5.0.0": + version: 5.0.0 + resolution: "validate-npm-package-name@npm:5.0.0" + dependencies: + builtins: "npm:^5.0.0" + checksum: 10c0/36a9067650f5b90c573a0d394b89ddffb08fe58a60507d7938ad7c38f25055cc5c6bf4a10fbd604abe1f4a31062cbe0dfa8e7ccad37b249da32e7b71889c079e + languageName: node + linkType: hard + "validator@npm:^13.9.0": version: 13.11.0 resolution: "validator@npm:13.11.0" @@ -3654,6 +7224,48 @@ __metadata: languageName: node linkType: hard +"walk-up-path@npm:^3.0.1": + version: 3.0.1 + resolution: "walk-up-path@npm:3.0.1" + checksum: 10c0/3184738e0cf33698dd58b0ee4418285b9c811e58698f52c1f025435a85c25cbc5a63fee599f1a79cb29ca7ef09a44ec9417b16bfd906b1a37c305f7aa20ee5bc + languageName: node + linkType: hard + +"wcwidth@npm:^1.0.0": + version: 1.0.1 + resolution: "wcwidth@npm:1.0.1" + dependencies: + defaults: "npm:^1.0.3" + checksum: 10c0/5b61ca583a95e2dd85d7078400190efd452e05751a64accb8c06ce4db65d7e0b0cde9917d705e826a2e05cc2548f61efde115ffa374c3e436d04be45c889e5b4 + languageName: node + linkType: hard + +"which-boxed-primitive@npm:^1.0.2": + version: 1.0.2 + resolution: "which-boxed-primitive@npm:1.0.2" + dependencies: + is-bigint: "npm:^1.0.1" + is-boolean-object: "npm:^1.1.0" + is-number-object: "npm:^1.0.4" + is-string: "npm:^1.0.5" + is-symbol: "npm:^1.0.3" + checksum: 10c0/0a62a03c00c91dd4fb1035b2f0733c341d805753b027eebd3a304b9cb70e8ce33e25317add2fe9b5fea6f53a175c0633ae701ff812e604410ddd049777cd435e + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15": + version: 1.1.15 + resolution: "which-typed-array@npm:1.1.15" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/4465d5348c044032032251be54d8988270e69c6b7154f8fcb2a47ff706fe36f7624b3a24246b8d9089435a8f4ec48c1c1025c5d6b499456b9e5eff4f48212983 + languageName: node + linkType: hard + "which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -3676,6 +7288,15 @@ __metadata: languageName: node linkType: hard +"wide-align@npm:^1.1.5": + version: 1.1.5 + resolution: "wide-align@npm:1.1.5" + dependencies: + string-width: "npm:^1.0.2 || 2 || 3 || 4" + checksum: 10c0/1d9c2a3e36dfb09832f38e2e699c367ef190f96b82c71f809bc0822c306f5379df87bab47bed27ea99106d86447e50eb972d3c516c2f95782807a9d082fbea95 + languageName: node + linkType: hard + "wkx@npm:^0.5.0": version: 0.5.0 resolution: "wkx@npm:0.5.0" @@ -3685,7 +7306,14 @@ __metadata: languageName: node linkType: hard -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wordwrap@npm:^1.0.0": + version: 1.0.0 + resolution: "wordwrap@npm:1.0.0" + checksum: 10c0/7ed2e44f3c33c5c3e3771134d2b0aee4314c9e49c749e37f464bf69f2bcdf0cbf9419ca638098e2717cff4875c47f56a007532f6111c3319f557a2ca91278e92 + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" dependencies: @@ -3714,13 +7342,30 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.0": +"write-file-atomic@npm:^5.0.0, write-file-atomic@npm:^5.0.1": + version: 5.0.1 + resolution: "write-file-atomic@npm:5.0.1" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^4.0.1" + checksum: 10c0/e8c850a8e3e74eeadadb8ad23c9d9d63e4e792bd10f4836ed74189ef6e996763959f1249c5650e232f3c77c11169d239cbfc8342fc70f3fe401407d23810505d + languageName: node + linkType: hard + +"xtend@npm:^4.0.0, xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" checksum: 10c0/366ae4783eec6100f8a02dff02ac907bf29f9a00b82ac0264b4d8b832ead18306797e283cf19de776538babfdcb2101375ec5646b59f08c52128ac4ab812ed0e languageName: node linkType: hard +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + "yallist@npm:^4.0.0": version: 4.0.0 resolution: "yallist@npm:4.0.0" @@ -3737,6 +7382,50 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^20.2.2": + version: 20.2.9 + resolution: "yargs-parser@npm:20.2.9" + checksum: 10c0/0685a8e58bbfb57fab6aefe03c6da904a59769bd803a722bb098bd5b0f29d274a1357762c7258fb487512811b8063fb5d2824a3415a0a4540598335b3b086c72 + languageName: node + linkType: hard + +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + +"yargs@npm:^16.0.0": + version: 16.2.0 + resolution: "yargs@npm:16.2.0" + dependencies: + cliui: "npm:^7.0.2" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.0" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^20.2.2" + checksum: 10c0/b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 + languageName: node + linkType: hard + +"yargs@npm:^17.5.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From 518927300c3a657b628ef3d8045b0544a293104a Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Mon, 15 Apr 2024 12:46:59 +0200 Subject: [PATCH 03/18] fix: use version from package.json --- rollup.config.js | 2 +- src/index.ts | 4 +++- tsconfig.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index ad2188b..20512a5 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -10,7 +10,7 @@ const commands = readdirSync('./src/commands'); export default [ ...[ 'index.js', ...commands.map(item => `commands/${item.replace('ts', 'js')}`) ].map(file => ({ - input: `./dist/${file}`, + input: `./dist/src/${file}`, output: { file: `./lib/${file}`, format: 'es' diff --git a/src/index.ts b/src/index.ts index 3d36627..ec8b29e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,10 +2,12 @@ import { program } from 'commander'; +import pkg from '../package.json'; + program .name('dcdx') .description('The Unofficial Atlassian Data Center Plugin Development CLI') - .version('0.0.1') + .version(pkg.version); // ------------------------------------------------------------------------------------------ Run diff --git a/tsconfig.json b/tsconfig.json index 3becbbc..47ffa16 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,8 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "skipLibCheck": true + "skipLibCheck": true, + "resolveJsonModule": true }, "include": [ "./src/**/*.ts", From 5f1787b521d402e62c4bf1bc9d6bf827fee65146 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Mon, 15 Apr 2024 12:49:28 +0200 Subject: [PATCH 04/18] chore: add semantic release --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f1a40c4..2241373 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,10 +21,8 @@ jobs: - node_modules - run: - name: Publish to NPM - command: | - echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc - npm publish --access public + name: Publish + command: npx semantic-release executors: @@ -38,4 +36,6 @@ workflows: - publish: filters: branches: - only: main + only: + - main + - next From 535062150c6dc191d16079c697424af16050b345 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Mon, 15 Apr 2024 12:51:15 +0200 Subject: [PATCH 05/18] chore: add semantic release --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2241373..64cdb06 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,7 +28,7 @@ executors: nodejs: docker: - - image: cimg/node:18.16 + - image: cimg/node:20.12 workflows: deploy: From cdd2ec8af4473c5fbf608a824145cb9961d88500 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Mon, 15 Apr 2024 13:16:48 +0200 Subject: [PATCH 06/18] chore: label next releases as pre-releases --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5183f10..eedb5f4 100644 --- a/package.json +++ b/package.json @@ -75,8 +75,8 @@ }, "release": { "branches": [ - "main", - "next" + { "name": "main" }, + { "name": "next", "channel": "next", "prerelease": true } ] } } From e68d814630f77e942cbf77c3cd97d83503c327e0 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Tue, 16 Apr 2024 19:40:44 +0200 Subject: [PATCH 07/18] feat: add new start command The start command allows you to run the Atlassian host product within the context of the plugin Maven project with file watchers to (re)-build your project --- package.json | 15 +- src/applications/amps.ts | 178 ++++++++++++++++--- src/applications/bamboo.ts | 10 +- src/applications/base.ts | 25 ++- src/applications/bitbucket.ts | 7 +- src/applications/confluence.ts | 10 +- src/applications/jira.ts | 10 +- src/commands/database-mssql.ts | 1 + src/commands/database-mysql.ts | 1 + src/commands/database-postgres.ts | 1 + src/commands/database.ts | 1 + src/commands/run-bamboo.ts | 1 + src/commands/run-bitbucket.ts | 1 + src/commands/run-confluence.ts | 1 + src/commands/run-jira.ts | 1 + src/commands/run.ts | 3 +- src/commands/start.ts | 102 +++++++++++ src/helpers/getApplication.ts | 15 ++ src/helpers/isRecursiveBuild.ts | 3 + src/helpers/showRecursiveBuildWarning.ts | 13 ++ src/helpers/{assets.ts => toAbsolutePath.ts} | 2 +- src/index.ts | 6 +- yarn.lock | 49 +++-- 23 files changed, 387 insertions(+), 69 deletions(-) create mode 100644 src/commands/start.ts create mode 100644 src/helpers/getApplication.ts create mode 100644 src/helpers/isRecursiveBuild.ts create mode 100644 src/helpers/showRecursiveBuildWarning.ts rename src/helpers/{assets.ts => toAbsolutePath.ts} (82%) diff --git a/package.json b/package.json index eedb5f4..e27fe0c 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@types/js-yaml": "4", "@types/node": "18.16.0", "@types/pg": "8", + "@types/yargs": "17.0.32", "@typescript-eslint/eslint-plugin": "7.6.0", "@typescript-eslint/parser": "7.6.0", "eslint": "9.0.0", @@ -61,6 +62,7 @@ "dependencies": { "@xmldom/xmldom": "0.8.10", "axios": "1.6.8", + "chokidar": "3.6.0", "commander": "12.0.0", "docker-compose": "0.24.8", "exit-hook": "4.0.0", @@ -71,12 +73,19 @@ "sequelize": "6.37.2", "simple-git": "3.24.0", "tedious": "18.1.0", - "xpath": "0.0.34" + "xpath": "0.0.34", + "yargs": "17.7.2" }, "release": { "branches": [ - { "name": "main" }, - { "name": "next", "channel": "next", "prerelease": true } + { + "name": "main" + }, + { + "name": "next", + "channel": "next", + "prerelease": true + } ] } } diff --git a/src/applications/amps.ts b/src/applications/amps.ts index e1f3906..14fc4ef 100644 --- a/src/applications/amps.ts +++ b/src/applications/amps.ts @@ -1,59 +1,179 @@ import { DOMParser, XMLSerializer } from '@xmldom/xmldom'; +import { ChildProcess, spawn } from 'child_process'; import { XMLParser } from 'fast-xml-parser'; import { existsSync, readFileSync } from 'fs' +import { cwd } from 'process'; import xpath from 'xpath'; +import { hideBin } from 'yargs/helpers'; +import yargs from 'yargs/yargs'; import { SupportedApplications } from '../types/SupportedApplications'; +const { P, activeProfiles } = yargs(hideBin(process.argv)).parseSync(); +const profile = P as string || activeProfiles as string || undefined; + export class AMPS { + private static maven: ChildProcess|null; + + // ------------------------------------------------------------------------------------------ Public Static Methods + + public static stop() { + if (AMPS.maven) { + AMPS.maven.kill(0); + } + } + + public static async build(args: Array) { + return new Promise((resolve, reject) => { + if (AMPS.maven) { + const killed = AMPS.maven.kill(0); + if (!killed) { + reject(new Error('Failed to terminate existing Maven process')); + } + } + + AMPS.maven = spawn( + 'mvn', + [ 'package', ...args ], + { cwd: cwd(), stdio: 'inherit' } + ); + + AMPS.maven.on('exit', (code) => { + AMPS.maven = null; + if (code === 0 || code === 130) { + resolve(); + } else { + reject(new Error(`Maven exited with code ${code}`)); + } + }); + }); + } + public static isAtlassianPlugin = (): boolean => { try { - const hasPomFile = existsSync('./pom.xml'); - if (hasPomFile) { - const content = readFileSync('./pom.xml', 'utf8'); - const parser = new XMLParser(); - const pom = parser.parse(content); - return pom?.project?.packaging === 'atlassian-plugin'; - } - return false; + const nodes = AMPS.getNodes('//*[local-name()=\'packaging\']'); + return nodes.some(item => item.textContent === 'atlassian-plugin'); } catch (err) { + console.log(err); return false; } } + public static getApplicationVersion(): string|undefined { + const node = !profile + ? AMPS.getNodes('//*[local-name()=\'groupId\' and text()=\'com.atlassian.maven.plugins\']', true) + : AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${profile}']/..//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']`, true); + + if (node) { + const parentNode = node.parentNode; + if (parentNode) { + const { plugin } = AMPS.toObject(parentNode); + const version = plugin?.configuration?.productVersion; + return version ? this.doPropertyReplacement(version) : undefined; + } + } + return undefined; + } + public static getApplication(): SupportedApplications|null { const applications = AMPS.getApplications(); - return applications.length === 1 ? applications[0] : null; + if (applications.length === 1) { + return applications[0]; + } else if (profile) { + const profileApplications = AMPS.getApplications(profile); + if (profileApplications.length === 1) { + return profileApplications[0]; + } + } + return null; } - public static getApplications(): Array { + public static getApplications(profile?: string): Array { const result = new Set(); - if (AMPS.isAtlassianPlugin()) { + const nodes = !profile + ? AMPS.getNodes('//*[local-name()=\'groupId\' and text()=\'com.atlassian.maven.plugins\']') + : AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${profile}']/..//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']`); + + nodes.forEach(node => { + const parentNode = node.parentNode; + if (parentNode) { + const { plugin } = AMPS.toObject(parentNode); + if (plugin?.artifactId?.includes(SupportedApplications.JIRA)) { + result.add(SupportedApplications.JIRA); + } else if (plugin?.artifactId?.includes(SupportedApplications.CONFLUENCE)) { + result.add(SupportedApplications.CONFLUENCE); + } else if (plugin?.artifactId?.includes(SupportedApplications.BAMBOO)) { + result.add(SupportedApplications.BAMBOO); + } else if (plugin?.artifactId?.includes(SupportedApplications.BITBUCKET)) { + result.add(SupportedApplications.BITBUCKET); + } + } + }); + + return Array.from(result); + } + + // ------------------------------------------------------------------------------------------ Private Static Methods + + private static doPropertyReplacement(value: string) { + let result = value; + + // If there is a profile, replace profile properties first as they take precedence + const profileProperties = profile ? AMPS.getProperties(profile) : {}; + Object.entries(profileProperties).forEach(([propertyKey, propertyValue]) => { + result = result.replaceAll(`$\{${propertyKey}}`, propertyValue); + }); + + const properties = AMPS.getProperties(); + Object.entries(properties).forEach(([propertyKey, propertyValue]) => { + result = result.replaceAll(`$\{${propertyKey}}`, propertyValue); + }); + + return result; + } + + private static getProperties(profile?: string): Record { + const result: Record = {}; + + const nodes = !profile + ? AMPS.getNodes('//*[local-name()=\'properties\']') + : AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${profile}']/..//*[local-name()='properties']`); + + nodes.forEach(node => { + const { properties } = AMPS.toObject(node); + Object.entries(properties as Record).forEach(([ key, value ]) => result[key] = value); + }); + return result; + } + + private static getNodes(expression: string): Array; + private static getNodes(expression: string, single: true): Node|null; + private static getNodes(expression: string, single?: true): Array|Node|null { + const hasPomFile = existsSync('./pom.xml'); + if (hasPomFile) { const xml = readFileSync('./pom.xml', 'utf8'); const doc = new DOMParser().parseFromString(xml, 'text/xml'); - const nodes = xpath.select('//*[local-name()=\'groupId\' and text()=\'com.atlassian.maven.plugins\']', doc); + const nodes = single ? xpath.select(expression, doc, true) : xpath.select(expression, doc, false); if (Array.isArray(nodes)) { - nodes.forEach(node => { - const parentNode = node.parentNode; - if (parentNode) { - const parser = new XMLParser(); - const { plugin } = parser.parse(new XMLSerializer().serializeToString(parentNode)); - if (plugin?.artifactId?.includes(SupportedApplications.JIRA)) { - result.add(SupportedApplications.JIRA); - } else if (plugin?.artifactId?.includes(SupportedApplications.CONFLUENCE)) { - result.add(SupportedApplications.CONFLUENCE); - } else if (plugin?.artifactId?.includes(SupportedApplications.BAMBOO)) { - result.add(SupportedApplications.BAMBOO); - } else if (plugin?.artifactId?.includes(SupportedApplications.BITBUCKET)) { - result.add(SupportedApplications.BITBUCKET); - } - } - }); + return nodes; + } else if (single) { + return nodes as Node; + } else { + return []; } } - return Array.from(result); + return []; + } + + private static toObject(node: Node) { + try { + const parser = new XMLParser(); + return parser.parse(new XMLSerializer().serializeToString(node)); + } catch (err) { + return null; + } } } \ No newline at end of file diff --git a/src/applications/bamboo.ts b/src/applications/bamboo.ts index 8333ed1..cae8174 100644 --- a/src/applications/bamboo.ts +++ b/src/applications/bamboo.ts @@ -1,8 +1,8 @@ import axios from 'axios'; -import { getFullPath } from '../helpers/assets'; import { timebomb } from '../helpers/licences'; +import { toAbsolutePath } from '../helpers/toAbsolutePath'; import { ApplicationOptions } from '../types/ApplicationOptions'; import { DatabaseEngine } from '../types/DatabaseEngine'; import { Service } from '../types/DockerComposeV3'; @@ -31,11 +31,15 @@ export class Bamboo extends Base { return { build: { - context: getFullPath('../../assets'), + context: toAbsolutePath('../../assets'), dockerfile_inline: ` FROM dcdx/${this.name}:${this.options.version} -COPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bamboo/shared/plugins/quickreload-5.0.2.jar COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/bamboo/lib/mysql-connector-j-8.3.0.jar +COPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bamboo/shared/plugins/quickreload-5.0.2.jar +RUN echo "/opt/quickreload" > /var/atlassian/application-data/bamboo/quickreload.properties; \ + mkdir -p /opt/quickreload; \ + chown -R bamboo:bamboo /opt/quickreload; + RUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo` }, ports: [ diff --git a/src/applications/base.ts b/src/applications/base.ts index 618b9fa..e279cfc 100644 --- a/src/applications/base.ts +++ b/src/applications/base.ts @@ -1,6 +1,6 @@ import axios from 'axios'; import { spawn } from 'child_process'; -import { downAll, ps, upAll } from 'docker-compose/dist/v2.js'; +import { downAll, execCompose, ps, upAll } from 'docker-compose/dist/v2.js'; import EventEmitter from 'events'; import { gracefulExit } from 'exit-hook'; import { existsSync, mkdirSync } from 'fs'; @@ -64,6 +64,20 @@ export abstract class Base extends EventEmitter { await this.down(); } + async cp(filename: string) { + const service = await this.getServiceState(); + const isRunning = service && service.state.toLowerCase().startsWith('up'); + if (isRunning) { + const config = this.getDockerComposeConfig(); + const configAsString = dump(config); + await execCompose('cp', [ filename, `${this.name}:/opt/quickreload/` ], { + cwd: cwd(), + configAsString, + log: false + }); + } + } + // ------------------------------------------------------------------------------------------ Protected Methods protected abstract getService(): Service; @@ -213,10 +227,8 @@ export abstract class Base extends EventEmitter { const docker = spawn( 'docker', [ 'logs', '-f', '-n', '5000', service ], - { cwd: cwd() } + { cwd: cwd(), stdio: 'inherit' } ); - docker.stdout.on('data', (lines: Buffer) => { console.log(lines.toString('utf-8').trim()); }); - docker.stderr.on('data', (lines: Buffer) => { console.log(lines.toString('utf-8').trim()); }); docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); }); } @@ -226,11 +238,8 @@ export abstract class Base extends EventEmitter { const docker = spawn( 'docker', [ 'exec', '-i', service, `tail`, `-F`, `-n`, `5000`, this.logFilePath ], - { cwd: cwd() } + { cwd: cwd(), stdio: 'inherit' } ); - docker.stdout.on('data', (lines: Buffer) => { console.log(lines.toString('utf-8').trim()); }); - docker.stderr.on('data', (lines: Buffer) => { console.log(lines.toString('utf-8').trim()); }); - docker.on('SIGINT', () => resolve()); docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); }); } diff --git a/src/applications/bitbucket.ts b/src/applications/bitbucket.ts index c4418b2..80452ae 100644 --- a/src/applications/bitbucket.ts +++ b/src/applications/bitbucket.ts @@ -1,6 +1,6 @@ -import { getFullPath } from '../helpers/assets'; import { timebomb } from '../helpers/licences'; +import { toAbsolutePath } from '../helpers/toAbsolutePath'; import { ApplicationOptions } from '../types/ApplicationOptions'; import { DatabaseEngine } from '../types/DatabaseEngine'; import { Service } from '../types/DockerComposeV3'; @@ -29,11 +29,14 @@ export class Bitbucket extends Base { return { build: { - context: getFullPath('../../assets'), + context: toAbsolutePath('../../assets'), dockerfile_inline: ` FROM dcdx/${this.name}:${this.options.version} COPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bitbucket/plugins/installed-plugins/quickreload-5.0.2.jar COPY ./mysql-connector-j-8.3.0.jar /var/atlassian/application-data/bitbucket/lib/mysql-connector-j-8.3.0.jar +RUN echo "/opt/quickreload" > /var/atlassian/application-data/bitbucket/quickreload.properties; \ + mkdir -p /opt/quickreload; \ + chown -R bitbucket:bitbucket /opt/quickreload; RUN mkdir -p /var/atlassian/application-data/bitbucket/shared; \ touch /var/atlassian/application-data/bitbucket/shared/bitbucket.properties; \ diff --git a/src/applications/confluence.ts b/src/applications/confluence.ts index e39f96a..0da16cc 100644 --- a/src/applications/confluence.ts +++ b/src/applications/confluence.ts @@ -1,6 +1,6 @@ -import { getFullPath } from '../helpers/assets'; import { timebomb } from '../helpers/licences'; +import { toAbsolutePath } from '../helpers/toAbsolutePath'; import { ApplicationOptions } from '../types/ApplicationOptions'; import { DatabaseEngine } from '../types/DatabaseEngine'; import { Service } from '../types/DockerComposeV3'; @@ -29,11 +29,15 @@ export class Confluence extends Base { return { build: { - context: getFullPath('../../assets'), + context: toAbsolutePath('../../assets'), dockerfile_inline: ` FROM dcdx/${this.name}:${this.options.version} -COPY ./quickreload-5.0.2.jar /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/quickreload-5.0.2.jar COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/confluence/confluence/WEB-INF/lib/mysql-connector-j-8.3.0.jar +COPY ./quickreload-5.0.2.jar /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/quickreload-5.0.2.jar +RUN echo "/opt/quickreload" > /var/atlassian/application-data/confluence/quickreload.properties; \ + mkdir -p /opt/quickreload; \ + chown -R confluence:confluence /opt/quickreload; + RUN chown -R confluence:confluence /opt/atlassian/confluence` }, ports: [ diff --git a/src/applications/jira.ts b/src/applications/jira.ts index cfbb9f5..2b78d5c 100644 --- a/src/applications/jira.ts +++ b/src/applications/jira.ts @@ -1,6 +1,6 @@ -import { getFullPath } from '../helpers/assets'; import { timebomb } from '../helpers/licences'; +import { toAbsolutePath } from '../helpers/toAbsolutePath'; import { ApplicationOptions } from '../types/ApplicationOptions'; import { DatabaseEngine } from '../types/DatabaseEngine'; import { Service } from '../types/DockerComposeV3'; @@ -28,12 +28,16 @@ export class Jira extends Base { return { build: { - context: getFullPath('../../assets'), + context: toAbsolutePath('../../assets'), dockerfile_inline: ` FROM dcdx/${this.name}:${this.options.version} COPY ./jira-data-generator-5.0.0.jar /var/atlassian/application-data/jira/plugins/installed-plugins/jira-data-generator-5.0.0.jar -COPY ./quickreload-5.0.2.jar /var/atlassian/application-data/jira/plugins/installed-plugins/quickreload-5.0.2.jar COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/jira/lib/mysql-connector-j-8.3.0.jar +COPY ./quickreload-5.0.2.jar /var/atlassian/application-data/jira/plugins/installed-plugins/quickreload-5.0.2.jar +RUN echo "/opt/quickreload" > /var/atlassian/application-data/jira/quickreload.properties; \ + mkdir -p /opt/quickreload; \ + chown -R jira:jira /opt/quickreload; + RUN chown -R jira:jira /var/atlassian/application-data/jira` }, ports: [ diff --git a/src/commands/database-mssql.ts b/src/commands/database-mssql.ts index ff489b6..cf2c24e 100644 --- a/src/commands/database-mssql.ts +++ b/src/commands/database-mssql.ts @@ -7,6 +7,7 @@ import { MSSQL, MSSQLOptions } from '../databases/mssql'; (async () => { const options = program + .showHelpAfterError(true) .addOption(new Option('-v, --version ', 'The version of Microsoft SQL Server').choices([ '2017', '2019', '2022' ]).default('2022')) .addOption(new Option('-e, --edition ', 'The edition of Microsoft SQL Server').choices([ 'Developer', 'Express', 'Standard', 'Enterprise', 'EnterpriseCore' ]).default('Developer')) .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('1433')) diff --git a/src/commands/database-mysql.ts b/src/commands/database-mysql.ts index 3eb12c3..8770127 100644 --- a/src/commands/database-mysql.ts +++ b/src/commands/database-mysql.ts @@ -7,6 +7,7 @@ import { MySQL } from '../databases/mysql'; (async () => { const options = program + .showHelpAfterError(true) .addOption(new Option('-v, --version ', 'The version of Postgres').choices([ '8.0', '8.3' ]).default('8.3')) .addOption(new Option('-d, --database ', 'The value passed to MYSQL_DATABASE environment variable').default('dcdx')) .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('3306')) diff --git a/src/commands/database-postgres.ts b/src/commands/database-postgres.ts index 483a029..6800961 100644 --- a/src/commands/database-postgres.ts +++ b/src/commands/database-postgres.ts @@ -7,6 +7,7 @@ import { Postgres } from '../databases/postgres'; (async () => { const options = program + .showHelpAfterError(true) .addOption(new Option('-v, --version ', 'The version of Postgres').choices([ '12', '13', '14', '15']).default('15')) .addOption(new Option('-d, --database ', 'The value passed to POSTGRES_DB environment variable').default('dcdx')) .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('5432')) diff --git a/src/commands/database.ts b/src/commands/database.ts index 38ccf73..2c267c1 100644 --- a/src/commands/database.ts +++ b/src/commands/database.ts @@ -7,6 +7,7 @@ program .command('postgresql', 'Start PostgreSQL', { executableFile: './database-postgres.js'}) .command('mysql', 'Start MySQL', { executableFile: './database-mysql.js'}) .command('mssql', 'Start Microsoft SQL Server', { executableFile: './database-mssql.js'}) + .showHelpAfterError(true); program.parse(); diff --git a/src/commands/run-bamboo.ts b/src/commands/run-bamboo.ts index b0c6fce..e1c2cf3 100644 --- a/src/commands/run-bamboo.ts +++ b/src/commands/run-bamboo.ts @@ -7,6 +7,7 @@ import { Bamboo } from '../applications/bamboo'; (async () => { const options = program + .showHelpAfterError(true) .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.4.3' ]).default('9.4.3')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) diff --git a/src/commands/run-bitbucket.ts b/src/commands/run-bitbucket.ts index 000724a..fd0ae9e 100644 --- a/src/commands/run-bitbucket.ts +++ b/src/commands/run-bitbucket.ts @@ -7,6 +7,7 @@ import { Bitbucket } from '../applications/bitbucket'; (async () => { const options = program + .showHelpAfterError(true) .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '8.9.0' ]).default('8.9.0')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) diff --git a/src/commands/run-confluence.ts b/src/commands/run-confluence.ts index 4e26949..114f7d3 100644 --- a/src/commands/run-confluence.ts +++ b/src/commands/run-confluence.ts @@ -7,6 +7,7 @@ import { Confluence } from '../applications/confluence'; (async () => { const options = program + .showHelpAfterError(true) .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '8.9.0' ]).default('8.9.0')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) diff --git a/src/commands/run-jira.ts b/src/commands/run-jira.ts index 3e0bc8b..5de033c 100644 --- a/src/commands/run-jira.ts +++ b/src/commands/run-jira.ts @@ -7,6 +7,7 @@ import { Jira } from '../applications/jira'; (async () => { const options = program + .showHelpAfterError(true) .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.15.0' ]).default('9.15.0')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) diff --git a/src/commands/run.ts b/src/commands/run.ts index d3d5c1c..e2fd8ef 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -21,6 +21,7 @@ program .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'}); + .command('jira', 'Start Atlassian Jira (standalone)', { executableFile: './run-jira.js'}) + .showHelpAfterError(true); program.parse(process.argv); \ No newline at end of file diff --git a/src/commands/start.ts b/src/commands/start.ts new file mode 100644 index 0000000..d58c711 --- /dev/null +++ b/src/commands/start.ts @@ -0,0 +1,102 @@ +#!/usr/bin/env node + +import { watch } from 'chokidar'; +import { Option, program } from 'commander'; +import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { cwd } from 'process'; + +import { AMPS } from '../applications/amps'; +import { getApplicationByName } from '../helpers/getApplication'; +import { isRecursiveBuild } from '../helpers/isRecursiveBuild'; +import { showRecursiveBuildWarning } from '../helpers/showRecursiveBuildWarning'; + +if (!AMPS.isAtlassianPlugin()) { + console.log('Unable to find an Atlassian Plugin project in the current directory 🤔'); + gracefulExit(); +} + +const application = AMPS.getApplication(); +if (!application) { + console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + gracefulExit(); + process.exit(); +} + +const Application = getApplicationByName(application); +if (!Application) { + console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + process.exit(); +} + +const version = AMPS.getApplicationVersion(); + +(async () => { + const options = program + .name('dcdx start') + .description('Build & install the Atlassian Data Center plugin from the current directory.\nYou can add Maven build arguments after the command options.') + .usage('[options] [...maven_arguments]') + .addOption(new Option('-v, --version ', 'The version of the host application').default(version)) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) + .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('-o, --outputDirectory ', 'Output directory where QuickReload will look for generated JAR files').default('target')) + .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(true)) + .allowUnknownOption(true) + .parse(process.argv) + .opts(); + + const instance = new Application({ + version: options.version, + database: options.database, + port: Number(options.port), + contextPath: options.contextPath, + debug: options.debug + }); + + const mavenOpts = program.args.slice(); + if (options.activateProfiles) { + mavenOpts.push(...[ '-P', options.activateProfiles ]); + } + + console.log('Watching filesystem for changes to source files (QuickReload)'); + let lastBuildCompleted = new Date().getTime(); + const quickReload = watch('**/*', { + cwd: cwd(), + usePolling: true, + interval: 2 * 1000, + binaryInterval: 2 * 1000, + awaitWriteFinish: true + }).on('change', async (path) => { + if (path.startsWith(options.outputDirectory) && path.toLowerCase().endsWith('.jar')) { + console.log('Found updated JAR file(s), uploading them to QuickReload'); + await instance.cp(path); + lastBuildCompleted = new Date().getTime(); + } else if (!path.startsWith(options.outputDirectory)) { + if (isRecursiveBuild(lastBuildCompleted)) { + showRecursiveBuildWarning(options.outputDirectory); + } else { + console.log('Detected file change, rebuilding Atlasian Plugin for QuickReload'); + await AMPS.build(mavenOpts).catch(() => Promise.resolve()); + } + } + }); + + asyncExitHook(async () => { + console.log(`Stopping filesystem watcher... ⏳`); + await quickReload.close(); + console.log(`Stopping ${instance.name}... ⏳`); + await instance.stop(); + console.log(`Successfully stopped all running processes 💪`); + }, { wait: 30 * 1000 }); + + console.log('Starting application...'); + await instance.start(); + +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); + diff --git a/src/helpers/getApplication.ts b/src/helpers/getApplication.ts new file mode 100644 index 0000000..2ff88c0 --- /dev/null +++ b/src/helpers/getApplication.ts @@ -0,0 +1,15 @@ +import { Bamboo } from '../applications/bamboo'; +import { Bitbucket } from '../applications/bitbucket'; +import { Confluence } from '../applications/confluence'; +import { Jira } from '../applications/jira'; +import { SupportedApplications } from '../types/SupportedApplications'; + + +export const getApplicationByName = (name: SupportedApplications) => { + switch (name) { + case SupportedApplications.JIRA: return Jira; + case SupportedApplications.CONFLUENCE: return Confluence; + case SupportedApplications.BAMBOO: return Bamboo; + case SupportedApplications.BITBUCKET: return Bitbucket; + } +} \ No newline at end of file diff --git a/src/helpers/isRecursiveBuild.ts b/src/helpers/isRecursiveBuild.ts new file mode 100644 index 0000000..300f898 --- /dev/null +++ b/src/helpers/isRecursiveBuild.ts @@ -0,0 +1,3 @@ + +export const isRecursiveBuild = (lastBuildCompleted: number) => + lastBuildCompleted > (new Date().getTime() - 5 * 1000) diff --git a/src/helpers/showRecursiveBuildWarning.ts b/src/helpers/showRecursiveBuildWarning.ts new file mode 100644 index 0000000..ddf925f --- /dev/null +++ b/src/helpers/showRecursiveBuildWarning.ts @@ -0,0 +1,13 @@ + +export const showRecursiveBuildWarning = (outputDirectory: string) => { + console.log(` +=============================================================================================================== +Recursive build trigger detected. The last build completed last than 5 seconds ago +This may indicate that the build changes files outside of the output directory +Alternatively, Maven is using a different output directory than configured: +'${outputDirectory}' + +Please make sure to check your build process and/or specify a different output directory using the '-o' option +=============================================================================================================== + `); +} diff --git a/src/helpers/assets.ts b/src/helpers/toAbsolutePath.ts similarity index 82% rename from src/helpers/assets.ts rename to src/helpers/toAbsolutePath.ts index dbddc27..7d3353a 100644 --- a/src/helpers/assets.ts +++ b/src/helpers/toAbsolutePath.ts @@ -1,6 +1,6 @@ import { dirname, join } from 'path'; -export const getFullPath = (relativePath: string) => { +export const toAbsolutePath = (relativePath: string) => { const [ , filePath ] = process.argv; const executableFileDir = dirname(filePath); const basedir = executableFileDir.substring(0, executableFileDir.indexOf('dcdx') + 4); diff --git a/src/index.ts b/src/index.ts index ec8b29e..2ae762f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,10 +7,14 @@ import pkg from '../package.json'; program .name('dcdx') .description('The Unofficial Atlassian Data Center Plugin Development CLI') - .version(pkg.version); + .version(pkg.version) + .showHelpAfterError(true); // ------------------------------------------------------------------------------------------ Run +program + .command('start', 'Build & install the Atlassian Data Center plugin from the current directory', { executableFile: './commands/start.js' }); + program .command('run', 'Start the Atlassian host application (standalone)', { executableFile: './commands/run.js' }); diff --git a/yarn.lock b/yarn.lock index 72d3689..9648ccc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1266,6 +1266,22 @@ __metadata: languageName: node linkType: hard +"@types/yargs-parser@npm:*": + version: 21.0.3 + resolution: "@types/yargs-parser@npm:21.0.3" + checksum: 10c0/e71c3bd9d0b73ca82e10bee2064c384ab70f61034bbfb78e74f5206283fc16a6d85267b606b5c22cb2a3338373586786fed595b2009825d6a9115afba36560a0 + languageName: node + linkType: hard + +"@types/yargs@npm:17.0.32": + version: 17.0.32 + resolution: "@types/yargs@npm:17.0.32" + dependencies: + "@types/yargs-parser": "npm:*" + checksum: 10c0/2095e8aad8a4e66b86147415364266b8d607a3b95b4239623423efd7e29df93ba81bb862784a6e08664f645cc1981b25fd598f532019174cd3e5e1e689e1cccf + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:7.6.0": version: 7.6.0 resolution: "@typescript-eslint/eslint-plugin@npm:7.6.0" @@ -1857,7 +1873,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.5.2": +"chokidar@npm:3.6.0, chokidar@npm:^3.5.2": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -2263,10 +2279,12 @@ __metadata: "@types/js-yaml": "npm:4" "@types/node": "npm:18.16.0" "@types/pg": "npm:8" + "@types/yargs": "npm:17.0.32" "@typescript-eslint/eslint-plugin": "npm:7.6.0" "@typescript-eslint/parser": "npm:7.6.0" "@xmldom/xmldom": "npm:0.8.10" axios: "npm:1.6.8" + chokidar: "npm:3.6.0" commander: "npm:12.0.0" docker-compose: "npm:0.24.8" eslint: "npm:9.0.0" @@ -2286,6 +2304,7 @@ __metadata: typescript: "npm:5.4.4" typescript-eslint: "npm:7.6.0" xpath: "npm:0.0.34" + yargs: "npm:17.7.2" bin: dcdx: ./lib/index.js languageName: unknown @@ -7431,33 +7450,33 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^16.0.0": - version: 16.2.0 - resolution: "yargs@npm:16.2.0" +"yargs@npm:17.7.2, yargs@npm:^17.5.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" dependencies: - cliui: "npm:^7.0.2" + cliui: "npm:^8.0.1" escalade: "npm:^3.1.1" get-caller-file: "npm:^2.0.5" require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.0" + string-width: "npm:^4.2.3" y18n: "npm:^5.0.5" - yargs-parser: "npm:^20.2.2" - checksum: 10c0/b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 languageName: node linkType: hard -"yargs@npm:^17.5.1": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" +"yargs@npm:^16.0.0": + version: 16.2.0 + resolution: "yargs@npm:16.2.0" dependencies: - cliui: "npm:^8.0.1" + cliui: "npm:^7.0.2" escalade: "npm:^3.1.1" get-caller-file: "npm:^2.0.5" require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.3" + string-width: "npm:^4.2.0" y18n: "npm:^5.0.5" - yargs-parser: "npm:^21.1.1" - checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + yargs-parser: "npm:^20.2.2" + checksum: 10c0/b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 languageName: node linkType: hard From e18c4a99f1a40450b4e0aa4c5fe395069cc4f3d9 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Wed, 17 Apr 2024 09:19:53 +0200 Subject: [PATCH 08/18] feat: add new reset command & clean/prune arguments The reset command allows you to delete all Atlassian host product data, which will now be persisted unless you use the new clean/prune options --- src/applications/amps.ts | 1 - src/applications/bamboo.ts | 2 +- src/applications/base.ts | 39 +++++++++++++++++++++---------- src/applications/bitbucket.ts | 2 +- src/applications/confluence.ts | 2 +- src/applications/jira.ts | 2 +- src/commands/database-mssql.ts | 4 ++++ src/commands/database-mysql.ts | 4 ++++ src/commands/database-postgres.ts | 4 ++++ src/commands/reset-bamboo.ts | 30 ++++++++++++++++++++++++ src/commands/reset-bitbucket.ts | 30 ++++++++++++++++++++++++ src/commands/reset-confluence.ts | 30 ++++++++++++++++++++++++ src/commands/reset-jira.ts | 30 ++++++++++++++++++++++++ src/commands/reset.ts | 28 ++++++++++++++++++++++ src/commands/run-bamboo.ts | 6 ++++- src/commands/run-bitbucket.ts | 6 ++++- src/commands/run-confluence.ts | 4 ++++ src/commands/run-jira.ts | 4 ++++ src/commands/run.ts | 3 ++- src/commands/start.ts | 4 ++++ src/databases/base.ts | 29 +++++++++++++++-------- src/index.ts | 9 ++++++- src/types/ApplicationOptions.ts | 4 +++- src/types/DatabaseEngine.ts | 6 ++--- src/types/DatabaseOptions.ts | 2 ++ 25 files changed, 249 insertions(+), 36 deletions(-) create mode 100644 src/commands/reset-bamboo.ts create mode 100644 src/commands/reset-bitbucket.ts create mode 100644 src/commands/reset-confluence.ts create mode 100644 src/commands/reset-jira.ts create mode 100644 src/commands/reset.ts diff --git a/src/applications/amps.ts b/src/applications/amps.ts index 14fc4ef..12ab60d 100644 --- a/src/applications/amps.ts +++ b/src/applications/amps.ts @@ -56,7 +56,6 @@ export class AMPS { const nodes = AMPS.getNodes('//*[local-name()=\'packaging\']'); return nodes.some(item => item.textContent === 'atlassian-plugin'); } catch (err) { - console.log(err); return false; } } diff --git a/src/applications/bamboo.ts b/src/applications/bamboo.ts index cae8174..123639b 100644 --- a/src/applications/bamboo.ts +++ b/src/applications/bamboo.ts @@ -24,7 +24,7 @@ export class Bamboo extends Base { // ------------------------------------------------------------------------------------------ Protected Methods - protected getService = (): Service => { + protected getService(): Service { const volumes = this.getVolumes(); const environment = this.getEnvironmentVariables(); diff --git a/src/applications/base.ts b/src/applications/base.ts index e279cfc..2377ba6 100644 --- a/src/applications/base.ts +++ b/src/applications/base.ts @@ -1,6 +1,6 @@ import axios from 'axios'; import { spawn } from 'child_process'; -import { downAll, execCompose, ps, upAll } from 'docker-compose/dist/v2.js'; +import { downAll, execCompose, ps, stop, upAll } from 'docker-compose/dist/v2.js'; import EventEmitter from 'events'; import { gracefulExit } from 'exit-hook'; import { existsSync, mkdirSync } from 'fs'; @@ -37,7 +37,10 @@ export abstract class Base extends EventEmitter { // ------------------------------------------------------------------------------------------ Properties protected get baseUrl(): string { - const baseUrl = `http://localhost:${this.options.port}`; + let baseUrl = `http://localhost`; + if (this.options.port) { + baseUrl += `:${this.options.port}`; + } return this.options.contextPath ? `${baseUrl}/${this.options.contextPath}` : baseUrl; } @@ -52,15 +55,32 @@ export abstract class Base extends EventEmitter { } async start() { - await this.stop(); + if (this.options.clean) { + await this.down(); + } await this.build(this.options.version); - await this.database.start(this.name, this.options.version); + await this.database.start(this.options.clean); await this.up(); } - async stop() { - await this.database.stop(); + async stop(): Promise { + await this.database.stop(this.options.prune); + if (this.options.prune) { + await this.down(); + } else { + const configAsString = dump(this.getDockerComposeConfig()); + await stop({ + cwd: cwd(), + configAsString, + log: true + }); + } + this.emit(`${this.name}:stopped`); + } + + async reset() { + await this.database.stop(true); await this.down(); } @@ -140,15 +160,12 @@ export abstract class Base extends EventEmitter { private async down() { const configAsString = dump(this.getDockerComposeConfig()); - await downAll({ cwd: cwd(), configAsString, commandOptions: [ '-v', '--remove-orphans', '--rmi', 'local' ], log: true }); - - this.emit(`${this.name}:stopped`); } private async getServiceState() { @@ -205,10 +222,8 @@ export abstract class Base extends EventEmitter { const docker = spawn( 'docker', [ 'build', '-t', `dcdx/${this.name}:${version}`, '--build-arg', `${this.name.toUpperCase()}_VERSION=${version}`, '.'], - { cwd: checkoutPath } + { cwd: checkoutPath, stdio: 'inherit' } ); - docker.stdout.on('data', (lines: Buffer) => { console.log(lines.toString('utf-8').trim()); }); - docker.stderr.on('data', (lines: Buffer) => { console.log(lines.toString('utf-8').trim()); }); docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); }); } diff --git a/src/applications/bitbucket.ts b/src/applications/bitbucket.ts index 80452ae..22ec401 100644 --- a/src/applications/bitbucket.ts +++ b/src/applications/bitbucket.ts @@ -22,7 +22,7 @@ export class Bitbucket extends Base { // ------------------------------------------------------------------------------------------ Protected Methods - protected getService = (): Service => { + protected getService(): Service { const volumes = this.getVolumes(); const environment = this.getEnvironmentVariables(); diff --git a/src/applications/confluence.ts b/src/applications/confluence.ts index 0da16cc..adf6a45 100644 --- a/src/applications/confluence.ts +++ b/src/applications/confluence.ts @@ -22,7 +22,7 @@ export class Confluence extends Base { // ------------------------------------------------------------------------------------------ Protected Methods - protected getService = (): Service => { + protected getService(): Service { const volumes = this.getVolumes(); const environment = this.getEnvironmentVariables(); diff --git a/src/applications/jira.ts b/src/applications/jira.ts index 2b78d5c..c33eada 100644 --- a/src/applications/jira.ts +++ b/src/applications/jira.ts @@ -22,7 +22,7 @@ export class Jira extends Base { // ------------------------------------------------------------------------------------------ Protected Methods - protected getService = (): Service => { + protected getService(): Service { const volumes = this.getVolumes(); const environment = this.getEnvironmentVariables(); diff --git a/src/commands/database-mssql.ts b/src/commands/database-mssql.ts index cf2c24e..1ec6781 100644 --- a/src/commands/database-mssql.ts +++ b/src/commands/database-mssql.ts @@ -12,6 +12,8 @@ import { MSSQL, MSSQLOptions } from '../databases/mssql'; .addOption(new Option('-e, --edition ', 'The edition of Microsoft SQL Server').choices([ 'Developer', 'Express', 'Standard', 'Enterprise', 'EnterpriseCore' ]).default('Developer')) .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('1433')) .addOption(new Option('-P, --password ', 'The value passed to MSSQL_SA_PASSWORD environment variable. MS SQL Server password policy applies.').default('DataCenterDX!')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) .parse(process.argv) .opts(); @@ -20,6 +22,8 @@ import { MSSQL, MSSQLOptions } from '../databases/mssql'; edition: options.edition, port: Number(options.port), password: options.password, + clean: options.clean, + prune: options.prune, logging: true } as MSSQLOptions); diff --git a/src/commands/database-mysql.ts b/src/commands/database-mysql.ts index 8770127..6c4609e 100644 --- a/src/commands/database-mysql.ts +++ b/src/commands/database-mysql.ts @@ -13,6 +13,8 @@ import { MySQL } from '../databases/mysql'; .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('3306')) .addOption(new Option('-U, --username ', 'The value passed to MYSQL_USER environment variable').default('dcdx')) .addOption(new Option('-P, --password ', 'The value passed to MYSQL_PASSWORD environment variable').default('dcdx')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) .parse(process.argv) .opts(); @@ -22,6 +24,8 @@ import { MySQL } from '../databases/mysql'; port: Number(options.port), username: options.username, password: options.password, + clean: options.clean, + prune: options.prune, logging: true }); diff --git a/src/commands/database-postgres.ts b/src/commands/database-postgres.ts index 6800961..7e09a6b 100644 --- a/src/commands/database-postgres.ts +++ b/src/commands/database-postgres.ts @@ -13,6 +13,8 @@ import { Postgres } from '../databases/postgres'; .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('5432')) .addOption(new Option('-U, --username ', 'The value passed to POSTGRES_USER environment variable').default('dcdx')) .addOption(new Option('-P, --password ', 'The value passed to POSTGRES_PASSWORD environment variable').default('dcdx')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) .parse(process.argv) .opts(); @@ -22,6 +24,8 @@ import { Postgres } from '../databases/postgres'; port: Number(options.port), username: options.username, password: options.password, + clean: options.clean, + prune: options.prune, logging: true }) diff --git a/src/commands/reset-bamboo.ts b/src/commands/reset-bamboo.ts new file mode 100644 index 0000000..e70a6cb --- /dev/null +++ b/src/commands/reset-bamboo.ts @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { gracefulExit } from 'exit-hook'; + +import { AMPS } from '../applications/amps'; +import { Bamboo } from '../applications/bamboo'; + +const version = AMPS.getApplicationVersion() || '9.6.1'; + +(async () => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.4.3' ]).default(version)) + .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .parse(process.argv) + .opts(); + + const instance = new Bamboo({ + version: options.version, + database: options.database + }); + + await instance.reset(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/commands/reset-bitbucket.ts b/src/commands/reset-bitbucket.ts new file mode 100644 index 0000000..1439075 --- /dev/null +++ b/src/commands/reset-bitbucket.ts @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { gracefulExit } from 'exit-hook'; + +import { AMPS } from '../applications/amps'; +import { Bitbucket } from '../applications/bitbucket'; + +const version = AMPS.getApplicationVersion() || '9.4.3'; + +(async () => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.4.3' ]).default(version)) + .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .parse(process.argv) + .opts(); + + const instance = new Bitbucket({ + version: options.version, + database: options.database + }); + + await instance.reset(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/commands/reset-confluence.ts b/src/commands/reset-confluence.ts new file mode 100644 index 0000000..8723ca7 --- /dev/null +++ b/src/commands/reset-confluence.ts @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { gracefulExit } from 'exit-hook'; + +import { AMPS } from '../applications/amps'; +import { Confluence } from '../applications/confluence'; + +const version = AMPS.getApplicationVersion() || '8.9.0'; + +(async () => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '8.9.0' ]).default(version)) + .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .parse(process.argv) + .opts(); + + const instance = new Confluence({ + version: options.version, + database: options.database + }); + + await instance.reset(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/commands/reset-jira.ts b/src/commands/reset-jira.ts new file mode 100644 index 0000000..0dddec7 --- /dev/null +++ b/src/commands/reset-jira.ts @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { gracefulExit } from 'exit-hook'; + +import { AMPS } from '../applications/amps'; +import { Jira } from '../applications/jira'; + +const version = AMPS.getApplicationVersion() || '9.15.0'; + +(async () => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.15.0' ]).default(version)) + .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .parse(process.argv) + .opts(); + + const instance = new Jira({ + version: options.version, + database: options.database, + }); + + await instance.reset(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/commands/reset.ts b/src/commands/reset.ts new file mode 100644 index 0000000..2458499 --- /dev/null +++ b/src/commands/reset.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; + +import { AMPS } from '../applications/amps'; +import { SupportedApplications } from '../types/SupportedApplications'; + +// Check if there is a command in the arguments +const isDefaultCommand = !process.argv.some(item => Object.values(SupportedApplications).includes(item as SupportedApplications)); +// If there is no command, check if we are running this within the context of an Atlassian Plugin project +if (isDefaultCommand) { + const application = AMPS.getApplication(); + if (application) { + const args = [ application, ...process.argv.splice(2) ]; + process.argv = [ ...process.argv.slice(0, 2), ...args ]; + } +} + +program + .name('dcdx reset') + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .command('bamboo', 'Remove all data (incl. database) for Atlassian Bamboo (standalone)', { executableFile: './reset-bamboo.js'}) + .command('bitbucket', 'Remove all data (incl. database) for Atlassian Bitbucket (standalone)', { executableFile: './reset-bitbucket.js'}) + .command('confluence', 'Remove all data (incl. database) for Atlassian Confluence (standalone)', { executableFile: './reset-confluence.js'}) + .command('jira', 'Remove all data (incl. database) for Atlassian Jira (standalone)', { executableFile: './reset-jira.js'}) + .showHelpAfterError(true); + +program.parse(process.argv); \ No newline at end of file diff --git a/src/commands/run-bamboo.ts b/src/commands/run-bamboo.ts index e1c2cf3..0694f16 100644 --- a/src/commands/run-bamboo.ts +++ b/src/commands/run-bamboo.ts @@ -8,11 +8,13 @@ import { Bamboo } from '../applications/bamboo'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.4.3' ]).default('9.4.3')) + .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.4.3' ]).default('9.6.1')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) .parse(process.argv) .opts(); @@ -23,6 +25,8 @@ import { Bamboo } from '../applications/bamboo'; port: Number(options.port), contextPath: options.contextPath, quickReload: options.qr, + clean: options.clean, + prune: options.prune, debug: options.debug }); diff --git a/src/commands/run-bitbucket.ts b/src/commands/run-bitbucket.ts index fd0ae9e..39e470b 100644 --- a/src/commands/run-bitbucket.ts +++ b/src/commands/run-bitbucket.ts @@ -8,10 +8,12 @@ import { Bitbucket } from '../applications/bitbucket'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '8.9.0' ]).default('8.9.0')) + .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '8.19.1' ]).default('8.19.1')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) .parse(process.argv) .opts(); @@ -21,6 +23,8 @@ import { Bitbucket } from '../applications/bitbucket'; database: options.database, port: Number(options.port), quickReload: options.qr, + clean: options.clean, + prune: options.prune, debug: options.debug }); diff --git a/src/commands/run-confluence.ts b/src/commands/run-confluence.ts index 114f7d3..7e9d731 100644 --- a/src/commands/run-confluence.ts +++ b/src/commands/run-confluence.ts @@ -13,6 +13,8 @@ import { Confluence } from '../applications/confluence'; .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) .parse(process.argv) .opts(); @@ -23,6 +25,8 @@ import { Confluence } from '../applications/confluence'; port: Number(options.port), contextPath: options.contextPath, quickReload: options.qr, + clean: options.clean, + prune: options.prune, debug: options.debug }); diff --git a/src/commands/run-jira.ts b/src/commands/run-jira.ts index 5de033c..0cbfb70 100644 --- a/src/commands/run-jira.ts +++ b/src/commands/run-jira.ts @@ -13,6 +13,8 @@ import { Jira } from '../applications/jira'; .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) .parse(process.argv) .opts(); @@ -23,6 +25,8 @@ import { Jira } from '../applications/jira'; port: Number(options.port), contextPath: options.contextPath, quickReload: options.qr, + clean: options.clean, + prune: options.prune, debug: options.debug }); diff --git a/src/commands/run.ts b/src/commands/run.ts index e2fd8ef..c539997 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { program } from 'commander'; +import { Option, program } from 'commander'; import { AMPS } from '../applications/amps'; import { SupportedApplications } from '../types/SupportedApplications'; @@ -18,6 +18,7 @@ if (isDefaultCommand) { program .name('dcdx run') + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) .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'}) diff --git a/src/commands/start.ts b/src/commands/start.ts index d58c711..eec4b77 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -41,6 +41,8 @@ const version = AMPS.getApplicationVersion(); .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) .addOption(new Option('-o, --outputDirectory ', 'Output directory where QuickReload will look for generated JAR files').default('target')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(true)) .allowUnknownOption(true) .parse(process.argv) @@ -51,6 +53,8 @@ const version = AMPS.getApplicationVersion(); database: options.database, port: Number(options.port), contextPath: options.contextPath, + clean: options.clean, + prune: options.prune, debug: options.debug }); diff --git a/src/databases/base.ts b/src/databases/base.ts index 23181f4..189877c 100644 --- a/src/databases/base.ts +++ b/src/databases/base.ts @@ -1,6 +1,6 @@ import { spawn } from 'child_process'; import { upAll } from 'docker-compose'; -import { downAll, ps } from 'docker-compose/dist/v2.js'; +import { downAll, ps, stop } from 'docker-compose/dist/v2.js'; import EventEmitter from 'events'; import { gracefulExit } from 'exit-hook'; import { dump } from 'js-yaml'; @@ -42,13 +42,16 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { return null; } - async start(): Promise { + async start(clean = this.options.clean): Promise { console.log(`Starting instance of ${this.name} ⏳`); - await this.stop(); - await this.up(); + if (clean) { + await this.down(); + } + await this.up(); this.emit(`${this.name}:up`); + const isAvailable = await this.waitUntilReady(); if (!isAvailable) { console.log(`Failed to start database ${this.name} ⛔`); @@ -67,8 +70,17 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { } } - async stop(): Promise { - await this.down(); + async stop(prune = this.options.prune): Promise { + if (prune) { + await this.down(); + } else { + const configAsString = dump(this.getDockerComposeConfig()); + await stop({ + cwd: cwd(), + configAsString, + log: true + }) + } this.emit('db:stopped'); } @@ -103,7 +115,6 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { private async down() { const configAsString = dump(this.getDockerComposeConfig()); - return downAll({ cwd: cwd(), configAsString, @@ -149,10 +160,8 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { const docker = spawn( 'docker', [ 'logs', '-f', '-n', '5000', service ], - { cwd: cwd() } + { cwd: cwd(), stdio: 'inherit' } ); - docker.stdout.on('data', (lines: Buffer) => { console.log(lines.toString('utf-8').trim()); }); - docker.stderr.on('data', (lines: Buffer) => { console.log(lines.toString('utf-8').trim()); }); docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); }); } diff --git a/src/index.ts b/src/index.ts index 2ae762f..157833b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,11 +10,13 @@ program .version(pkg.version) .showHelpAfterError(true); -// ------------------------------------------------------------------------------------------ Run +// ------------------------------------------------------------------------------------------ Start program .command('start', 'Build & install the Atlassian Data Center plugin from the current directory', { executableFile: './commands/start.js' }); +// ------------------------------------------------------------------------------------------ Run + program .command('run', 'Start the Atlassian host application (standalone)', { executableFile: './commands/run.js' }); @@ -87,6 +89,11 @@ program program.parse(process.argv); }); +// ------------------------------------------------------------------------------------------ Reset + +program + .command('reset', 'Remove all application data (incl. database) and start fresh!', { executableFile: './commands/reset.js' }); + // ------------------------------------------------------------------------------------------ Profile program diff --git a/src/types/ApplicationOptions.ts b/src/types/ApplicationOptions.ts index 7cf3453..1f77fb4 100644 --- a/src/types/ApplicationOptions.ts +++ b/src/types/ApplicationOptions.ts @@ -3,9 +3,11 @@ import { SupportedDatabaseEngines } from './SupportedDatabaseEngines'; export type ApplicationOptions = { version: string; database: SupportedDatabaseEngines; - port: number; + port?: number; contextPath?: string; quickReload?: boolean; license?: string; + clean?: boolean; + prune?: boolean; debug?: boolean; } \ No newline at end of file diff --git a/src/types/DatabaseEngine.ts b/src/types/DatabaseEngine.ts index 43955a2..73eb6b6 100644 --- a/src/types/DatabaseEngine.ts +++ b/src/types/DatabaseEngine.ts @@ -1,7 +1,6 @@ import EventEmitter from 'events'; import { DatabaseOptions } from './DatabaseOptions'; -import { SupportedApplications } from './SupportedApplications'; import { SupportedDatabaseDrivers } from './SupportedDatabaseDrivers'; import { SupportedDatabaseEngines } from './SupportedDatabaseEngines'; @@ -10,8 +9,7 @@ export interface DatabaseEngine extends EventEmitter { url: string; driver: SupportedDatabaseDrivers; options: DatabaseOptions; - start(): Promise; - start(application: SupportedApplications, version: string): Promise; - stop(): Promise; + start(clean?: boolean): Promise; + stop(prune?: boolean): Promise; run(sql: string | { query: string; values: unknown[] }): Promise<[unknown[], unknown]|null>; } \ No newline at end of file diff --git a/src/types/DatabaseOptions.ts b/src/types/DatabaseOptions.ts index 0ed6745..db95b76 100644 --- a/src/types/DatabaseOptions.ts +++ b/src/types/DatabaseOptions.ts @@ -5,5 +5,7 @@ export type DatabaseOptions = { database: string; username: string; password: string; + clean?: boolean; + prune?: boolean; logging?: boolean; } \ No newline at end of file From b57f93c2216d8878cf59176ada3fceb62edfa397 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Wed, 17 Apr 2024 12:42:30 +0200 Subject: [PATCH 09/18] feat: use Docker tags as version list Retrieve the list of supported Docker tags for each application/database from Docker Hub using the API and embed it in the command --- assets/versions.json | 1 + generateVersionList.mjs | 49 +++++++++++++++++++++++++++++++ package.json | 5 ++-- src/commands/database-mssql.ts | 3 +- src/commands/database-mysql.ts | 3 +- src/commands/database-postgres.ts | 3 +- src/commands/reset-bamboo.ts | 5 ++-- src/commands/reset-bitbucket.ts | 5 ++-- src/commands/reset-confluence.ts | 5 ++-- src/commands/reset-jira.ts | 5 ++-- src/commands/run-bamboo.ts | 3 +- src/commands/run-bitbucket.ts | 3 +- src/commands/run-confluence.ts | 3 +- src/commands/run-jira.ts | 3 +- src/index.ts | 4 +-- 15 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 assets/versions.json create mode 100644 generateVersionList.mjs diff --git a/assets/versions.json b/assets/versions.json new file mode 100644 index 0000000..f24bd14 --- /dev/null +++ b/assets/versions.json @@ -0,0 +1 @@ +{"jira":["latest","9.8.0-ubuntu-jdk11","9.8.0-jdk11","9.8.0","9.5-ubuntu-jdk11","9.5-jdk11","9.5.1-ubuntu-jdk11","9.5.1-jdk11","9.5.1","9.5","9.14.0-ubuntu-jdk11","9.14.0-jdk11","9.14-ubuntu-jdk11","9.14.0","9.14-jdk11","9.14.1-ubuntu-jdk11","9.14","9.14.1","9.12.5-ubuntu-jdk11","9.12.5-jdk11","9.12.5","9.0-ubuntu-jdk11","9.0-jdk11","9.0.0-ubuntu-jdk11","9.0.0-jdk11","9.0.0","9.0","9.8.1-ubuntu-jdk11","9.8.1-jdk11","9.8.1","9.14.1-jdk11","9.10-ubuntu-jdk11","9.10-jdk11","9.10.2-ubuntu-jdk11","9.10.2-jdk11","9.10.2","9.8","9.10","9.12.6-jdk11","9.12.6-ubuntu-jdk11","9.12.6","9.8-ubuntu-jdk11","9.8-jdk11","9.8.2-ubuntu-jdk11","9.8.2-jdk11","9.8.2","9.6-ubuntu-jdk11","9.6-jdk11","9.6.0-ubuntu-jdk11","9.6.0-jdk11","9.6.0","9.6","9.4.2-ubuntu-jdk11","9.4.2-jdk11","9.4.2","9.3.0-ubuntu-jdk11","9.3.0-jdk11","9.3.0","9.14.0-ubi9-jdk17","9.14.0-ubi9","9.12.7-ubi9-jdk17","9.12.7-ubi9","9.5.0-ubi9-jdk17","9.5.0-ubi9","9.12-ubuntu-jdk11","9.12-jdk11","9.12.7-ubuntu-jdk11","9.12.7-jdk11","9.12.7","9.12","9.1.0-ubuntu-jdk11","9.1.0-jdk11","ubuntu-jdk11","9.1.0","jdk11","9-ubuntu-jdk11","9-jdk11","9.4-ubuntu-jdk11","9.4-jdk11","9.4.20-ubuntu-jdk11","9.4.20-jdk11","9.4.20","9.4","9.3.1-ubuntu-jdk11","9.3.1-jdk11","9.3.1","9.15-ubuntu-jdk11","9.15-jdk11","9.15.0-ubuntu-jdk11","9.15.0-jdk11","9.15.0","9.15","9.12.0-ubuntu-jdk11","9.12.0-jdk11","9.12.0","9.11.0-ubuntu-jdk11","9.11.0-jdk11","9.11.0","9","9.8.0-ubi9-jdk17","9.8.0-ubi9","9.7.0-ubi9-jdk17","9.7.0-ubi9","9.5.1-ubi9-jdk17","9.5.1-ubi9","9.1-ubuntu-jdk11","9.1-jdk11","9.14.1-ubi9-jdk17","9.14.1-ubi9","9.1.1-ubuntu-jdk11","9.1.1-jdk11","9.1.1","9.9.0-ubuntu-jdk11","9.1","9.9.0-ubi9-jdk17","9.9.0-ubi9","9.9.0-jdk11","9.9.0","9.7.0-ubuntu-jdk11","9.7.0-jdk11","9.7.0","9.4.6-ubuntu-jdk11","9.4.6-jdk11","9.4.6","9.4.1-ubuntu-jdk11","9.4.1-jdk11","9.4.15-ubuntu-jdk11","9.4.15-jdk11","9.4.15","9.4.1","9.3.2-ubuntu-jdk11","9.3.2-jdk11","9.3.2","9.12.1-ubuntu-jdk11","9.12.1-jdk11","9.12.1","9.11.1-ubuntu-jdk11","9.11.1-jdk11","9.11.1","9.8.1-ubi9-jdk17","9.8.1-ubi9","9.7.1-ubi9-jdk17","9.7.1-ubi9","9.4.10-ubuntu-jdk11","9.4.10-jdk11","9.4.10","9.13.0-ubuntu-jdk11","9.13.0-ubi9-jdk17","9.13.0-ubi9","9.13.0-jdk11","9.13.0","9.11.3-ubi9-jdk17","9.11.3-ubi9","9.10.2-ubi9-jdk17","9.10.2-ubi9","9.9.1-ubuntu-jdk11","9.9.1-ubi9-jdk17","9.9.1-ubi9","9.9.1-jdk11","9.9.1","9.7.1-ubuntu-jdk11","9.7.1-jdk11","9.7.1","9.4.7-ubuntu-jdk11","9.4.7-jdk11","9.4.7","9.4.16-ubuntu-jdk11","9.4.16-jdk11","9.4.16","9.3-ubuntu-jdk11","9.3-jdk11","9.3.3-ubuntu-jdk11","9.3.3-jdk11","9.3.3","9.3","9.2.0-ubuntu-jdk11","9.2.0-jdk11","9.2.0","9.12.4-ubi9-jdk17","9.12.4-ubi9","9.12.1-ubi9-jdk17","9.12.1-ubi9","9.11.2-ubuntu-jdk11","9.11.2-jdk11","9.11.2","ubi9-jdk17","ubi9","9.8.2-ubi9-jdk17","9.8.2-ubi9","9.7.2-ubi9-jdk17","9.7.2-ubi9","9.6.0-ubi9-jdk17","9.6.0-ubi9","9.4.8-ubuntu-jdk11","9.4.8-jdk11","9.4.8","9.4.3-ubuntu-jdk11","9.4.3-jdk11","9.4.3","9.4.11-ubuntu-jdk11","9.4.11-jdk11","9.4.11","9.15.0-ubi9-jdk17","9.15.0-ubi9","9.13-ubuntu-jdk11","9.13-jdk11","9.13.1-ubuntu-jdk11","9.13.1-ubi9-jdk17","9.13.1-ubi9","9.13.1-jdk11","9.13.1","9.13","9.12.2-ubuntu-jdk11","9.12.2-jdk11","9.12.2","9.9-ubuntu-jdk11","9.9-jdk11","9.9.2-ubuntu-jdk11","9.9.2-ubi9-jdk17","9.9.2-ubi9","9.9.2-jdk11","9.9.2","9.9","9.7-ubuntu-jdk11","9.7-jdk11","9.7.2-ubuntu-jdk11","9.7.2-jdk11","9.7.2","9.7","9.4.17-ubuntu-jdk11","9.4.17-jdk11","9.4.17","9.2-ubuntu-jdk11","9.2-jdk11","9.2.1-ubuntu-jdk11","9.2.1-jdk11","9.2.1","9.2","9.12.5-ubi9-jdk17","9.12.5-ubi9","9.11-ubuntu-jdk11","9.11-jdk11","9.11.3-ubuntu-jdk11","9.11.3-jdk11","9.11.3","9.11.1-ubi9-jdk17","9.11.1-ubi9","9.11","9.10.0-ubuntu-jdk11","9.10.0-ubi9-jdk17","9.10.0-ubi9","9.10.0-jdk11","9.10.0","9.4.9-ubuntu-jdk11","9.4.9-jdk11","9.4.9","9.4.4-ubuntu-jdk11","9.4.4-jdk11","9.4.4","9.4.12-ubuntu-jdk11","9.4.12-jdk11","9.4.12","9.12.3-ubuntu-jdk11","9.12.3-jdk11","9.12.3","9.12.2-ubi9-jdk17","9.12.2-ubi9","9.4.18-ubuntu-jdk11","9.4.18-jdk11","9.4.18","9.5.0-ubuntu-jdk11","9.5.0-jdk11","9.5.0","9.4.5-ubuntu-jdk11","9.4.5-jdk11","9.4.5","9.4.19-ubuntu-jdk11","9.4.19-jdk11","9.4.19","9.4.14-ubuntu-jdk11","9.4.14-jdk11","9.4.14","9.4.0-ubuntu-jdk11","9.4.0-jdk11","9.4.0","9.12.6-ubi9-jdk17","9.12.6-ubi9","9.12.4-ubuntu-jdk11","9.12.4-jdk11","9.12.4","9.12.3-ubi9-jdk17","9.12.3-ubi9","9.12.0-ubi9-jdk17","9.12.0-ubi9","9.11.2-ubi9-jdk17","9.11.2-ubi9","9.11.0-ubi9-jdk17","9.11.0-ubi9","9.10.1-ubuntu-jdk11","9.10.1-ubi9-jdk17","9.10.1-ubi9","9.10.1-jdk11","9.10.1","10.0.0-EAP02-ubi9-jdk17","10.0.0-EAP02","10.0.0-EAP02-ubi9","10.0.0-EAP02-ubuntu-jdk11","10.0.0-EAP02-jdk11","9.15.0-RC","9.15.0-RC-jdk11","9.15.0-RC-ubuntu-jdk11","9.15.0-RC-ubi9","9.15.0-RC-ubi9-jdk17","9.15.0-EAP02-ubi9-jdk17","9.15.0-EAP02-ubi9","9.15.0-EAP02-jdk11","9.15.0-EAP02-ubuntu-jdk11","9.15.0-EAP02","8.22.3-ubuntu-jdk11","8.22.3-jdk11","8.22.3","8.22.4-ubuntu-jdk11","8.22.4-jdk11","8.22.4","8.22.5-ubuntu-jdk11","8.22.5","8.22.5-jdk11","8.16-ubuntu-jdk11","8.16-jdk11","8.16.2-ubuntu-jdk11","8.16.2-jdk11","8.16.2","8.16","8-ubuntu-jdk11","8-jdk11","8.22-ubuntu-jdk11","8.22-jdk11","8.22.6-ubuntu-jdk11","8.22.6-jdk11","8.22.6","8.22","8","8.17.0-ubuntu-jdk11","8.17.0-jdk11","8.17.0","8.20.21-ubuntu-jdk11","8.20.21-jdk11","8.20.21","8.20.4-ubuntu-jdk11","8.20.4-jdk11","8.20.4","8.17-ubuntu-jdk11","8.17-jdk11","8.17.1-ubuntu-jdk11","8.17.1-jdk11","8.17.1","8.17","8.20.22-ubuntu-jdk11","8.20.22-jdk11","8.20.22","8.13.5-ubuntu-jdk11","8.13.5-jdk11","8.13.5","8.20.5-ubuntu-jdk11","8.20.5-jdk11","8.20.5","8.13.19-ubuntu-jdk11","8.13.19-jdk11","8.13.19","8.13.6-jdk11","8.20.23-ubuntu-jdk11","8.20.23-jdk11","8.20.23","8.20.11-ubuntu-jdk11","8.20.11-jdk11","8.20.11","8.13.6-ubuntu-jdk11","8.13.6","8.13.2-ubuntu-jdk11","8.13.2-jdk11","8.13.2","8.20.6-ubuntu-jdk11","8.20.6-jdk11","8.20.6","8.18.0-ubuntu-jdk11","8.18.0-jdk11","8.18.0","8.13.0-ubuntu-jdk11","8.13.0-jdk11","8.13.0","8.20.24-ubuntu-jdk11","8.20.24-jdk11","8.20.24","8.20.12-ubuntu-jdk11","8.20.12-jdk11","8.20.12","8.13.7-ubuntu-jdk11","8.13.7-jdk11","8.20.7-ubuntu-jdk11","8.13.7","8.20.7-jdk11","8.20.7","8.18.1-ubuntu-jdk11","8.18.1-jdk11","8.18.1","8.13.20-ubuntu-jdk11","8.13.20-jdk11","8.13.20","8.13.1-ubuntu-jdk11","8.13.1-jdk11","8.13.1","8.13.8-ubuntu-jdk11","8.13.8-jdk11","8.13.8","8.20.25-ubuntu-jdk11","8.20.25-jdk11","8.20.25","8.20.13-ubuntu-jdk11","8.20.13-jdk11","8.20.13","8.13.21-ubuntu-jdk11","8.13.21-jdk11","8.13.21","8.13.10-ubuntu-jdk11","8.13.10-jdk11","8.20.8-ubuntu-jdk11","8.13.10","8.20.8-jdk11","8.20.8","8.18-ubuntu-jdk11","8.18-jdk11","8.18.2-ubuntu-jdk11","8.18.2-jdk11","8.18.2","8.18","8.13.9-ubuntu-jdk11","8.13.9-jdk11","8.13.9","8.20.26-ubuntu-jdk11","8.20.26-jdk11","8.20.26","8.20.14-ubuntu-jdk11","8.20.14-jdk11","8.20.14","8.13.22-ubuntu-jdk11","8.13.22-jdk11","8.13.22","8.20.9-ubuntu-jdk11","8.20.9-jdk11","8.20.9","8.14.0-ubuntu-jdk11","8.14.0-jdk11","8.14.0","8.13.11-ubuntu-jdk11","8.13.11-jdk11","8.13.11","8.20.15-ubuntu-jdk11","8.20.15-jdk11","8.20.15","8.13.24-ubuntu-jdk11","8.13.24-jdk11","8.13.24","8.20.27-ubuntu-jdk11","8.20.27-jdk11","8.20.27","8.19.0-ubuntu-jdk11","8.19.0-jdk11","8.19.0","8.14-ubuntu-jdk11","8.14-jdk11","8.14.1-ubuntu-jdk11","8.14.1-jdk11","8.14.1","8.14","8.13.12-ubuntu-jdk11","8.13.12-jdk11","8.13.12","8.21.0-ubuntu-jdk11","8.21.0-jdk11","8.21.0","8.20.28-ubuntu-jdk11","8.20.28-jdk11","8.20.28","8.20.16-ubuntu-jdk11","8.20.16-jdk11","8.20.16","8.13.25-ubuntu-jdk11","8.13.25-jdk11","8.13.25","8.13.13-ubuntu-jdk11","8.13.13-jdk11","8.13.13","8.21-ubuntu-jdk11","8.21-jdk11","8.21.1-ubuntu-jdk11","8.21.1-jdk11","8.21.1","8.21","8.19-ubuntu-jdk11","8.19-jdk11","8.19.1-ubuntu-jdk11","8.19.1-jdk11","8.19.1","8.19","8.20.17-ubuntu-jdk11","8.20.17-jdk11","8.20.17","8.15.0-ubuntu-jdk11","8.15.0-jdk11","8.15.0","8.13.26-ubuntu-jdk11","8.13.26-jdk11","8.13.26","8.13.14-ubuntu-jdk11","8.13.14-jdk11","8.20.29-ubuntu-jdk11","8.20.29-jdk11","8.20.29","8.13.14","8.13-ubuntu-jdk11","8.13-jdk11","8.13.27-ubuntu-jdk11","8.13.27-jdk11","8.13.27","8.13","8.20.3-ubuntu-jdk11","8.20.3-jdk11","8.20.3","8.20.18-ubuntu-jdk11","8.20.18-jdk11","8.20.18","8.15-ubuntu-jdk11","8.15-jdk11","8.15.1-ubuntu-jdk11","8.15.1-jdk11","8.15.1","8.15","8.13.15-ubuntu-jdk11","8.13.15-jdk11","8.13.15","8.22.0-ubuntu-jdk11","8.22.0-jdk11","8.22.0","8.20.19-ubuntu-jdk11","8.20.19-jdk11","8.20.19","8.20.0-ubuntu-jdk11","8.20.0-jdk11","8.20.0","8.13.16-ubuntu-jdk11","8.13.16-jdk11","8.20-ubuntu-jdk11","8.13.16","8.20-jdk11","8.20.30-ubuntu-jdk11","8.20.30-jdk11","8.20.30","8.20","8.22.1-ubuntu-jdk11","8.22.1-jdk11","8.22.1","8.20.2-ubuntu-jdk11","8.20.2-jdk11","8.20.2","8.20.1-ubuntu-jdk11","8.20.1-jdk11","8.20.1","8.13.17-ubuntu-jdk11","8.13.17-jdk11","8.13.17","8.16.0-ubuntu-jdk11","8.16.0-jdk11","8.16.0","8.13.3-ubuntu-jdk11","8.13.3-jdk11","8.13.3","8.22.2-ubuntu-jdk11","8.22.2-jdk11","8.22.2","8.20.20-ubuntu-jdk11","8.20.20-jdk11","8.20.20","8.20.10-ubuntu-jdk11","8.20.10-jdk11","8.20.10","8.13.4-ubuntu-jdk11","8.13.4-jdk11","8.13.4","8.13.18-ubuntu-jdk11","8.13.18-jdk11","8.13.18","8.16.1-ubuntu-jdk11","8.16.1-jdk11","8.16.1","9.15.0-EAP01-ubuntu-jdk11","9.15.0-EAP01","9.15.0-EAP01-jdk11","10.0.0-EAP01-jdk11","10.0.0-EAP01-ubuntu-jdk11","10.0.0-EAP01","9.14.0-RC-ubuntu-jdk11","9.14.0-RC-jdk11","9.14.0-RC","9.14.0-EAP02","9.14.0-EAP02-ubuntu-jdk11","9.14.0-EAP02-jdk11","9.14.0-EAP01-ubuntu-jdk11","9.14.0-EAP01-jdk11","9.14.0-EAP01","9.13.0-EAP02","9.13.0-EAP02-ubuntu-jdk11","9.13.0-EAP02-jdk11","9.4.13-jdk11","9.4.13","9.4.13-ubuntu-jdk11","9.13.0-EAP01-ubuntu-jdk11","9.13.0-EAP01-jdk11","9.13.0-EAP01","8.11.0-ubuntu-jdk11","8.11.0-jdk11","8.11.0","8.11-ubuntu-jdk11","8.11-jdk11","8.11.1-ubuntu-jdk11","8.11.1-jdk11","8.11.1","8.11","8.12.0-ubuntu-jdk11","8.12.0-jdk11","8.12.0","8.12.1-ubuntu-jdk11","8.12.1-jdk11","8.12.1","8.12.2-ubuntu-jdk11","8.12.2-jdk11","8.12.2","8.12-ubuntu-jdk11","8.12-jdk11","8.12.3-ubuntu-jdk11","8.12.3-jdk11","8.12.3","8.12","9.12.0-EAP01-ubuntu-jdk11","9.12.0-EAP01-jdk11","9.12.0-EAP01","9.11.0-RC-jdk11","9.11.0-RC-ubuntu-jdk11","9.11.0-RC","9.11.0-EAP02-ubuntu-jdk11","9.11.0-EAP02-jdk11","9.11.0-EAP02","9.11.0-EAP01-ubuntu-jdk11","9.11.0-EAP01-jdk11","9.11.0-EAP01","9.10.0-RC-ubuntu-jdk11","9.10.0-RC-jdk11","9.10.0-RC","9.10.0-EAP02-ubuntu-jdk11","9.10.0-EAP02-jdk11","9.10.0-EAP02","9.10.0-EAP01-ubuntu-jdk11","9.10.0-EAP01","9.10.0-EAP01-jdk11","9.9.0-RC02-jdk11","9.9.0-RC02-ubuntu-jdk11","9.9.0-RC02","9.9.0-RC01-jdk11","9.9.0-RC01-ubuntu-jdk11","9.9.0-RC01","9.9.0-EAP02-ubuntu-jdk11","9.9.0-EAP02-jdk11","9.9.0-EAP02","9.9.0-EAP01-jdk11","9.9.0-EAP01-ubuntu-jdk11","9.9.0-EAP01","9.8.0-RC01-jdk11","9.8.0-RC01-ubuntu-jdk11","9.8.0-RC01","9.8.0-EAP02","eap-jdk11","eap","9.8.0-EAP02-ubuntu-jdk11","eap-ubuntu-jdk11","9.8.0-EAP02-jdk11","9.8.0-EAP01","9.8.0-EAP01-jdk11","9.8.0-EAP01-ubuntu-jdk11","9.7.0-RC01-jdk11","9.7.0-RC01","9.7.0-RC01-ubuntu-jdk11","9.7.0-EAP02","9.7.0-EAP02-ubuntu-jdk11","9.7.0-EAP02-jdk11","9.7.0-EAP01-ubuntu-jdk11","9.7.0-EAP01-jdk11","9.7.0-EAP01","9.6.0-RC01","9.6.0-RC01-ubuntu-jdk11","9.6.0-EAP02-ubuntu-jdk11","9.6.0-EAP02","9.6.0-EAP02-jdk11","9.6.0-EAP01","9.6.0-EAP01-jdk11","9.6.0-EAP01-ubuntu-jdk11","9.5.0-RC01","9.5.0-RC01-ubuntu-jdk11","9.5.0-RC01-jdk11","9.5.0-EAP03-ubuntu-jdk11","9.5.0-EAP03-jdk11","9.5.0-EAP03","9.5.0-EAP02-ubuntu-jdk11","9.5.0-EAP02","9.5.0-EAP02-jdk11","9.4.0-RC02-jdk11","9.4.0-RC02","9.4.0-RC02-ubuntu-jdk11","9.4.0-RC01-ubuntu-jdk11","9.4.0-RC01-jdk11","9.4.0-RC01","9.4.0-EAP01","9.4.0-EAP01-ubuntu-jdk11","9.4.0-EAP01-jdk11","9.5.0-EAP01-ubuntu-jdk11","9.5.0-EAP01","9.5.0-EAP01-jdk11","9.3.0-RC01","9.3.0-RC01-ubuntu-jdk11","9.3.0-RC01-jdk11","9.3.0-EAP02-jdk11","9.3.0-EAP02","9.3.0-EAP02-ubuntu-jdk11","9.3.0-EAP01-jdk11","9.3.0-EAP01-ubuntu-jdk11","9.3.0-EAP01","9.2.0-RC01-ubuntu-jdk11","9.2.0-RC01-jdk11","9.2.0-RC01","9.2.0-EAP03-jdk11","9.2.0-EAP03-ubuntu-jdk11","9.2.0-EAP03","9.2.0-EAP02-ubuntu-jdk11","9.2.0-EAP02-jdk11","9.2.0-EAP02","9.2.0-EAP01-jdk11","9.2.0-EAP01-ubuntu-jdk11","9.2.0-EAP01","9.1.0-RC01-ubuntu-jdk11","9.1.0-RC01-jdk11","9.1.0-RC01","9.1.0-EAP02-ubuntu-jdk11","9.1.0-EAP02-jdk11","9.1.0-EAP02","9.1.0-EAP01-jdk11","9.1.0-EAP01-ubuntu-jdk11","9.1.0-EAP01","8.8.0-ubuntu-jdk11","8.8.0-jdk11","8.8.0","8.9-ubuntu-jdk11","8.9-jdk11","8.9.1-ubuntu-jdk11","8.9.1-jdk11","8.9.1","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.9.0","8.9","8.8-ubuntu-jdk11","8.8-jdk11","8.8.1-ubuntu-jdk11","8.8.1-jdk11","8.8.1","8.8","8.10-ubuntu-jdk11","8.10-jdk11","8.10.1-ubuntu-jdk11","8.10.1-jdk11","8.10.1","8.10.0-ubuntu-jdk11","8.10.0-jdk11","8.10.0","8.10","9.0.0-RC02-jdk11","9.0.0-RC02-ubuntu-jdk11","9.0.0-RC02","9.0.0-RC01-jdk11","9.0.0-RC01-ubuntu-jdk11","9.0.0-RC01","9.0.0-EAP04-ubuntu-jdk11","9.0.0-EAP04-jdk11","9.0.0-EAP04","9.0.0-EAP03-ubuntu-jdk11","9.0.0-EAP03-jdk11","9.0.0-EAP03","9.0.0-EAP02-ubuntu-jdk11","9.0.0-EAP02-jdk11","9.0.0-EAP02","9.0.0-EAP01-ubuntu-jdk11","9.0.0-EAP01-jdk11","9.0.0-EAP01","8.22.0-RC01-ubuntu-jdk11","8.22.0-RC01-jdk11","8.22.0-RC01","8.22.0-EAP02-ubuntu-jdk11","8.22.0-EAP02-jdk11","8.22.0-EAP02","8.22.0-EAP01-ubuntu-jdk11","8.22.0-EAP01-jdk11","8.22.0-EAP01","8.21.0-RC01-ubuntu-jdk11","8.21.0-RC01-jdk11","8.21.0-RC01","8.21.0-EAP02-ubuntu-jdk11","8.21.0-EAP02-jdk11","8.21.0-EAP02","8.21.0-EAP01-ubuntu-jdk11","8.21.0-EAP01-jdk11","8.21.0-EAP01","8.20-ubuntu-jdk-11","8.20.7-ubuntu-jdk-11","8.20.0-RC01-ubuntu-jdk11","8.20.0-RC01-jdk11","8.20.0-RC01","8.20.0-EAP01-ubuntu-jdk11","8.20.0-EAP01-jdk11","8.20.0-EAP01","8.19.0-RC01-ubuntu-jdk11","8.19.0-RC01-jdk11","8.19.0-RC01","8.19.0-EAP02-ubuntu-jdk11","8.19.0-EAP02-jdk11","8.19.0-EAP02","8.19.0-EAP01-ubuntu-jdk-11","8.19.0-EAP01-jdk11","8.19.0-EAP01","8.18.0-RC01-ubuntu-jdk11","8.18.0-RC01-jdk11","8.18.0-RC01","8.18.0-EAP02-ubuntu-jdk-11","8.18.0-EAP02-jdk11","8.18.0-EAP02","8.18.0-EAP01-ubuntu-jdk11","8.18.0-EAP01-jdk11","8.18.0-EAP01","8.17.0-RC02-ubuntu-jdk11","8.17.0-RC02-jdk11","8.17.0-RC02","8.17.0-RC01-ubuntu-jdk11","8.17.0-RC01-jdk11","8.17.0-RC01","8.17.0-EAP02-ubuntu-jdk11","8.17.0-EAP02-jdk11","8.17.0-EAP02","8.17.0-EAP01-ubuntu-jdk11","8.17.0-EAP01-jdk11","8.17.0-EAP01","8.8.0-ubuntu-jdk-11","8.8.1-ubuntu-jdk-11","8.8-ubuntu-jdk-11","8.7-jdk11","8.7-ubuntu-jdk11","8.7","8.7.1-jdk11","8.7.1","8.7.1-ubuntu-jdk11","8.7.0-jdk11","8.7.0-ubuntu-jdk11","8.7.0","8.5.18","8.5.18-jdk11","8.5.18-ubuntu-jdk11","8.5.19-jdk11","8.5.19-ubuntu-jdk11","8.5-jdk11","8.5-ubuntu-jdk11","8.5","8.5.19","8.5.2-ubuntu-jdk11","8.5.2","8.5.2-jdk11","8.5.3","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.5.4-ubuntu-jdk11","8.5.4","8.5.4-jdk11","8.5.5-jdk11","8.5.5-ubuntu-jdk11","8.5.5","8.5.6-ubuntu-jdk11","8.5.6","8.5.6-jdk11","8.5.7-ubuntu-jdk11","8.5.7","8.5.12-ubuntu-jdk11","8.5.7-jdk11","8.5.12","8.5.12-jdk11","8.5.8","8.5.8-jdk11","8.5.8-ubuntu-jdk11","8.5.13-jdk11","8.5.13","8.5.13-ubuntu-jdk11","8.5.9-jdk11","8.5.9","8.5.9-ubuntu-jdk11","8.5.14-jdk11","8.5.0-ubuntu-jdk11","8.5.14","8.5.0","8.5.14-ubuntu-jdk11","8.5.0-jdk11","8.6.0-jdk11","8.6.0","8.6.0-ubuntu-jdk11","8.5.15-jdk11","8.5.1","8.5.15","8.5.1-ubuntu-jdk11","8.5.15-ubuntu-jdk11","8.5.1-jdk11","8.6","8.6-ubuntu-jdk11","8.6.1-ubuntu-jdk11","8.6.1","8.5.16","8.5.10-ubuntu-jdk11","8.6.1-jdk11","8.5.16-ubuntu-jdk11","8.5.10","8.6-jdk11","8.5.16-jdk11","8.5.10-jdk11","8.5.17","8.5.11","8.5.17-ubuntu-jdk11","8.5.11-jdk11","8.5.17-jdk11","8.5.11-ubuntu-jdk11","8.14.0-jdk8","8.14.0-ubuntu-jdk8","8.14.0-ubuntu","8.14.1-jdk8","8.14-jdk8","8.14.1-ubuntu","8.14.1-ubuntu-jdk8","8.14-ubuntu","8.14-ubuntu-jdk8","8.15.0-ubuntu","8.15.0-jdk8","8.15.0-ubuntu-jdk8","8.15-ubuntu","8.15.1-ubuntu","8.15-ubuntu-jdk8","8.15.1-ubuntu-jdk8","8.15.1-jdk8","8.15-jdk8","8.16.0-ubuntu","8.16.0-ubuntu-jdk8","8.16.0-jdk8","8.18.1-jdk8","8.18.1-ubuntu","8.7.1-ubuntu-jdk8","8.18.1-ubuntu-jdk8","8.7-jdk8","8.16.1-jdk8","8.16.1-ubuntu-jdk8","8.7.1-ubuntu","8.16.1-ubuntu","8.7.1-jdk8","8.7-ubuntu","8.18.2-ubuntu","8.7-ubuntu-jdk8","8.18-ubuntu","8.18-jdk8","8.16-ubuntu","8.18.2-jdk8","8.18-ubuntu-jdk8","8.16-ubuntu-jdk8","8.10.0-ubuntu-jdk8","8.18.2-ubuntu-jdk8","8.10.0-ubuntu","8.16.2-jdk8","8.13.1-ubuntu","8.10.0-jdk8","8.16-jdk8","8.13.1-ubuntu-jdk8","8.16.2-ubuntu","8.12.0-ubuntu-jdk8","8.12.0-ubuntu","8.16.2-ubuntu-jdk8","8.8.0-ubuntu","8.8.0-jdk8","8.5.8-jdk8","8.5.15-ubuntu","8.10.1-ubuntu","8.5.8-ubuntu-jdk8","8.5.15-ubuntu-jdk8","8.8.0-ubuntu-jdk8","8.12.0-jdk8","8.5.15-jdk8","8.13.1-jdk8","8.5.8-ubuntu","8.10.1-ubuntu-jdk8","8.19-ubuntu-jdk8","8.10-ubuntu-jdk8","8.5.16-jdk8","8.12.1-ubuntu-jdk8","8.8.1-ubuntu-jdk8","8.10.1-jdk8","8.13-jdk8","8.12.1-ubuntu","8.8-ubuntu","8.10-ubuntu","8.13.10-jdk8","8.5.16-ubuntu-jdk8","8.5.9-ubuntu-jdk8","8.12.1-jdk8","8.8.1-ubuntu","8.13.10-ubuntu-jdk8","8.19.0-ubuntu-jdk8","8.5.16-ubuntu","8.5.9-jdk8","8.17.0-ubuntu-jdk8","8.10-jdk8","8.13-ubuntu","8-ubuntu-jdk8","8.5.9-ubuntu","8.8-jdk8","jdk8","8.8.1-jdk8","8.19-ubuntu","8.17.0-ubuntu","8.8-ubuntu-jdk8","8.5.3-ubuntu-jdk8","8.13.5-ubuntu-jdk8","8.19-jdk8","8.5.10-jdk8","8.17.0-jdk8","8.5.17-jdk8","8.5.3-jdk8","8.13.5-ubuntu","8.12.2-ubuntu","8.5.10-ubuntu","8.19.0-ubuntu","8.13-ubuntu-jdk8","8.6.0-ubuntu-jdk8","8.5.17-ubuntu","8.13.5-jdk8","8.12.2-ubuntu-jdk8","8.5.10-ubuntu-jdk8","8.6.0-ubuntu","8.5.17-ubuntu-jdk8","8.5.3-ubuntu","ubuntu-jdk8","8.12.2-jdk8","8.19.0-jdk8","8.17-ubuntu","8.6.0-jdk8","8-jdk8","8.17.1-jdk8","8.13.6-jdk8","ubuntu","8.11.0-ubuntu","8.5.11-jdk8","8-ubuntu","8.5.4-jdk8","8.5.18-ubuntu-jdk8","8.17.1-ubuntu","8.12.3-ubuntu","8.11.0-jdk8","8.13.6-ubuntu-jdk8","8.5.11-ubuntu","8.5.4-ubuntu","8.17.1-ubuntu-jdk8","8.6.1-jdk8","8.5.18-jdk8","8.13.6-ubuntu","8.5.11-ubuntu-jdk8","8.9.0-ubuntu-jdk8","8.5.4-ubuntu-jdk8","8.12.3-jdk8","8.17-jdk8","8.5-ubuntu-jdk8","8.11.0-ubuntu-jdk8","8.9.0-jdk8","8.12-jdk8","8.5-jdk8","8.9.0-ubuntu","8.12-ubuntu-jdk8","8.6.1-ubuntu","8.17-ubuntu-jdk8","8.12.3-ubuntu-jdk8","8.6.1-ubuntu-jdk8","8.5-ubuntu","8.13.7-jdk8","8.13.2-ubuntu-jdk8","8.5.5-jdk8","8.6-ubuntu","8.13.7-ubuntu-jdk8","8.11-jdk8","8.13.2-ubuntu","8.5.5-ubuntu-jdk8","8.6-jdk8","8.12-ubuntu","8.5.12-ubuntu-jdk8","8.11-ubuntu-jdk8","8.5.18-ubuntu","8.13.7-ubuntu","8.5.5-ubuntu","8.6-ubuntu-jdk8","8.5.12-ubuntu","8.13.2-jdk8","8.5.12-jdk8","8.11.1-jdk8","8.9-ubuntu-jdk8","8.11-ubuntu","8.9.1-jdk8","8.11.1-ubuntu","8.9.1-ubuntu-jdk8","8.11.1-ubuntu-jdk8","8.9-jdk8","8.13.3-ubuntu","8.13.8-ubuntu-jdk8","8.5.6-jdk8","8.9-ubuntu","8.5.0-jdk8","8.5.13-jdk8","8.5.0-ubuntu-jdk8","8.13.3-jdk8","8.5.6-ubuntu-jdk8","8.13.8-ubuntu","8.5.13-ubuntu","8.9.1-ubuntu","8.5.0-ubuntu","8.13.3-ubuntu-jdk8","8.5.6-ubuntu","8.13.8-jdk8","8.5.13-ubuntu-jdk8","8.18.0-jdk8","8.13.0-jdk8","8.5.7-jdk8","8.5.2-jdk8","8.13.9-ubuntu-jdk8","8.7.0-ubuntu","8.5.14-ubuntu","8.18.0-ubuntu","8.5.1-ubuntu-jdk8","8.5.7-ubuntu","8.5.2-ubuntu-jdk8","8.13.4-ubuntu-jdk8","8.13.0-ubuntu-jdk8","8.7.0-jdk8","8.18.0-ubuntu-jdk8","8.5.2-ubuntu","8.13.4-jdk8","8.13.0-ubuntu","8.13.9-jdk8","8.5.1-ubuntu","8.5.14-jdk8","8.5.7-ubuntu-jdk8","8.13.4-ubuntu","8.13.9-ubuntu","8.5.1-jdk8","8.5.14-ubuntu-jdk8","8.7.0-ubuntu-jdk8","8.13.10-ubuntu","8.3.5-jdk8","8.3.5","8.3.5-ubuntu-jdk8","8.3-ubuntu","8.3-jdk8","8.3","8.3.5-ubuntu","7.13.13-ubuntu","8.3-ubuntu-jdk8","7.13.13-jdk8","7.13.4-ubuntu-jdk8","7.13.13-ubuntu-jdk8","7.13.4","7.13.13","7.13.4-ubuntu","7.13.4-jdk8","8.2.4","8.2.4-ubuntu-jdk8","8.2.4-ubuntu","8.1.3-jdk8","7.13.14-ubuntu-jdk8","8.2.4-jdk8","7.13.14","8.1.3-ubuntu","7.13.5-jdk8","7.13.14-jdk8","8.1-ubuntu","7.13.5-ubuntu-jdk8","7.13.14-ubuntu","8.4.0","7.13.5-ubuntu","8.4.0-jdk8","8.2.5-ubuntu","7.13.5","8.4.0-ubuntu-jdk8","8.2.5","8.4.0-ubuntu","8.2.5-ubuntu-jdk8","8.2.5-jdk8","7.13.15","7.13.15-jdk8","7.13.6-ubuntu","7.13.15-ubuntu","7.13.6-ubuntu-jdk8","7.13.15-ubuntu-jdk8","8.4.1-ubuntu-jdk8","7.13.6","8.4.1-ubuntu","8.2","7.13.6-jdk8","8.4.1-jdk8","8.2-jdk8","8.4.1","8.2-ubuntu-jdk8","8.2-ubuntu","7.13.16-ubuntu-jdk8","8.2.6-ubuntu","7.13.16-jdk8","8.2.6-jdk8","7.13.8-ubuntu","7.13.16","8.2.6-ubuntu-jdk8","7.13.16-ubuntu","8.4.2","7.13.8-jdk8","8.2.6","8.4.2-jdk8","7.13.8-ubuntu-jdk8","8.4.2-ubuntu","7.13.8","8.4.2-ubuntu-jdk8","7.13.17-ubuntu-jdk8","8.4.1-ubuntu-jdk11","8.2.2-ubuntu-jdk11","7.13.17-ubuntu","8.4.1-jdk11","8.3.1-ubuntu-jdk11","8.2.2-jdk11","7.13.17","7.13.9-ubuntu","8.3.1-jdk11","7.13.17-jdk8","8.4.3-ubuntu","7.13.9","8.4.3-jdk8","7.13.9-ubuntu-jdk8","8.4.3","7.13.9-jdk8","8.2.3-jdk11","8.4.2-ubuntu-jdk11","8.4-jdk8","8.2.3-ubuntu-jdk11","8.3.2-ubuntu-jdk11","7.13-jdk8","8.4.3-ubuntu-jdk8","8.4.2-jdk11","8.3.2-jdk11","7.13-ubuntu-jdk8","8.4-ubuntu-jdk8","8.3.0-ubuntu-jdk8","7-jdk8","8.4-ubuntu","8.0.0-jdk8","8.3.0","7.13.18","8.4","8.0.0-ubuntu","8.3.0-ubuntu","7.13","8.2.4-ubuntu-jdk11","8.0.0-ubuntu-jdk8","8.4-jdk11","8.3.0-jdk8","7.13-ubuntu","8.2.4-jdk11","8.3.3-ubuntu-jdk11","8.0.0","8.4.3-jdk11","7-ubuntu","8.3.3-jdk11","8.4-ubuntu-jdk11","7","7-ubuntu-jdk8","8.4.3-ubuntu-jdk11","7.13.18-jdk8","8.3.1-jdk8","8.0.2","8.2.5-jdk11","7.13.18-ubuntu-jdk8","8.3.1","8.2.0","8.0.2-ubuntu","8.3.4-ubuntu-jdk11","8.2.5-ubuntu-jdk11","8.3.1-ubuntu-jdk8","7.13.18-ubuntu","8.2.0-ubuntu-jdk8","8.0.2-jdk8","8.3.4-jdk11","8.3.1-ubuntu","8.2.0-ubuntu","8.0.2-ubuntu-jdk8","8.2.0-jdk8","8.2-ubuntu-jdk11","8.3-jdk11","8.2.6-jdk11","8.3.2-ubuntu-jdk8","8.0-jdk8","8.3.5-jdk11","8.2.1","8.3.2","8.2-jdk11","8.0.3-jdk8","8.3.5-ubuntu-jdk11","8.2.1-ubuntu-jdk8","8.3.2-jdk8","8.2.6-ubuntu-jdk11","8.0-ubuntu","8.3-ubuntu-jdk11","8.2.1-ubuntu","8.3.2-ubuntu","8.0","8.2.1-jdk8","8.0.3","8.0.3-ubuntu","8.0-ubuntu-jdk8","7.13.2-jdk8","8.0.3-ubuntu-jdk8","8.3.3-jdk8","7.13.2-ubuntu-jdk8","8.2.2-ubuntu","8.3.3-ubuntu","7.13.2","8.2.2-ubuntu-jdk8","8.3.3","7.13.2-ubuntu","8.2.2-jdk8","8.2.0-ubuntu-jdk11","8.3.3-ubuntu-jdk8","8.2.2","8.2.0-jdk11","7.13.3-ubuntu","8.3.0-ubuntu-jdk11","7.13.3-ubuntu-jdk8","8.3.4-ubuntu-jdk8","8.2.3-ubuntu-jdk8","8.4.0-ubuntu-jdk11","8.2.1-ubuntu-jdk11","7.13.3-jdk8","8.3.0-jdk11","8.3.4-jdk8","8.2.3","8.4.0-jdk11","8.2.1-jdk11","7.13.3","8.3.4-ubuntu","8.2.3-jdk8","8.3.4","8.2.3-ubuntu","8.19.0-RC01-ubuntu-jdk8","8.19.0-RC01-jdk8","8.19.0-RC01-ubuntu","7.13.0-ubuntu-jdk8","7.13.0","7.13.0-ubuntu","7.13.0-jdk8","7.13.1-ubuntu-jdk8","7.13.1-jdk8","7.13.1-ubuntu","7.13.1","7.13.11-ubuntu","7.13.11","8.1.0-jdk8","8.1.0","7.13.11-ubuntu-jdk8","8.1.0-ubuntu-jdk8","7.13.11-jdk8","8.1.0-ubuntu","7.13.12-ubuntu","8.1.1-ubuntu","7.13.12","8.1.1-jdk8","7.13.12-jdk8","8.1.1","7.13.12-ubuntu-jdk8","8.1.1-ubuntu-jdk8","8.1.2-ubuntu-jdk8","8.1.2-ubuntu","8.1.2","8.1.2-jdk8","8.1.3-ubuntu-jdk8","8.1","8.1.3","8.1-jdk8","8.1-ubuntu-jdk8","eap-ubuntu","8.19.0-EAP02-ubuntu","eap-ubuntu-jdk8","8.19.0-EAP02-ubuntu-jdk8","eap-jdk8","8.19.0-EAP02-jdk8","8.19.0-EAP01-ubuntu","8.19.0-EAP01-jdk8","8.19.0-EAP01-ubuntu-jdk8","8.19.0-EAP01-ubuntu-jdk11","8.18.0-ssmith-perm-build-1","8.18.0-RC01-ubuntu","8.18.0-RC01-ubuntu-jdk8","8.18.0-RC01-jdk8","8.18.0-EAP02-ubuntu-jdk8","8.18.0-EAP02-jdk8","8.18.0-EAP02-ubuntu","8.18.0-EAP02-ubuntu-jdk11","8.18.0-EAP01-ubuntu","8.18.0-EAP01-jdk8","8.18.0-EAP01-ubuntu-jdk8","8.17.0-RC02-jdk8","8.17.0-RC02-ubuntu","8.17.0-RC02-ubuntu-jdk8","8.17.0-RC01-jdk8","8.17.0-RC01-ubuntu-jdk8","8.17.0-RC01-ubuntu","8.17.0-EAP02-ubuntu","8.17.0-EAP02-jdk8","8.17.0-EAP02-ubuntu-jdk8","8.17.0-EAP01-ubuntu","8.17.0-EAP01-ubuntu-jdk8","8.17.0-EAP01-jdk8","8.16.0-EAP02-ubuntu-jdk8","8.16.0-EAP02-jdk8","8.16.0-EAP02-ubuntu","8.16.0-EAP02","8.16.0-EAP02-jdk11","8.16.0-EAP02-ubuntu-jdk11","8.16.0-RC01-ubuntu-jdk11","8.16.0-RC01-jdk11","8.16.0-EAP03","8.16.0-EAP03-ubuntu-jdk11","8.16.0-EAP03-ubuntu-jdk8","8.16.0-EAP03-jdk11","8.16.0-EAP03-jdk8","8.16.0-EAP03-ubuntu","8.16.0-RC01-ubuntu-jdk8","8.16.0-RC01-ubuntu","8.16.0-RC01","8.16.0-RC01-jdk8","8.16.0-RC02-ubuntu-jdk11","8.16.0-RC02-jdk11","8.16.0-RC02","8.16.0-RC02-ubuntu","8.16.0-RC02-ubuntu-jdk8","8.16.0-RC02-jdk8","8.16.0-EAP01","8.16.0-EAP01-jdk8","8.16.0-EAP01-ubuntu","8.16.0-EAP01-ubuntu-jdk11","8.16.0-EAP01-jdk11","8.15.0-RC01","8.15.0-RC01-jdk8","8.15.0-RC01-ubuntu-jdk11","8.15.0-RC01-ubuntu","8.15.0-RC01-jdk11","8.15.0-EAP03-jdk11","8.15.0-EAP03-ubuntu-jdk11","8.15.0-EAP03-jdk8","8.15.0-EAP03","8.15.0-EAP03-ubuntu","8.15.0-EAP02-ubuntu","8.15.0-EAP02","8.15.0-EAP02-jdk8","8.15.0-EAP02-ubuntu-jdk11","8.15.0-EAP02-jdk11","8.15.0-EAP01-ubuntu-jdk11","8.15.0-EAP01-jdk11","8.15.0-EAP01","8.15.0-EAP01-ubuntu","8.15.0-EAP01-jdk8","8.14.0-RC01-jdk11","8.14.0-RC01-ubuntu-jdk11","8.14.0-RC01","8.14.0-RC01-ubuntu","8.14.0-RC01-jdk8","8.14.0-EAP01-ubuntu","8.14.0-EAP01","8.14.0-EAP01-jdk8","8.14.0-EAP01-ubuntu-jdk11","8.14.0-EAP01-jdk11","8.13.0-RC01","8.13.0-RC01-jdk11","8.13.0-RC01-ubuntu","8.13.0-RC01-ubuntu-jdk11","8.13.0-RC01-jdk8","8.13.0-EAP03-jdk8","8.13.0-EAP03","8.13.0-EAP03-ubuntu","8.13.0-EAP03-ubuntu-jdk11","8.13.0-EAP03-jdk11","8.13.0-EAP02","8.13.0-EAP02-ubuntu","8.13.0-EAP02-jdk8","8.13.0-EAP02-jdk11","8.13.0-EAP02-ubuntu-jdk11","8.12.0-RC02-jdk11","8.12.0-RC02-ubuntu-jdk11","8.12.0-RC02-jdk8","8.12.0-RC02-ubuntu","8.12.0-RC02","8.13.0-EAP01-jdk11","8.13.0-EAP01-ubuntu-jdk11","8.12.0-RC01-jdk11","8.12.0-RC01-ubuntu-jdk11","8.13.0-EAP01-jdk8","8.13.0-EAP01-ubuntu","8.13.0-EAP01","8.12.0-RC01-jdk8","8.12.0-RC01-ubuntu","8.12.0-RC01","8.12.0-EAP02-ubuntu-jdk11","8.12.0-EAP02-jdk11","8.12.0-EAP02","8.12.0-EAP02-ubuntu","8.12.0-EAP02-jdk8","8.12.0-EAP01-jdk11","8.12.0-EAP01-ubuntu-jdk11","8.12.0-EAP01","8.12.0-EAP01-jdk8","8.12.0-EAP01-ubuntu","8.5.6-RC02-jdk8","8.5.6-RC02-ubuntu","8.5.6-RC02","8.5.6-RC02-ubuntu-jdk11","8.5.6-RC02-jdk11","8.11.0-RC01","8.11.0-RC01-jdk8","8.11.0-RC01-ubuntu","8.11.0-RC01-jdk11","8.11.0-RC01-ubuntu-jdk11","8.11.0-EAP01-ubuntu-jdk11","8.11.0-EAP01-jdk11","8.11.0-EAP01-jdk8","8.11.0-EAP01-ubuntu","8.11.0-EAP01","8.10.0-RC01-ubuntu","8.10.0-RC01","8.10.0-RC01-jdk8","8.10.0-RC01-jdk11","8.10.0-RC01-ubuntu-jdk11","7.13.14-RC01","7.13.14-RC01-ubuntu","7.13.14-RC01-jdk8","8.10.0-EAP02-jdk8","8.10.0-EAP02","8.10.0-EAP02-ubuntu","8.10.0-EAP01-jdk8","8.10.0-EAP01","8.10.0-EAP01-ubuntu","8.10.0-EAP01-ubuntu-jdk11","7.13.15-RC01-ubuntu","8.10.0-EAP01-jdk11","7.13.15-RC01","7.13.15-RC01-jdk8","8.10.0-EAP02-ubuntu-jdk11","8.10.0-EAP02-jdk11","8.5.6-RC01-jdk11","8.5.6-RC01-ubuntu-jdk11","8.5.6-RC01","8.5.6-RC01-ubuntu","8.5.6-RC01-jdk8","8.5.5-RC01-jdk11","8.5.5-RC01-ubuntu-jdk11","8.5.5-RC01","8.5.5-RC01-jdk8","8.5.5-RC01-ubuntu","8.9.0-RC01-ubuntu","8.9.0-RC01-jdk8","8.9.0-RC01","8.9.0-RC01-jdk11","8.9.0-RC01-ubuntu-jdk11","8.9.0-EAP02-jdk11","8.9.0-EAP02-ubuntu-jdk11","8.9.0-EAP02-jdk8","8.9.0-EAP02-ubuntu","8.9.0-EAP02","8.9.0-EAP01-ubuntu-jdk11","8.9.0-EAP01-jdk11","8.9.0-EAP01-ubuntu","8.9.0-EAP01","8.9.0-EAP01-jdk8","8.8.0-RC02-ubuntu-jdk11","8.8.0-RC02-jdk11","8.8.0-RC02-jdk8","8.8.0-RC02-ubuntu","8.8.0-RC02"],"confluence":["8.7-ubuntu-jdk17","8.7-jdk17","8.7.2-ubuntu-jdk17","8.6.0-jdk17","8.7.2","8.6.0-ubuntu-jdk17","8.7.2-jdk17","8.6.0","8.7","8.5.5-ubuntu-jdk17","8.5.5-jdk17","8.8.0-ubuntu-jdk17","8.8.0-jdk17","8.5.5","8.7-jdk11","8.7-ubuntu-jdk11","8.8.0","8.7.2-ubuntu-jdk11","8.7.2-jdk11","8.1-jdk11","8.1-ubuntu-jdk11","8.1.4-ubuntu-jdk11","8.1.4-jdk11","8.1","8.1.4","7.19-ubuntu-jdk11","7.19-jdk11","7.19.21-ubuntu-jdk11","7.19.21","7.19.21-jdk11","8.8-ubuntu-jdk17","8.8.1-ubi9","8.8.1-ubi9-jdk17","8.8.0-ubuntu-jdk11","8.8.1-jdk17","8.8.0-jdk11","8.6.1","8.6.1-jdk17","8.6.1-ubuntu-jdk17","8.5.6-ubuntu-jdk17","8.5.6-jdk17","8.5.6","8.5.5-ubuntu-jdk11","8.5.1-ubuntu-jdk17","8.5.5-jdk11","8.5.1-jdk17","8.5.1","8.4.4-jdk17","8.4.4-ubuntu-jdk17","8.4.4","8.0.3-jdk11","8.0.3","8.0.3-ubuntu-jdk11","7.20.2-jdk11","7.20.2-ubuntu-jdk11","7.20.2","8.8-jdk17","8.8.1-ubuntu-jdk17","8.8.1","8.8","8.6.0-ubuntu-jdk11","8.0-jdk11","8.0.4-ubuntu-jdk11","8.3.3-ubuntu-jdk17","8.6.0-jdk11","8.0.4-jdk11","8.3.3","8.0","8.3.3-jdk17","7.19.7-ubuntu-jdk11","7.19.7-jdk11","8.8-ubuntu-jdk11","8.8-jdk11","8.8.1-ubuntu-jdk11","7.19.7","7.19.17-ubuntu-jdk11","8.8.1-jdk11","7.19.17-jdk11","7.19.17","8.7.2-ubi9-jdk17","8.6-ubuntu-jdk17","8.7.2-ubi9","8.6-jdk17","8.6.2-ubuntu-jdk17","8.6.2-ubi9-jdk17","8.6.2-ubi9","8.6","8.6.2-jdk17","8.6.2","8.5.7-ubuntu-jdk17","8.5.7","8.5.7-jdk17","8.5.6-ubuntu-jdk11","8.5.6-jdk11","8.5.2-ubuntu-jdk17","8.5.2-jdk17","8.4.5-ubuntu-jdk17","8.4-jdk17","8.5.2","8.4-ubuntu-jdk17","latest","8.4.5-jdk17","8.4.5","8.4.1-ubuntu-jdk17","8.4","8.4.1-jdk17","8.4.1","8.2.0-jdk11","8.2.0-ubuntu-jdk11","8.3.0","8.3.0-ubuntu-jdk17","8.3.0-jdk17","8.0.4","8.0-ubuntu-jdk11","7-jdk11","7-ubuntu-jdk11","7.20-ubuntu-jdk11","7.20-jdk11","7.20.3-jdk11","7.20.3-ubuntu-jdk11","7.20.3","7.20","8.6.1-ubuntu-jdk11","7","8.6.1-jdk11","8.5.1-jdk11","8.5.1-ubuntu-jdk11","8.4.4-ubuntu-jdk11","8.3-ubuntu-jdk17","8.4.4-jdk11","8.3-jdk17","8.3.4-ubuntu-jdk17","8.3.4","8.3.4-jdk17","8.3.3-ubuntu-jdk11","8.3.3-jdk11","8.3","7.19.3-ubuntu-jdk11","7.19.18-jdk11","7.19.3-jdk11","7.19.18-ubuntu-jdk11","7.19.3","7.19.18","7.19.12-ubuntu-jdk11","7.19.12-jdk11","7.19.12","ubuntu-jdk17","ubi9-jdk17","jdk17","ubi9","8-ubuntu-jdk17","8-jdk17","8.9-ubuntu-jdk17","8.9-jdk17","8.9.0-ubuntu-jdk17","8.9.0-ubi9","8.9.0-ubi9-jdk17","8.9.0","8.9.0-jdk17","8.9","8.5-ubuntu-jdk17","8.5-jdk17","8.5.8-ubuntu-jdk17","8","8.5","8.5.8","8.5.8-jdk17","7.19.8-jdk11","7.19.8-ubuntu-jdk11","7.19.8","7.19.0-ubuntu-jdk11","7.19.0-jdk11","7.19.0","8.5.8-ubi9-jdk17","8.5.7-ubuntu-jdk11","8.5.7-jdk11","8.5.8-ubi9","8.5.3-jdk17","8.5.3-ubuntu-jdk17","8.5.3","8.4.2-ubuntu-jdk17","8.4.2-jdk17","8.3.1-ubuntu-jdk17","8.4.2","8.3.1-jdk17","8.6-ubuntu-jdk11","8.3.1","8.6-jdk11","8.6.2-ubuntu-jdk11","8.2.1-jdk11","8.2.1-ubuntu-jdk11","8.6.2-jdk11","8.2.1","8.2.0-ubuntu-jdk17","8.5.2-ubuntu-jdk11","8.5.2-jdk11","8.2.0-jdk17","8.4-ubuntu-jdk11","8.4.5-ubuntu-jdk11","8.4-jdk11","8.4.5-jdk11","8.4.1-ubuntu-jdk11","8.3-ubuntu-jdk11","8.4.1-jdk11","8.3-jdk11","8.3.4-ubuntu-jdk11","8.3.4-jdk11","8.3.0-ubuntu-jdk11","8.3.0-jdk11","8.1.3-jdk17","8.1.3-ubuntu-jdk17","7.19.4-ubuntu-jdk11","7.19.4-jdk11","7.19.4","7.19.1-ubuntu-jdk11","7.19.19-ubuntu-jdk11","7.19.1-jdk11","7.19.19-jdk11","7.19.19","8.8.0-ubi9-jdk17","7.19.14-ubuntu-jdk11","7.19.14-jdk11","8.8.0-ubi9","8.7.1-ubuntu-jdk17","7.19.14","7.19","8.7.1-ubi9-jdk17","7.19.1","8.7.1-jdk17","8.7.1-ubi9","8.7.1","8.6.1-ubi9-jdk17","8.6.1-ubi9","8.6.0-ubi9-jdk17","8.6.0-ubi9","8.5.7-ubi9-jdk17","8.5.7-ubi9","8.5.6-ubi9-jdk17","8.5.6-ubi9","8.5.0-ubuntu-jdk17","8.5.0-jdk17","8.5.0","8.4.0-ubuntu-jdk17","8.4.0","8.4.0-jdk17","8.3.2-jdk17","8.3.2","8.3.2-ubuntu-jdk17","8.2.2-ubuntu-jdk17","8.2.2-ubuntu-jdk11","8.2.2-jdk17","8.2.2-jdk11","8.2.2","8.2.1-ubuntu-jdk17","8.2.1-jdk17","8.1.0-jdk11","8.1.0-ubuntu-jdk11","8.1.0","8.0.3-ubuntu-jdk17","8.0.3-jdk17","8.0.0-ubuntu-jdk11","8.0.0-jdk11","8.2.0","8.0.0","7.19.9-ubuntu-jdk11","7.19.9-jdk11","7.19.9","jdk11","ubuntu-jdk11","8-jdk11","8-ubuntu-jdk11","8.9-ubuntu-jdk11","8.9-jdk11","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.5-ubuntu-jdk11","8.5-jdk11","8.5.4-ubuntu-jdk17","8.5.8-jdk11","8.5.8-ubuntu-jdk11","8.5.4-jdk17","8.5.4","8.4.3-ubuntu-jdk17","8.4.3-jdk17","8.4.3","8.0.0-ubuntu-jdk17","8.0.0-jdk17","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.4.2-ubuntu-jdk11","8.4.2-jdk11","8.3.1-ubuntu-jdk11","8.3.1-jdk11","8.2-ubuntu-jdk17","8.2-jdk17","8.2.3-jdk17","8.2.3-ubuntu-jdk17","8.1-ubuntu-jdk17","8.1.4-ubuntu-jdk17","8.1.4-jdk17","8.1-jdk17","8.1.0-ubuntu-jdk17","8.1.0-jdk17","8.0-ubuntu-jdk17","8.0.4-ubuntu-jdk17","8.0-jdk17","8.0.4-jdk17","7.19.5-ubuntu-jdk11","7.19.5","7.19.2-ubuntu-jdk11","7.19.5-jdk11","7.19.2-jdk11","7.19.2","7.19.15-ubuntu-jdk11","7.19.15-jdk11","7.19.10-jdk11","7.19.15","7.19.10-ubuntu-jdk11","7.19.10","8.2-ubuntu-jdk11","8.2-jdk11","8.2.3-jdk11","8.2.3","8.2","8.1.1-ubuntu-jdk11","8.1.1-jdk11","8.1.1","8.0.1-ubuntu-jdk11","8.0.1-jdk11","7.20.0-ubuntu-jdk11","8.0.1","7.20.0-jdk11","7.20.0","8.5.4-jdk11","8.5.4-ubuntu-jdk11","8.5.0-ubuntu-jdk11","8.5.0-jdk11","8.7.1-jdk11","8.7.1-ubuntu-jdk11","8.4.3-jdk11","8.4.3-ubuntu-jdk11","8.4.0-ubuntu-jdk11","8.4.0-jdk11","8.1.1-ubuntu-jdk17","8.1.3-ubuntu-jdk11","8.3.2-ubuntu-jdk11","8.3.2-jdk11","8.0.2-ubuntu-jdk17","8.1.3","8.1.3-jdk11","8.0.2-ubuntu-jdk11","8.2.3-ubuntu-jdk11","8.0.2-jdk11","8.0.1-ubuntu-jdk17","8.0.2","7.20.1","7.20.1-jdk11","7.19.6-ubuntu-jdk11","8.1.1-jdk17","7.19.20","7.19.16-ubuntu-jdk11","7.19.6","8.0.2-jdk17","7.19.20-jdk11","7.19.16","8.0.1-jdk17","7.20.1-ubuntu-jdk11","7.19.6-jdk11","7.19.20-ubuntu-jdk11","7.19.16-jdk11","7.19.11","7.19.11-jdk11","7.19.11-ubuntu-jdk11","eap-ubuntu-jdk11","eap-jdk17","eap-jdk11","9.0.0-m41-jdk17","9.0.0-m41","9.0.0-m41-jdk11","9.0.0-m41-ubuntu-jdk17","9.0.0-m41-ubi9","9.0.0-m41-ubi9-jdk17","eap-ubuntu-jdk17","eap","9.0.0-m41-ubuntu-jdk11","9.0.0-m30-jdk17","9.0.0-m30-ubuntu-jdk17","9.0.0-m30-jdk11","9.0.0-m30","9.0.0-m30-ubuntu-jdk11","9.0.0-m26-ubuntu-jdk17","9.0.0-m26-jdk17","9.0.0-m26-ubuntu-jdk11","9.0.0-m26-ubi9","9.0.0-m26-jdk11","9.0.0-m26","9.0.0-m26-ubi9-jdk17","8.9.0-rc1-ubuntu-jdk11","8.9.0-rc1-jdk11","8.9.0-rc1-ubi9","8.9.0-rc1-jdk17","8.9.0-rc1-ubi9-jdk17","8.9.0-rc1-ubuntu-jdk17","8.9.0-rc1","9.0.0-m23-jdk11","9.0.0-m23-ubi9-jdk17","9.0.0-m23-ubi9","9.0.0-m23-ubuntu-jdk11","9.0.0-m23","9.0.0-m23-jdk17","9.0.0-m23-ubuntu-jdk17","8.9.0-beta2-ubi9-jdk17","8.9.0-beta2-ubi9","8.9.0-beta2-ubuntu-jdk17","8.9.0-beta2-jdk17","8.9.0-beta2-jdk11","8.9.0-beta2-ubuntu-jdk11","8.9.0-beta2","9.0.0-m16-ubuntu-jdk17","9.0.0-m16-jdk17","9.0.0-m16-ubi9-jdk17","9.0.0-m16-ubi9","9.0.0-m16-jdk11","9.0.0-m16-ubuntu-jdk11","9.0.0-m16","8.9.0-m58-ubuntu-jdk11","8.9.0-m58-jdk11","8.9.0-m58-jdk17","8.9.0-m58-ubuntu-jdk17","8.9.0-m58","9.0.0-m15-jdk17","9.0.0-m15-ubuntu-jdk17","9.0.0-m15","9.0.0-m15-jdk11","9.0.0-m15-ubuntu-jdk11","8.9.0-m50-jdk17","8.9.0-m50-ubuntu-jdk11","8.9.0-m50-ubuntu-jdk17","8.9.0-m50","8.9.0-m50-jdk11","9.0.0-m14-ubuntu-jdk11","9.0.0-m14-jdk17","9.0.0-m14-jdk11","9.0.0-m14","9.0.0-m14-ubuntu-jdk17","9.0.0-m13-jdk11","9.0.0-m13-jdk17","9.0.0-m13","9.0.0-m13-ubuntu-jdk17","9.0.0-m13-ubuntu-jdk11","8.9.0-m43-jdk17","8.9.0-m43","8.9.0-m43-jdk11","8.9.0-m43-ubuntu-jdk11","8.9.0-m43-ubuntu-jdk17","9.0.0-m11-jdk17","9.0.0-m11","9.0.0-m11-ubuntu-jdk17","9.0.0-m10-ubuntu-jdk11","9.0.0-m10-jdk11","9.0.0-m10-ubuntu-jdk17","9.0.0-m10-jdk17","9.0.0-m10","9.0.0-m09-ubuntu-jdk17","9.0.0-m09-jdk17","9.0.0-m09-ubuntu-jdk11","9.0.0-m09-jdk11","9.0.0-m09","8.8.0-rc1-jdk17","8.8.0-rc1-jdk11","8.8.0-rc1-ubuntu-jdk11","8.8.0-rc1","8.8.0-rc1-ubuntu-jdk17","9.0.0-m08-jdk17","9.0.0-m08-ubuntu-jdk17","9.0.0-m08","9.0.0-m08-ubuntu-jdk11","9.0.0-m08-jdk11","8.8.0-beta1-jdk17","8.8.0-beta1-ubuntu-jdk17","8.8.0-beta1-jdk11","8.8.0-beta1","8.8.0-beta1-ubuntu-jdk11","9.0.0-m07-ubuntu-jdk17","9.0.0-m07-jdk17","9.0.0-m07-jdk11","9.0.0-m07","9.0.0-m07-ubuntu-jdk11","7.16.2-ubuntu-jdk11","7.16.2","7.16.2-jdk11","7.13.7-ubuntu-jdk11","7.13.7-jdk11","7.13.7","7.11.0-ubuntu-jdk11","7.11.0-jdk11","7.11.0","7.13.12-ubuntu-jdk11","7.13.12-jdk11","7.13.12","7.16.3-ubuntu-jdk11","7.16.3-jdk11","7.16.3","7.13.8-ubuntu-jdk11","7.13.8-jdk11","7.13.8","7.11.1-ubuntu-jdk11","7.11.1-jdk11","7.11.1","7.13.13-ubuntu-jdk11","7.13.13-jdk11","7.13.13","7.16.4-ubuntu-jdk11","7.16.4-jdk11","7.16.4","7.11.2-ubuntu-jdk11","7.11.2-jdk11","7.11.2","7.13.9-ubuntu-jdk11","7.13.9-jdk11","7.13.9","7.16-ubuntu-jdk11","7.16-jdk11","7.16.5-ubuntu-jdk11","7.16.5-jdk11","7.16.5","7.16","7.13.14-ubuntu-jdk11","7.13.14-jdk11","7.13.14","7.14.0-ubuntu-jdk11","7.14.0-jdk11","7.14.0","7.11.3-ubuntu-jdk11","7.11.3-jdk11","7.11.3","7.13.15-ubuntu-jdk11","7.13.15-jdk11","7.11-ubuntu-jdk11","7.13.15","7.11.6-ubuntu-jdk11","7.11.6","7.11","7.17.0-ubuntu-jdk11","7.17.0-jdk11","7.17.0","7.14.1-ubuntu-jdk11","7.14.1-jdk11","7.14.1","7.11-jdk11","7.11.6-jdk11","7.13.16-ubuntu-jdk11","7.13.16-jdk11","7.13.16","7.17.1-ubuntu-jdk11","7.17.1-jdk11","7.17.1","7.14.2-ubuntu-jdk11","7.14.2-jdk11","7.14.2","7.13.17-ubuntu-jdk11","7.13.17-jdk11","7.13.17","7.12.0-ubuntu-jdk11","7.12.0-jdk11","7.12.0","7.17.2-ubuntu-jdk11","7.17.2-jdk11","7.17.2","7.14.3-ubuntu-jdk11","7.14.3-jdk11","7.14.3","7.13.18-ubuntu-jdk11","7.13.18-jdk11","7.13.18","7.17.3-ubuntu-jdk11","7.17.3-jdk11","7.17.3","7.14-ubuntu-jdk11","7.14-jdk11","7.14.4-ubuntu-jdk11","7.14.4-jdk11","7.14.4","7.14","7.12.1-ubuntu-jdk11","7.12.1-jdk11","7.12.1","7.17.4-ubuntu-jdk11","7.17.4-jdk11","7.17.4","7.13.19-ubuntu-jdk11","7.13.19-jdk11","7.13.19","7.12.2-ubuntu-jdk11","7.12.2-jdk11","7.12.2","7.15.0-ubuntu-jdk11","7.15.0-jdk11","7.15.0","7.13.2-ubuntu-jdk11","7.13.2-jdk11","7.13.2","7.12.3-ubuntu-jdk11","7.12.3-jdk11","7.12.3","7.17-ubuntu-jdk11","7.17-jdk11","7.17.5-ubuntu-jdk11","7.17.5-jdk11","7.17.5","7.17","7.15.1-ubuntu-jdk11","7.15.1-jdk11","7.15.1","7.13-ubuntu-jdk11","7.13-jdk11","7.13.20-ubuntu-jdk11","7.13.20-jdk11","7.13.20","7.13","7.12.4-ubuntu-jdk11","7.12.4-jdk11","7.12.4","7.18.0-ubuntu-jdk11","7.18.0-jdk11","7.18.0","7.15.2-ubuntu-jdk11","7.15.2-jdk11","7.15.2","7.12-ubuntu-jdk11","7.12.5","7.12-jdk11","7.12.5-ubuntu-jdk11","7.12.5-jdk11","7.12","7.15-ubuntu-jdk11","7.15-jdk11","7.15.3-ubuntu-jdk11","7.15.3-jdk11","7.15.3","7.15","7.13.3-ubuntu-jdk11","7.13.3-jdk11","7.13.3","7.18.1-ubuntu-jdk11","7.18.1-jdk11","7.18.1","7.18.2-ubuntu-jdk11","7.18.2-jdk11","7.18.2","7.13.4-ubuntu-jdk11","7.13.4-jdk11","7.13.4","7.13.0-ubuntu-jdk11","7.13.0-jdk11","7.13.0","7.16.0-ubuntu-jdk11","7.16.0-jdk11","7.16.0","7.18-ubuntu-jdk11","7.18-jdk11","7.18.3-ubuntu-jdk11","7.18.3-jdk11","7.18.3","7.18","7.13.5-ubuntu-jdk11","7.13.5-jdk11","7.13.5","7.13.1-ubuntu-jdk11","7.13.1-jdk11","7.13.1","7.16.1-ubuntu-jdk11","7.16.1-jdk11","7.16.1","7.13.6-ubuntu-jdk11","7.13.6-jdk11","7.13.6","7.13.11-ubuntu-jdk11","7.13.11-jdk11","7.13.11","8.8.0-m61-ubuntu-jdk11","8.8.0-m61-jdk11","8.8.0-m61","8.8.0-m61-ubuntu-jdk17","8.8.0-m61-jdk17","8.8.0-m46-ubuntu-jdk11","8.8.0-m46-ubuntu-jdk17","8.8.0-m46-jdk17","8.8.0-m46","8.8.0-m46-jdk11","8.7.0-rc1-ubuntu-jdk17","8.7.0-rc1-jdk17","8.7.0-rc1-jdk11","8.7.0-rc1","8.7.0-rc1-ubuntu-jdk11","8.7.0-beta1-ubuntu-jdk11","8.7.0-beta1","8.7.0-beta1-jdk11","8.7.0-beta1-jdk17","8.7.0-beta1-ubuntu-jdk17","8.7.0-m48-ubuntu-jdk17","8.7.0-m48-jdk17","8.7.0-m48-jdk11","8.7.0-m48-ubuntu-jdk11","8.7.0-m48","8.7.0-m41-ubuntu-jdk17","8.7.0-m41-ubuntu-jdk11","8.7.0-m41-jdk17","8.7.0-m41-jdk11","8.7.0-m41","8.6.0-rc1-ubuntu-jdk17","8.6.0-rc1-jdk17","8.6.0-rc1-ubuntu-jdk11","8.6.0-rc1-jdk11","8.6.0-rc1","8.6.0-beta2-jdk17","8.6.0-beta2","8.6.0-beta2-jdk11","8.6.0-beta2-ubuntu-jdk11","8.6.0-beta2-ubuntu-jdk17","8.6.0-beta1-jdk17","8.6.0-beta1-ubuntu-jdk17","8.6.0-beta1","8.6.0-beta1-ubuntu-jdk11","8.6.0-beta1-jdk11","8.6.0-m85-ubuntu-jdk17","8.6.0-m85-jdk17","8.6.0-m85","8.6.0-m85-ubuntu-jdk11","8.6.0-m85-jdk11","8.6.0-m79-jdk11","8.6.0-m79-ubuntu-jdk11","8.6.0-m79-ubuntu-jdk17","8.6.0-m79-jdk17","8.6.0-m79","8.5.0-rc1-jdk11","8.5.0-rc1","8.5.0-rc1-jdk17","8.5.0-rc1-ubuntu-jdk11","8.5.0-rc1-ubuntu-jdk17","8.5.0-beta1-ubuntu-jdk17","8.5.0-beta1-jdk17","8.5.0-beta1","8.5.0-beta1-ubuntu-jdk11","8.5.0-beta1-jdk11","8.5.0-m06-jdk17","8.5.0-m06-ubuntu-jdk17","8.5.0-m06-jdk11","8.5.0-m06","8.5.0-m06-ubuntu-jdk11","8.5.0-m05-jdk17","8.5.0-m05-jdk11","8.5.0-m05-ubuntu-jdk17","8.5.0-m05-ubuntu-jdk11","8.5.0-m05","8.5.0-m04-jdk17","8.5.0-m04-ubuntu-jdk11","8.5.0-m04-ubuntu-jdk17","8.5.0-m04","8.5.0-m04-jdk11","8.4.0-rc2-ubuntu-jdk17","8.4.0-rc2-jdk17","8.4.0-rc2-ubuntu-jdk11","8.4.0-rc2","8.4.0-rc2-jdk11","8.4.0-rc1-jdk17","8.4.0-rc1-ubuntu-jdk17","8.4.0-rc1-jdk11","8.4.0-rc1","8.4.0-rc1-ubuntu-jdk11","8.4.0-beta1-jdk17","8.4.0-beta1-ubuntu-jdk17","8.4.0-beta1","8.4.0-beta1-jdk11","8.4.0-beta1-ubuntu-jdk11","8.4.0-m48-jdk17","8.4.0-m48-ubuntu-jdk11","8.4.0-m48-jdk11","8.4.0-m48-ubuntu-jdk17","8.4.0-m48","8.4.0-m22","8.4.0-m22-jdk17","8.4.0-m22-ubuntu-jdk11","8.4.0-m22-jdk11","8.4.0-m22-ubuntu-jdk17","8.4.0-m21-ubuntu-jdk17","8.4.0-m21-jdk17","8.4.0-m21","8.4.0-m21-ubuntu-jdk11","8.4.0-m21-jdk11","8.3.0-rc2-ubuntu-jdk11","8.3.0-rc2-jdk11","8.3.0-rc2-ubuntu-jdk17","8.3.0-rc2-jdk17","8.3.0-rc2","8.3.0-rc1-ubuntu-jdk11","8.3.0-rc1-ubuntu-jdk17","8.3.0-rc1-jdk11","8.3.0-rc1-jdk17","8.3.0-rc1","8.3.0-beta1-jdk11","8.3.0-beta1-jdk17","8.3.0-beta1-ubuntu-jdk11","8.3.0-beta1","8.3.0-beta1-ubuntu-jdk17","8.3.0-tinymce6-m01-ubuntu-jdk11","8.3.0-tinymce6-m01","8.3.0-tinymce6-m01-ubuntu-jdk17","8.3.0-tinymce6-m01-jdk11","8.3.0-tinymce6-m01-jdk17","8.3.0-m48","8.3.0-m48-ubuntu-jdk17","8.3.0-m48-jdk11","8.3.0-m48-ubuntu-jdk11","8.3.0-m48-jdk17","8.3.0-m42-ubuntu-jdk17","8.3.0-m42-jdk17","8.3.0-m42-jdk11","8.3.0-m42-ubuntu-jdk11","8.3.0-m42","8.3.0-m37-jdk11","8.3.0-m37-ubuntu-jdk11","8.3.0-m37-jdk17","8.3.0-m37","8.3.0-m37-ubuntu-jdk17","8.3.0-m31-ubuntu-jdk17","8.3.0-m31","8.3.0-m31-ubuntu-jdk11","8.3.0-m31-jdk11","8.3.0-m31-jdk17","8.2.0-rc1-jdk11","8.2.0-rc1-jdk17","8.2.0-rc1-ubuntu-jdk11","8.2.0-rc1-ubuntu-jdk17","8.2.0-rc1","8.2.0-beta2","8.2.0-beta2-ubuntu-jdk11","8.2.0-beta2-jdk17","8.2.0-beta2-ubuntu-jdk17","8.2.0-beta2-jdk11","8.2.0-beta1-ubuntu-jdk11","8.2.0-beta1-ubuntu-jdk17","8.2.0-beta1-jdk17","8.2.0-beta1-jdk11","8.2.0-beta1","8.2.0-m36-jdk11","8.2.0-m36-jdk17","8.2.0-m36","8.2.0-m36-ubuntu-jdk17","8.2.0-m36-ubuntu-jdk11","8.2.0-m27-ubuntu-jdk11","8.2.0-m27-jdk17","8.2.0-m27-jdk11","8.2.0-m27","8.2.0-m27-ubuntu-jdk17","8.2.0-m20","8.2.0-m20-jdk11","8.2.0-m20-ubuntu-jdk11","8.2.0-m20-ubuntu-jdk17","8.2.0-m20-jdk17","8.1.0-rc1-jdk11","8.1.0-rc1","8.1.0-rc1-ubuntu-jdk11","8.1.0-rc1-ubuntu-jdk17","8.1.0-rc1-jdk17","8.1.0-beta2","8.1.0-beta2-jdk11","8.1.0-beta2-ubuntu-jdk11","8.1.0-beta2-ubuntu-jdk17","8.1.0-beta2-jdk17","8.1.0-beta1-jdk11","8.1.0-beta1","8.1.0-beta1-ubuntu-jdk11","8.1.0-beta1-jdk17","8.1.0-beta1-ubuntu-jdk17","8.1.0-m78-jdk11","8.1.0-m78-ubuntu-jdk17","8.1.0-m78-ubuntu-jdk11","8.1.0-m78","8.1.0-m78-jdk17","7.6.2-ubuntu-jdk11","7.6.2-jdk11","7.6.2","7.6.1-ubuntu-jdk11","7.6.1-jdk11","7.6.1","7.9-ubuntu-jdk11","7.9-jdk11","7.9.3-ubuntu-jdk11","7.9.3-jdk11","7.9.3","7.9.1-ubuntu-jdk11","7.9.1-jdk11","7.9.1","7.9.0-ubuntu-jdk11","7.9.0-jdk11","7.9.0","7.9","7.8-ubuntu-jdk11","7.8-jdk11","7.8.3-ubuntu-jdk11","7.8.3-jdk11","7.8.3","7.8","7.6.0-ubuntu-jdk11","7.6.0-jdk11","7.6.0","7.5-ubuntu-jdk11","7.5-jdk11","7.5.2-ubuntu-jdk11","7.5.2-jdk11","7.8.1-ubuntu-jdk11","7.8.1-jdk11","7.5.2","7.8.1","7.8.0-ubuntu-jdk11","7.8.0-jdk11","7.5.1-ubuntu-jdk11","7.8.0","7.7-ubuntu-jdk11","7.5.1-jdk11","7.7-jdk11","7.7.4-ubuntu-jdk11","7.5.1","7.7.4-jdk11","7.7.4","7.5.0-ubuntu-jdk11","7.7.3-ubuntu-jdk11","7.7.3-jdk11","7.7.3","7.5.0-jdk11","7.7.2-ubuntu-jdk11","7.7.2-jdk11","7.5.0","7.7.2","7.7","7.6-ubuntu-jdk11","7.5","7.6-jdk11","7.6.3-ubuntu-jdk11","7.4.9-ubuntu-jdk11","7.6.3-jdk11","7.6.3","7.6","7.4.9-jdk11","7.4.9","7.4.17-ubuntu-jdk11","7.4.17-jdk11","7.4-ubuntu-jdk11","7.4.17","7.4-jdk11","7.4.16-ubuntu-jdk11","7.4.8-ubuntu-jdk11","7.4.16-jdk11","7.4.8-jdk11","7.4.8","7.4.16","7.4.7-ubuntu-jdk11","7.4.7-jdk11","7.4.7","7.4.15-ubuntu-jdk11","7.4.6-ubuntu-jdk11","7.4.6-jdk11","7.4.6","7.4.15-jdk11","7.4.5-ubuntu-jdk11","7.4.5-jdk11","7.4.5","7.4.4-ubuntu-jdk11","7.4.15","7.4.4-jdk11","7.4.4","7.4.3-ubuntu-jdk11","7.4.3-jdk11","7.4.14-ubuntu-jdk11","7.4.3","7.4.1-ubuntu-jdk11","7.4.1-jdk11","7.4.14-jdk11","7.4.18-ubuntu-jdk11","7.4.18-jdk11","7.4.18","7.4.14","7.4.13-ubuntu-jdk11","7.4.13-jdk11","7.4.13","7.4.12-ubuntu-jdk11","7.4.12-jdk11","7.4.12","7.4.11-ubuntu-jdk11","7.4.11-jdk11","7.4.11","7.4.10-ubuntu-jdk11","7.4.10-jdk11","7.4.10","7.4.1","7.4.0-ubuntu-jdk11","7.4.0-jdk11","7.4.0","7.4","7.10-ubuntu-jdk11","7.10-jdk11","7.10.2-ubuntu-jdk11","7.10.2-jdk11","7.10.2","7.10.1-ubuntu-jdk11","7.10.1-jdk11","7.10.1","7.10.0-ubuntu-jdk11","7.10.0-jdk11","7.10.0","7.10","8.0.0-rc1-ubuntu-jdk11","8.0.0-rc1-jdk17","8.0.0-rc1-jdk11","8.0.0-rc1","8.0.0-rc1-ubuntu-jdk17","8.0.0-beta1-ubuntu-jdk17","8.0.0-beta1","8.0.0-beta1-ubuntu-jdk11","8.0.0-beta1-jdk17","8.0.0-beta1-jdk11","8.0.0-m90-ubuntu-jdk17","8.0.0-m90-ubuntu-jdk11","8.0.0-m90-jdk11","8.0.0-m90-jdk17","8.0.0-m90","8.0.0-m89-ubuntu-jdk17","8.0.0-m89-jdk17","8.0.0-m89-jdk11","8.0.0-m89","8.0.0-m89-ubuntu-jdk11","8.0.0-m86","8.0.0-m86-ubuntu-jdk17","8.0.0-m86-jdk11","8.0.0-m86-ubuntu-jdk11","8.0.0-m86-jdk17","8.0.0-m82-ubuntu-jdk11","8.0.0-m82-jdk11","8.0.0-m82","8.0.0-m82-ubuntu-jdk17","8.0.0-m82-jdk17","8.0.0-m76-ubuntu-jdk17","8.0.0-m76-ubuntu-jdk11","8.0.0-m76-jdk17","8.0.0-m76","8.0.0-m76-jdk11","8.0.0-m69","8.0.0-m69-ubuntu-jdk11","8.0.0-m69-jdk17","8.0.0-m69-ubuntu-jdk17","8.0.0-m69-jdk11","8.0.0-m60","8.0.0-m60-jdk17","8.0.0-m60-jdk11","8.0.0-m60-ubuntu-jdk17","8.0.0-m60-ubuntu-jdk11","7.20.0-rc1-ubuntu-jdk11","7.20.0-rc1-jdk11","7.20.0-rc1","8.0.0-struts-m48-ubuntu-jdk17","8.0.0-struts-m48-ubuntu-jdk11","8.0.0-struts-m48-jdk11","8.0.0-struts-m48","8.0.0-struts-m48-jdk17","7.20.0-beta4-jdk11","7.20.0-beta4-ubuntu-jdk11","7.20.0-beta4","8.0.0-m45-ubuntu-jdk11","8.0.0-m45-jdk17","8.0.0-m45-jdk11","8.0.0-m45-ubuntu-jdk17","8.0.0-m45","8.0.0-struts-m39-ubuntu-jdk11","8.0.0-struts-m39-ubuntu-jdk17","8.0.0-struts-m39-jdk11","8.0.0-struts-m39-jdk17","8.0.0-struts-m39","8.0.0-struts-m027-ubuntu-jdk11","8.0.0-struts-m027-ubuntu-jdk17","8.0.0-struts-m027","8.0.0-struts-m027-jdk17","8.0.0-struts-m027-jdk11","7.20.0-beta2-ubuntu-jdk11","7.20.0-beta2-jdk11","7.20.0-beta2-jdk17","7.20.0-beta2","7.20.0-beta2-ubuntu-jdk17","8.0.0-m026-ubuntu-jdk11","8.0.0-m026-ubuntu-jdk17","8.0.0-m026","8.0.0-m026-jdk17","8.0.0-m026-jdk11","8.0.0-struts-m020-ubuntu-jdk17","8.0.0-m023-jdk17","8.0.0-m023-ubuntu-jdk17","8.0.0-struts-m020-jdk17","7.20.0-m104-jdk17","7.20.0-m104-ubuntu-jdk17","8.0.0-m023-jdk11","7.20.0-m104","8.0.0-m023-ubuntu-jdk11","7.20.0-m104-ubuntu-jdk11","8.0.0-m023","7.20.0-m104-jdk11","8.0.0-m021-ubuntu-jdk11","8.0.0-m021","8.0.0-m021-jdk11","7.20.0-m99","7.20.0-m99-ubuntu-jdk11","7.20.0-m99-jdk11","8.0.0-struts-m020-jdk11","8.0.0-struts-m020-ubuntu-jdk11","8.0.0-struts-m020","8.0.0-m020-jdk11","8.0.0-m020","8.0.0-m020-ubuntu-jdk11","7.20.0-m78-ubuntu-jdk11","7.20.0-m78","7.20.0-m78-jdk11","7.19.0-rc3-jdk11","7.19.0-rc3-ubuntu-jdk11","7.19.0-rc3","8.0.0-m018-ubuntu-jdk11","8.0.0-m018-jdk11","8.0.0-m018","8.0.0-m017-ubuntu-jdk11","8.0.0-m017-jdk11","8.0.0-m017","7.19.0-rc2-ubuntu-jdk11","7.19.0-rc2-jdk11","7.19.0-rc2","8.0.0-m015-ubuntu-jdk11","8.0.0-m015","8.0.0-m015-jdk11","7.19.0-rc1-jdk11","7.19.0-rc1","7.19.0-rc1-ubuntu-jdk11","7.19.0-beta1-jdk11","7.19.0-beta1-ubuntu-jdk11","7.19.0-beta1","8.0.0-m014","8.0.0-m014-ubuntu-jdk11","8.0.0-m014-jdk11","7.17-ubuntu-18.04-adoptopenjdk11","7.17-ubuntu","7.17-adoptopenjdk11","7.17.5-ubuntu-18.04-adoptopenjdk11","7.17.5-ubuntu","7.17.5-adoptopenjdk11","eap-ubuntu-18.04-adoptopenjdk11","eap-ubuntu","8.0.0-m012-ubuntu-18.04-adoptopenjdk11","eap-adoptopenjdk11","8.0.0-m012-ubuntu","8.0.0-m012-adoptopenjdk11","8.0.0-m012","8.0.0-m012-jdk11","7.19.0-m02-jdk11","7.19.0-m02-ubuntu-18.04-adoptopenjdk11","7.19.0-m02-adoptopenjdk11","7.19.0-m02-ubuntu","7.19.0-m02","ubuntu-18.04-adoptopenjdk11","ubuntu","adoptopenjdk11","7-ubuntu-18.04-adoptopenjdk11","7-ubuntu","7-adoptopenjdk11","7.9.1-ubuntu-18.04-adoptopenjdk11","7.9.1-ubuntu","7.9.1-adoptopenjdk11","7.9.0-ubuntu-18.04-adoptopenjdk11","7.9.0-ubuntu","7.9.0-adoptopenjdk11","7.8-ubuntu-18.04-adoptopenjdk11","7.8-ubuntu","7.8-adoptopenjdk11","7.8.3-ubuntu-18.04-adoptopenjdk11","7.8.3-ubuntu","7.8.3-adoptopenjdk11","7.8.1-ubuntu-18.04-adoptopenjdk11","7.8.1-ubuntu","7.8.1-adoptopenjdk11","7.8.0-ubuntu-18.04-adoptopenjdk11","7.8.0-ubuntu","7.8.0-adoptopenjdk11","7.7-ubuntu-18.04-adoptopenjdk11","7.7-ubuntu","7.7-adoptopenjdk11","7.7.4-ubuntu-18.04-adoptopenjdk11","7.7.4-ubuntu","7.7.4-adoptopenjdk11","7.7.3-ubuntu-18.04-adoptopenjdk11","7.7.3-ubuntu","7.7.3-adoptopenjdk11","7.7.2-ubuntu-18.04-adoptopenjdk11","7.7.2-ubuntu","7.7.2-adoptopenjdk11","7.6-ubuntu-18.04-adoptopenjdk11","7.6-ubuntu","7.6-adoptopenjdk11","7.6.3-ubuntu-18.04-adoptopenjdk11","7.6.3-ubuntu","7.6.3-adoptopenjdk11","7.6.0-ubuntu-18.04-adoptopenjdk11","7.6.0-ubuntu","7.6.0-adoptopenjdk11","7.5-ubuntu-18.04-adoptopenjdk11","7.5-ubuntu","7.5-adoptopenjdk11","7.5.2-ubuntu-18.04-adoptopenjdk11","7.5.2-ubuntu","7.5.2-adoptopenjdk11","7.5.1-ubuntu-18.04-adoptopenjdk11","7.5.1-ubuntu","7.5.1-adoptopenjdk11","7.5.0-ubuntu-18.04-adoptopenjdk11","7.5.0-ubuntu","7.5.0-adoptopenjdk11","7.4-ubuntu-18.04-adoptopenjdk11","7.4-ubuntu","7.4-adoptopenjdk11","7.4.9-ubuntu-18.04-adoptopenjdk11","7.4.9-ubuntu","7.4.9-adoptopenjdk11","7.4.8-ubuntu-18.04-adoptopenjdk11","7.4.8-ubuntu","7.4.8-adoptopenjdk11","7.4.7-ubuntu-18.04-adoptopenjdk11","7.4.7-ubuntu","7.4.7-adoptopenjdk11","7.4.6-ubuntu-18.04-adoptopenjdk11","7.4.6-ubuntu","7.4.6-adoptopenjdk11","7.4.3-ubuntu-18.04-adoptopenjdk11","7.4.3-ubuntu","7.4.3-adoptopenjdk11","7.4.1-ubuntu","7.4.1-adoptopenjdk11","7.4.17-ubuntu-18.04-adoptopenjdk11","7.4.17-ubuntu","7.4.17-adoptopenjdk11","7.4.16-ubuntu-18.04-adoptopenjdk11","7.4.16-ubuntu","7.4.16-adoptopenjdk11","7.4.15-ubuntu-18.04-adoptopenjdk11","7.4.15-ubuntu","7.4.15-adoptopenjdk11","7.4.14-ubuntu-18.04-adoptopenjdk11","7.4.14-ubuntu","7.4.14-adoptopenjdk11","7.4.13-ubuntu-18.04-adoptopenjdk11","7.4.13-ubuntu","7.4.13-adoptopenjdk11","7.4.12-ubuntu-18.04-adoptopenjdk11","7.4.12-ubuntu","7.4.12-adoptopenjdk11","7.4.0-ubuntu-18.04-adoptopenjdk11","7.4.0-ubuntu","7.4.0-adoptopenjdk11","7.18-ubuntu-18.04-adoptopenjdk11","7.18-ubuntu","7.18-adoptopenjdk11","7.18.1-ubuntu-18.04-adoptopenjdk11","7.18.1-ubuntu","7.18.1-adoptopenjdk11","7.18.0-ubuntu-18.04-adoptopenjdk11","7.18.0-ubuntu","7.18.0-adoptopenjdk11","7.17.4-ubuntu-18.04-adoptopenjdk11","7.17.4-ubuntu","7.17.4-adoptopenjdk11","7.17.3-ubuntu-18.04-adoptopenjdk11","7.17.3-ubuntu","7.17.3-adoptopenjdk11","7.17.2-ubuntu-18.04-adoptopenjdk11","7.17.2-ubuntu","7.17.2-adoptopenjdk11","7.16-ubuntu-18.04-adoptopenjdk11","7.16-ubuntu","7.16-adoptopenjdk11","7.16.4-ubuntu-18.04-adoptopenjdk11","7.16.4-ubuntu","7.16.4-adoptopenjdk11","7.16.3-ubuntu-18.04-adoptopenjdk11","7.16.3-ubuntu","7.16.3-adoptopenjdk11","7.16.2-ubuntu-18.04-adoptopenjdk11","7.16.2-ubuntu","7.16.2-adoptopenjdk11","7.16.1-ubuntu-18.04-adoptopenjdk11","7.16.1-ubuntu","7.16.1-adoptopenjdk11","7.16.0-ubuntu-18.04-adoptopenjdk11","7.16.0-ubuntu","7.16.0-adoptopenjdk11","7.15-ubuntu-18.04-adoptopenjdk11","7.15-ubuntu","7.15-adoptopenjdk11","7.15.2-ubuntu-18.04-adoptopenjdk11","7.15.2-ubuntu","7.15.2-adoptopenjdk11","7.15.1-ubuntu-18.04-adoptopenjdk11","7.15.1-ubuntu","7.15.1-adoptopenjdk11","7.14-ubuntu-18.04-adoptopenjdk11","7.14.3-ubuntu-18.04-adoptopenjdk11","7.14.3-ubuntu","7.14.0-ubuntu","7.14.0-adoptopenjdk11","7.13-ubuntu-18.04-adoptopenjdk11","7.13-ubuntu","7.13-adoptopenjdk11","7.13.7-ubuntu-18.04-adoptopenjdk11","7.13.7-ubuntu","7.13.7-adoptopenjdk11","7.13.6-ubuntu-18.04-adoptopenjdk11","7.13.6-ubuntu","7.13.6-adoptopenjdk11","7.13.5-ubuntu-18.04-adoptopenjdk11","7.13.5-ubuntu","7.13.5-adoptopenjdk11","7.13.4-ubuntu-18.04-adoptopenjdk11","7.13.4-ubuntu","7.13.4-adoptopenjdk11","7.12.5-ubuntu-18.04-adoptopenjdk11","7.12.5-ubuntu","7.12.4-ubuntu-18.04-adoptopenjdk11","7.12.4-ubuntu","7.12.4-adoptopenjdk11","7.12.3-ubuntu-18.04-adoptopenjdk11","7.12.3-ubuntu","7.12.3-adoptopenjdk11","7.12.2-ubuntu-18.04-adoptopenjdk11","7.12.2-ubuntu","7.12.2-adoptopenjdk11","7.12.1-ubuntu-18.04-adoptopenjdk11","7.12.1-ubuntu","7.12.1-adoptopenjdk11","7.11.1-ubuntu-18.04-adoptopenjdk11","7.11.1-ubuntu","7.11.1-adoptopenjdk11","7.11.0-ubuntu-18.04-adoptopenjdk11","7.11.0-ubuntu","7.11.0-adoptopenjdk11","7.10-ubuntu-18.04-adoptopenjdk11","7.10-ubuntu","7.10-adoptopenjdk11","7.10.2-ubuntu-18.04-adoptopenjdk11","7.10.2-ubuntu","7.10.2-adoptopenjdk11","7.10.1-ubuntu-18.04-adoptopenjdk11","7.10.1-ubuntu","7.10.1-adoptopenjdk11","7.10.0-ubuntu-18.04-adoptopenjdk11","7.10.0-ubuntu","7.10.0-adoptopenjdk11","7.14-adoptopenjdk11","7.9-ubuntu-18.04-adoptopenjdk11","7.9-ubuntu","7.9-adoptopenjdk11","7.14.3-adoptopenjdk11","7.9.3-ubuntu-18.04-adoptopenjdk11","7.9.3-ubuntu","7.9.3-adoptopenjdk11","7.14.2-ubuntu-18.04-adoptopenjdk11","7.6.2-ubuntu-18.04-adoptopenjdk11","7.6.2-ubuntu","7.14.2-ubuntu","7.6.2-adoptopenjdk11","7.6.1-ubuntu-18.04-adoptopenjdk11","7.6.1-ubuntu","7.14.2-adoptopenjdk11","7.6.1-adoptopenjdk11","7.14.1-ubuntu-18.04-adoptopenjdk11","7.4.5-ubuntu-18.04-adoptopenjdk11","7.4.5-ubuntu","7.14.1-ubuntu","7.4.5-adoptopenjdk11","7.4.4-ubuntu-18.04-adoptopenjdk11","7.14.1-adoptopenjdk11","7.4.4-ubuntu","7.4.4-adoptopenjdk11","7.14.0-ubuntu-18.04-adoptopenjdk11","7.4.1-ubuntu-18.04-adoptopenjdk11","7.13.3-ubuntu-18.04-adoptopenjdk11","7.4.11-ubuntu-18.04-adoptopenjdk11","7.13.3-ubuntu","7.4.11-ubuntu","7.13.3-adoptopenjdk11","7.4.11-adoptopenjdk11","7.13.2-ubuntu-18.04-adoptopenjdk11","7.4.10-ubuntu-18.04-adoptopenjdk11","7.13.2-ubuntu","7.4.10-ubuntu","7.13.2-adoptopenjdk11","7.4.10-adoptopenjdk11","7.13.1-ubuntu-18.04-adoptopenjdk11","7.17.1-ubuntu-18.04-adoptopenjdk11","7.13.1-ubuntu","7.17.1-ubuntu","7.13.1-adoptopenjdk11","7.17.1-adoptopenjdk11","7.13.0-ubuntu-18.04-adoptopenjdk11","7.13.0-ubuntu","7.17.0-ubuntu-18.04-adoptopenjdk11","7.17.0-ubuntu","7.13.0-adoptopenjdk11","7.17.0-adoptopenjdk11","7.12-ubuntu-18.04-adoptopenjdk11","7.12-ubuntu","7.12-adoptopenjdk11","7.15.0-ubuntu-18.04-adoptopenjdk11","7.12.5-adoptopenjdk11","7.15.0-ubuntu","7.12.0-ubuntu-18.04-adoptopenjdk11","7.15.0-adoptopenjdk11","7.12.0-ubuntu","7.11-ubuntu-18.04-adoptopenjdk11","7.14-ubuntu","7.11-ubuntu","7.11.6-ubuntu-18.04-adoptopenjdk11","7.11.6-ubuntu","7.11.6-adoptopenjdk11","7.11.3-ubuntu-18.04-adoptopenjdk11","7.11.3-ubuntu","7.11.3-adoptopenjdk11","7.11.2-ubuntu-18.04-adoptopenjdk11","7.11.2-ubuntu","7.11.2-adoptopenjdk11","7.12.0-adoptopenjdk11","7.11-adoptopenjdk11","8.0.0-m011-jdk11","8.0.0-m011","8.0.0-m011-ubuntu-18.04-adoptopenjdk11","8.0.0-m011-ubuntu","7.19.0-m01-ubuntu-18.04-adoptopenjdk11","7.19.0-m01-jdk11","7.19.0-m01-adoptopenjdk11","8.0.0-m011-adoptopenjdk11","7.19.0-m01","7.19.0-m01-ubuntu","7.18.0-rc1-ubuntu-18.04-adoptopenjdk11","7.18.0-rc1-ubuntu","7.18.0-rc1-jdk11","7.18.0-rc1-adoptopenjdk11","7.18.0-rc1","7.18.0-beta1-ubuntu-18.04-adoptopenjdk11","7.18.0-beta1-ubuntu","7.18.0-beta1-jdk11","7.18.0-beta1-adoptopenjdk11","7.18.0-beta1","7.18.0-m68-jdk11","7.18.0-m68","7.18.0-m68-ubuntu-18.04-adoptopenjdk11","7.18.0-m68-ubuntu","7.18.0-m68-adoptopenjdk11","7.18.0-m61-ubuntu","7.18.0-m61-adoptopenjdk11","7.18.0-m61","7.18.0-m61-ubuntu-18.04-adoptopenjdk11","7.18.0-m61-jdk11","7.18.0-m53-ubuntu-18.04-adoptopenjdk11","7.18.0-m53-ubuntu","7.18.0-m53","7.18.0-m53-jdk11","7.18.0-m53-adoptopenjdk11","7.18.0-m46-ubuntu","7.18.0-m46-adoptopenjdk11","7.18.0-m46","7.18.0-m46-ubuntu-18.04-adoptopenjdk11","7.18.0-m46-jdk11","7.18.0-m40-ubuntu-18.04-adoptopenjdk11","7.18.0-m40-ubuntu","7.18.0-m40-jdk11","7.18.0-m40-adoptopenjdk11","7.18.0-m40","temurin-7.17.0","7.17.0-rc1-ubuntu-18.04-adoptopenjdk11","7.17.0-rc1-ubuntu","7.17.0-rc1-jdk11","7.17.0-rc1-adoptopenjdk11","7.17.0-rc1","7.4.6-ubuntu-jdk-11","7.4.5-ubuntu-jdk-11","7.4.4-ubuntu-jdk-11","7.4.3-ubuntu-jdk-11","7.4.1-ubuntu-jdk-11","7.4.0-ubuntu-jdk-11","7.3.2","7.3.2-jdk11","7.3.2-adoptopenjdk11","7.3.2-ubuntu-18.04-adoptopenjdk11","7.3.2-ubuntu","7.3.3-ubuntu-18.04-adoptopenjdk11","7.3.3-adoptopenjdk11","7.3.3-jdk11","7.3.3","7.3.3-ubuntu","7.3.4-jdk11","7.3.4-ubuntu-18.04-adoptopenjdk11","7.3.4-adoptopenjdk11","7.3.4-ubuntu","7.3.4","7.3.5-ubuntu","7.3-adoptopenjdk11","7.3-jdk11","7.3.5-ubuntu-18.04-adoptopenjdk11","7.3.5","7.3","7.3.5-adoptopenjdk11","7.3-ubuntu-18.04-adoptopenjdk11","7.3.5-jdk11","7.3-ubuntu","7.3.1-jdk11","7.3.1-ubuntu-18.04-adoptopenjdk11","7.3.1-adoptopenjdk11","7.3.1","7.3.1-ubuntu","7.17.0-beta1-jdk11","7.17.0-beta1-adoptopenjdk11","7.17.0-beta1","7.17.0-beta1-ubuntu-18.04-adoptopenjdk11","7.17.0-beta1-ubuntu","7.17.0-m61-adoptopenjdk11","7.17.0-m61","7.17.0-m61-ubuntu","7.17.0-m61-jdk11","7.17.0-m61-ubuntu-18.04-adoptopenjdk11","7.17.0-m59-ubuntu","7.17.0-m59-adoptopenjdk11","7.17.0-m59","7.17.0-m59-jdk11","7.17.0-m59-ubuntu-18.04-adoptopenjdk11","7.17.0-m47-jdk11","7.17.0-m47","7.17.0-m47-ubuntu","7.17.0-m47-adoptopenjdk11","7.17.0-m47-ubuntu-18.04-adoptopenjdk11","7.16.0-rc1","7.16.0-rc1-ubuntu-18.04-adoptopenjdk11","7.16.0-rc1-ubuntu","7.16.0-rc1-adoptopenjdk11","7.16.0-rc1-jdk11","7.2.0-adoptopenjdk11","7.2.0-ubuntu-18.04-adoptopenjdk11","7.2.0","7.2.0-jdk11","7.2.0-ubuntu","7.2.1","7.2.1-jdk11","7.2.1-ubuntu","7.2.1-adoptopenjdk11","7.2.1-ubuntu-18.04-adoptopenjdk11","7.2.2","7.2.2-adoptopenjdk11","7.2-jdk11","7.2-adoptopenjdk11","7.2-ubuntu","7.2","7.2.2-jdk11","7.2.2-ubuntu","7.2.2-ubuntu-18.04-adoptopenjdk11","7.2-ubuntu-18.04-adoptopenjdk11","7.16.0-beta2","7.16.0-beta2-adoptopenjdk11","7.16.0-beta2-ubuntu-18.04-adoptopenjdk11","7.16.0-beta2-ubuntu","7.16.0-beta2-jdk11","7.16.0-m49-ubuntu-18.04-adoptopenjdk11","7.16.0-m49-jdk11","7.16.0-m49-adoptopenjdk11","7.16.0-m49","7.16.0-m49-ubuntu","7.16.0-m39","7.16.0-m39-adoptopenjdk11","7.16.0-m39-ubuntu-18.04-adoptopenjdk11","7.16.0-m39-ubuntu","7.16.0-m39-jdk11","7.16.0-m30-ubuntu","7.16.0-m30","7.16.0-m30-adoptopenjdk11","7.16.0-m30-jdk11","7.16.0-m30-ubuntu-18.04-adoptopenjdk11","7.15.0-rc1-adoptopenjdk11","7.15.0-rc1-ubuntu-18.04-adoptopenjdk11","7.15.0-rc1-ubuntu","7.15.0-rc1","7.15.0-rc1-jdk11","7.15.0-beta3-jdk11","7.15.0-beta3-adoptopenjdk11","7.15.0-beta3","7.15.0-beta3-ubuntu-18.04-adoptopenjdk11","7.15.0-beta3-ubuntu","7.15.0-beta2-adoptopenjdk11","7.15.0-beta2-jdk11","7.15.0-beta2-ubuntu","7.15.0-beta2-ubuntu-18.04-adoptopenjdk11","7.15.0-beta2","7.15.0-beta1-ubuntu-18.04-adoptopenjdk11","7.15.0-beta1","7.15.0-beta1-jdk11","7.15.0-beta1-ubuntu","7.15.0-beta1-adoptopenjdk11","7.15.0-m35-jdk11","7.15.0-m35-adoptopenjdk11","7.15.0-m35-ubuntu-18.04-adoptopenjdk11","7.15.0-m35-ubuntu","7.15.0-m35","7.15.0-m27","7.15.0-m27-ubuntu","7.15.0-m27-ubuntu-18.04-adoptopenjdk11","7.15.0-m27-jdk11","7.15.0-m27-adoptopenjdk11","7.15.0-m20-ubuntu","7.15.0-m20","7.15.0-m20-ubuntu-18.04-adoptopenjdk11","7.15.0-m20-jdk11","7.15.0-m20-adoptopenjdk11","7.14.0-rc1-jdk11","7.14.0-rc1","7.14.0-rc1-ubuntu","7.14.0-rc1-ubuntu-18.04-adoptopenjdk11","7.14.0-rc1-adoptopenjdk11","7.14.0-beta1-ubuntu","7.14.0-beta1-jdk11","7.14.0-beta1","7.14.0-beta1-ubuntu-18.04-adoptopenjdk11","7.14.0-beta1-adoptopenjdk11","7.14.0-m176-jdk11","7.14.0-m176-ubuntu-18.04-adoptopenjdk11","7.14.0-m176","7.14.0-m176-ubuntu","7.14.0-m176-adoptopenjdk11","7.13.0-rc1-jdk11","7.13.0-rc1","7.13.0-rc1-adoptopenjdk11","7.13.0-rc1-ubuntu","7.13.0-rc1-ubuntu-18.04-adoptopenjdk11","7.13.0-beta-jdk11","7.13.0-beta","7.13.0-beta-ubuntu","7.13.0-beta-ubuntu-18.04-adoptopenjdk11","7.13.0-beta-adoptopenjdk11","7.13.0-m17","7.13.0-m17-adoptopenjdk11","7.13.0-m17-jdk11","7.13.0-m17-ubuntu-18.04-adoptopenjdk11","7.13.0-m17-ubuntu","7.13.0-m16-adoptopenjdk11","7.13.0-m16","7.13.0-m16-ubuntu","7.13.0-m16-ubuntu-18.04-adoptopenjdk11","7.13.0-m16-jdk11","6.13.20-ubuntu","6.5.2-jdk8","6.14.0-jdk8","6.15.7-jdk8","6.3.2-jdk8","6.6.8-adoptopenjdk8","6.11.1","6.6.15-ubuntu","6.1.1","6.13.10","6.8.3-ubuntu-18.04-adoptopenjdk8","6.5.2-ubuntu-18.04-adoptopenjdk8","6.13.20-adoptopenjdk8","6.14.0-ubuntu","6.15.7-ubuntu","6.6.8-ubuntu-18.04-adoptopenjdk8","6.3.2-ubuntu-18.04-adoptopenjdk8","6.11.1-ubuntu","6.6.15-jdk8","6.1.1-jdk8","6.13.10-adoptopenjdk8","6.8.3-adoptopenjdk8","6.5.2-adoptopenjdk8","6.13.20-ubuntu-18.04-adoptopenjdk8","6.14.0-ubuntu-18.04-adoptopenjdk8","6.15.7-adoptopenjdk8","6.6.8-ubuntu","6.3.2-adoptopenjdk8","6.6.15-ubuntu-18.04-adoptopenjdk8","6.8.3","6.13.3-ubuntu-18.04-adoptopenjdk8","6.13.3-adoptopenjdk8","6.6.0","6.13.3-ubuntu","6.6.0-ubuntu-18.04-adoptopenjdk8","6.13.3-jdk8","6.6.0-adoptopenjdk8","6.13.3","6.6.0-jdk8","6.6.0-ubuntu","6.13.4-ubuntu","6.13.4-jdk8","6.6.2","6.6.1-jdk8","6.13.4","6.6.2-adoptopenjdk8","6.6.1-ubuntu-18.04-adoptopenjdk8","6.13.4-ubuntu-18.04-adoptopenjdk8","6.6.2-ubuntu-18.04-adoptopenjdk8","6.6.1-ubuntu","6.13.4-adoptopenjdk8","6.6.2-ubuntu","6.6.1","6.6.2-jdk8","6.6.1-adoptopenjdk8","6.13.5-ubuntu-18.04-adoptopenjdk8","6.13.5-jdk8","6.6.3-ubuntu-18.04-adoptopenjdk8","6.6.10-jdk8","6.13.5","6.6.3-jdk8","6.6.10","6.13.5-adoptopenjdk8","6.6.3-adoptopenjdk8","6.6.10-ubuntu-18.04-adoptopenjdk8","6.13.5-ubuntu","6.6.3-ubuntu","6.6.10-ubuntu","6.6.3","6.6.10-adoptopenjdk8","6.13.6-ubuntu-18.04-adoptopenjdk8","6.13.6-ubuntu","6.6.4-jdk8","6.6.11","6.13.6-jdk8","6.6.4-adoptopenjdk8","6.6.11-adoptopenjdk8","6.13.6-adoptopenjdk8","6.6.11-jdk8","6.6.4-ubuntu-18.04-adoptopenjdk8","6.13.6","6.6.11-ubuntu","6.6.4","6.6.11-ubuntu-18.04-adoptopenjdk8","6.6.4-ubuntu","6.13.7-ubuntu-18.04-adoptopenjdk8","6.15.2-ubuntu","6.8.0-ubuntu","6.13.7-adoptopenjdk8","6.15.2-adoptopenjdk8","6.6.5-ubuntu-18.04-adoptopenjdk8","6.8.0-adoptopenjdk8","6.6.12-ubuntu-18.04-adoptopenjdk8","6.13.7-ubuntu","6.15.2","6.6.5-ubuntu","6.8.0-ubuntu-18.04-adoptopenjdk8","6.6.12-jdk8","6.13.7","6.15.2-jdk8","6.6.5","6.8.0-jdk8","6.6.12","6.13.7-jdk8","6.15.2-ubuntu-18.04-adoptopenjdk8","6.6.5-jdk8","6.8.0","6.6.12-ubuntu","6.6.5-adoptopenjdk8","6.6.12-adoptopenjdk8","6.13.0-jdk8","6.13.8-ubuntu","6.8.1","6.15.4-ubuntu","6.5.0-ubuntu","6.13.0-adoptopenjdk8","6.13.8","6.8.1-ubuntu-18.04-adoptopenjdk8","6.15.4-adoptopenjdk8","6.6.6-ubuntu","6.6.13-adoptopenjdk8","6.5.0-jdk8","6.13.0-ubuntu","6.13.8-jdk8","6.15.4","6.6.6-ubuntu-18.04-adoptopenjdk8","6.8.1-ubuntu","6.6.13","6.5.0-adoptopenjdk8","6.13.0","6.13.8-adoptopenjdk8","6.6.13-jdk8","6.8.1-jdk8","6.15.4-jdk8","6.6.6-jdk8","6.5.0","6.13.0-ubuntu-18.04-adoptopenjdk8","6.13.8-ubuntu-18.04-adoptopenjdk8","6.6.13-ubuntu-18.04-adoptopenjdk8","6.6.6","6.15.4-ubuntu-18.04-adoptopenjdk8","6.8.1-adoptopenjdk8","6.5.0-ubuntu-18.04-adoptopenjdk8","6.6.13-ubuntu","6.6.6-adoptopenjdk8","6.13.1-ubuntu","6.15.6-ubuntu-18.04-adoptopenjdk8","6.11.0-adoptopenjdk8","6.13.9-jdk8","6.1.0-ubuntu-18.04-adoptopenjdk8","6.5.1-ubuntu-18.04-adoptopenjdk8","6.3.1-jdk8","6.8.2-jdk8","6.13.1-jdk8","6.6.7-ubuntu","6.15.6-ubuntu","6.11.0-ubuntu-18.04-adoptopenjdk8","6.6.14-adoptopenjdk8","6.1.0-adoptopenjdk8","6.13.9","6.5.1-adoptopenjdk8","6.3.1","6.8.2-adoptopenjdk8","6.13.1-adoptopenjdk8","6.6.7-ubuntu-18.04-adoptopenjdk8","6.15.6-jdk8","6.6.14-ubuntu","6.11.0-ubuntu","6.13.9-ubuntu","6.1.0-ubuntu","6.5.1-ubuntu","6.3.1-ubuntu","6.8.2-ubuntu-18.04-adoptopenjdk8","6.13.1-ubuntu-18.04-adoptopenjdk8","6.6.7-adoptopenjdk8","6.15.6-adoptopenjdk8","6.6.14-jdk8","6.13.9-adoptopenjdk8","6.11.0-jdk8","6.1.0-jdk8","6.3.1-ubuntu-18.04-adoptopenjdk8","6.5.1","6.8.2","6.13.1","6.6.7-jdk8","6.15.6","6.6.14-ubuntu-18.04-adoptopenjdk8","6.13.9-ubuntu-18.04-adoptopenjdk8","6.1.0","6.11.0","6.3.1-adoptopenjdk8","6.5.1-jdk8","6.8.2-ubuntu","6.6.7","6.6.14","6.11.1-adoptopenjdk8","6.1.1-adoptopenjdk8","6.5.2-ubuntu","6.8.3-jdk8","6.13.10-ubuntu","6.6.8-jdk8","6.15.7-ubuntu-18.04-adoptopenjdk8","6.14.0-adoptopenjdk8","6.1.1-ubuntu-18.04-adoptopenjdk8","6.11.1-jdk8","6.8.3-ubuntu","6.6.15","6.15.7","6.14.0","6.1.1-ubuntu","6.3.2","6.5.2","6.13.10-ubuntu-18.04-adoptopenjdk8","6.6.8","6.3.2-ubuntu","6.13.10-jdk8","6.11.1-ubuntu-18.04-adoptopenjdk8","6.6.15-adoptopenjdk8","6.13.11-ubuntu-18.04-adoptopenjdk8","6.13.11-ubuntu","6.13.11-jdk8","6.13.11","6.13.11-adoptopenjdk8","6.13.12","6.13.12-jdk8","6.13.12-ubuntu-18.04-adoptopenjdk8","6.13.12-ubuntu","6.13.12-adoptopenjdk8","6.13.13-jdk8","6.13.13-ubuntu","6.13.13","6.13.13-ubuntu-18.04-adoptopenjdk8","6.13.13-adoptopenjdk8","6.13.15-ubuntu","6.13.15-adoptopenjdk8","6.13.15","6.13.15-jdk8","6.13.15-ubuntu-18.04-adoptopenjdk8","6.13.17","6.13.17-adoptopenjdk8","6.13.17-jdk8","6.13.17-ubuntu-18.04-adoptopenjdk8","6.13.17-ubuntu","6.13.18","6.13.18-ubuntu","6.13.18-jdk8","6.13.18-ubuntu-18.04-adoptopenjdk8","6.13.18-adoptopenjdk8","6.13.19-jdk8","6.13.19-ubuntu-18.04-adoptopenjdk8","6.13.19","6.13.19-adoptopenjdk8","6.13.19-ubuntu","6.13.2-adoptopenjdk8","6.13.2-jdk8","6.13.2","6.13.2-ubuntu","6.13.2-ubuntu-18.04-adoptopenjdk8","6.13.20-jdk8","6.13.20","7.13.0-m15-adoptopenjdk11","7.13.0-m15-ubuntu","7.13.0-m15-jdk11","7.13.0-m15","7.13.0-m14-jdk11","7.13.0-m14-ubuntu-18.04-adoptopenjdk11","7.13.0-m14-adoptopenjdk11","7.13.0-m14-ubuntu","7.13.0-m14","7.1.0-ubuntu","7.1.0-jdk11","7.1.0-ubuntu-18.04-adoptopenjdk11","7.1.0","7.1.0-adoptopenjdk11","7.1.1-ubuntu","7.1.1-adoptopenjdk11","7.1.1-ubuntu-18.04-adoptopenjdk11","7.1.1-jdk11","7.1.1","7.13.0-m13-adoptopenjdk11","7.13.0-m13","7.13.0-m13-ubuntu-18.04-adoptopenjdk11","7.13.0-m13-ubuntu","7.13.0-m13-jdk11","7.1.2-ubuntu-18.04-adoptopenjdk11","6.4.2-jdk8","6.7.3-adoptopenjdk8","6.12.3-jdk8","6.15.10","6.10.1","6.0.6-jdk8","6.4.2-adoptopenjdk8","6.7-jdk8","6.15.10-adoptopenjdk8","6.2.3","6.0.6-ubuntu-18.04-adoptopenjdk8","6.7-adoptopenjdk8","7.0.2-ubuntu","6-jdk8","6.2.3-jdk8","6.0.6-adoptopenjdk8","7.0.2-jdk8","6.7","6.12.4-jdk8","6","6.2.3-ubuntu","6.10.2-ubuntu","6.0.6","6.4-adoptopenjdk8","7.0.2","6.7-ubuntu-18.04-adoptopenjdk8","6.12-ubuntu","6.15-ubuntu","6.2.3-ubuntu-18.04-adoptopenjdk8","6.10.2-adoptopenjdk8","6.0.6-ubuntu","6.4","7.0.2-ubuntu-18.04-adoptopenjdk8","6.12.4-ubuntu","6.7.3-ubuntu","6.2.3-adoptopenjdk8","6.10.2","6.4-ubuntu","7.0.2-adoptopenjdk8","6.12-adoptopenjdk8","6.10.2-ubuntu-18.04-adoptopenjdk8","6.4.3","6.12.4-adoptopenjdk8","6.0-jdk8","6.4.3-ubuntu-18.04-adoptopenjdk8","6.10.2-jdk8","6.12.4","6.2.4-jdk8","6.4.3-jdk8","6.0-ubuntu","6.12.4-ubuntu-18.04-adoptopenjdk8","7.0.3-adoptopenjdk8","6.2.4","6.0","6.4.3-adoptopenjdk8","6.12","7.0.3-jdk8","6.2.4-ubuntu","6.0.7-jdk8","6.10-adoptopenjdk8","6.4-ubuntu-18.04-adoptopenjdk8","6.12-jdk8","7.0.3-ubuntu-18.04-adoptopenjdk8","6.10.3","6.0.7-ubuntu-18.04-adoptopenjdk8","6.2-jdk8","6.4-jdk8","6.12-ubuntu-18.04-adoptopenjdk8","6.0-adoptopenjdk8","6.2","6.10.3-ubuntu","7.0.3","6.4.3-ubuntu","6.0-ubuntu-18.04-adoptopenjdk8","6.2-ubuntu-18.04-adoptopenjdk8","6.10.3-jdk8","7.0.3-ubuntu","6.0.7-adoptopenjdk8","6.2-adoptopenjdk8","6.10.3-ubuntu-18.04-adoptopenjdk8","6.0.7","6.10-ubuntu-18.04-adoptopenjdk8","6.2.4-ubuntu-18.04-adoptopenjdk8","6.0.7-ubuntu","6.10","6.2.4-adoptopenjdk8","7.0.4-jdk8","6.10.3-adoptopenjdk8","6.2-ubuntu","7.0.4","6.10-ubuntu","7.0.4-ubuntu-18.04-adoptopenjdk8","6.10-jdk8","7.0.4-adoptopenjdk8","7.0.4-ubuntu","7.0.5-adoptopenjdk8","7.0-adoptopenjdk8","7.0.5-ubuntu","7.0-ubuntu","7.0.5-jdk8","7.0.5-ubuntu-18.04-adoptopenjdk8","7.0","7.0-ubuntu-18.04-adoptopenjdk8","7.0-jdk8","7.0.5","7.13.0-m12-jdk11","7.13.0-m12-ubuntu-18.04-adoptopenjdk11","7.13.0-m12","7.13.0-m12-ubuntu","7.13.0-m12-adoptopenjdk11","6.14.1-adoptopenjdk8","6.14.1-ubuntu-18.04-adoptopenjdk8","6.14.1-ubuntu","6.8-ubuntu","6.14.1","6.8-adoptopenjdk8","6.14.1-jdk8","6.8.5-jdk8","6.8.5","6.8.5-ubuntu","6.8-ubuntu-18.04-adoptopenjdk8","6.1.2-adoptopenjdk8","6.14.2-ubuntu","6.8-jdk8","6.1.2-ubuntu","6.14.2-adoptopenjdk8","6.8","6.1.2","6.14.2","6.8.5-ubuntu-18.04-adoptopenjdk8","6.1.2-ubuntu-18.04-adoptopenjdk8","6.14.2-jdk8","6.8.5-adoptopenjdk8","6.1.2-jdk8","6.14.2-ubuntu-18.04-adoptopenjdk8","6.3.3-adoptopenjdk8","6.1.3-ubuntu","6.14-adoptopenjdk8","6.3.3-jdk8","6.1.3-ubuntu-18.04-adoptopenjdk8","6.3.3-ubuntu-18.04-adoptopenjdk8","6.14","6.1.3-jdk8","6.3.3-ubuntu","6.14-ubuntu-18.04-adoptopenjdk8","6.1.3","6.9.0-ubuntu-18.04-adoptopenjdk8","6.3.3","6.14.3-ubuntu-18.04-adoptopenjdk8","6.1.3-adoptopenjdk8","6.9.0-adoptopenjdk8","6.0.1-adoptopenjdk8","6.14-ubuntu","6.9.0-jdk8","6.0.1-ubuntu","6.14-jdk8","6.9.0","6.0.1","6.14.3-ubuntu","6.9.0-ubuntu","6.3.4-jdk8","6.0.1-ubuntu-18.04-adoptopenjdk8","6.1.4-ubuntu-18.04-adoptopenjdk8","6.14.3","6.3-adoptopenjdk8","6.6.16-ubuntu-18.04-adoptopenjdk8","6.6.9-jdk8","6.1-jdk8","6.0.1-jdk8","6.14.3-adoptopenjdk8","6.3.4","6.6.9-ubuntu-18.04-adoptopenjdk8","6.1.4","6.6.16-ubuntu","6.14.3-jdk8","6.3-ubuntu-18.04-adoptopenjdk8","6.6.9","6.1-adoptopenjdk8","6.6.16","6.9.1-adoptopenjdk8","6.6.9-adoptopenjdk8","6.1.4-ubuntu","6.3-jdk8","6.6.16-adoptopenjdk8","6.9.1-ubuntu","6.0.2-adoptopenjdk8","6.6.9-ubuntu","6.3.4-ubuntu","6.6.16-jdk8","6.1-ubuntu","6.0.2","6.9.1-ubuntu-18.04-adoptopenjdk8","6.3","6.0.2-ubuntu","6.1.4-jdk8","6.9.1-jdk8","6.3-ubuntu","6.0.2-jdk8","6.1-ubuntu-18.04-adoptopenjdk8","6.9.1","6.3.4-ubuntu-18.04-adoptopenjdk8","6.7.0-adoptopenjdk8","6.0.2-ubuntu-18.04-adoptopenjdk8","6.1.4-adoptopenjdk8","6.6","6.15.1-jdk8","6.3.4-adoptopenjdk8","6.7.0","6.1","6.6.17-ubuntu","6.15.1-adoptopenjdk8","6.7.0-ubuntu","6.6-jdk8","6.9.3-jdk8","6.15.1-ubuntu","6.7.0-jdk8","6.0.3-ubuntu-18.04-adoptopenjdk8","6.6.17","6.9.3","6.15.1-ubuntu-18.04-adoptopenjdk8","6.7.0-ubuntu-18.04-adoptopenjdk8","6.0.3","6.6.17-ubuntu-18.04-adoptopenjdk8","6.9-adoptopenjdk8","6.15.1","6.0.3-adoptopenjdk8","6.9-jdk8","6.6-ubuntu","6.0.3-jdk8","6.6.17-jdk8","6.9.3-adoptopenjdk8","6.0.3-ubuntu","6.4.0-adoptopenjdk8","6.7.1-jdk8","6.10.0-ubuntu-18.04-adoptopenjdk8","6.6-adoptopenjdk8","6.9-ubuntu-18.04-adoptopenjdk8","6.4.0","6.7.1-adoptopenjdk8","6.10.0-jdk8","6.6.17-adoptopenjdk8","6.9-ubuntu","6.4.0-ubuntu-18.04-adoptopenjdk8","6.7.1-ubuntu","6.15.10-ubuntu","6.10.0-adoptopenjdk8","6.9.3-ubuntu-18.04-adoptopenjdk8","6.6-ubuntu-18.04-adoptopenjdk8","6.4.0-ubuntu","6.7.1","6.15","6.10.0-ubuntu","6.9.3-ubuntu","6.0.4-jdk8","6.4.0-jdk8","6.7.1-ubuntu-18.04-adoptopenjdk8","6-adoptopenjdk8","6.10.0","6.9","6.0.4-adoptopenjdk8","6.0.4-ubuntu","6.15-jdk8","6.0.4-ubuntu-18.04-adoptopenjdk8","6.15.10-ubuntu-18.04-adoptopenjdk8","6.4.1-ubuntu","6.0.4","7.1-ubuntu-18.04-adoptopenjdk11","6.7.2-ubuntu-18.04-adoptopenjdk8","6.4.1-ubuntu-18.04-adoptopenjdk8","6-ubuntu-18.04-adoptopenjdk8","6.7.2-ubuntu","7.1.2","6.10.1-ubuntu-18.04-adoptopenjdk8","6.4.1-adoptopenjdk8","6.15.10-jdk8","6.7.2","6.10.1-ubuntu","7.1","6.4.1-jdk8","6.15-ubuntu-18.04-adoptopenjdk8","6.7.2-jdk8","6.10.1-adoptopenjdk8","6.0.5","7.1.2-ubuntu","7.0.1-jdk8","6.4.1","6.7.2-adoptopenjdk8","6.10.1-jdk8","6.0.5-adoptopenjdk8","7.1-ubuntu","7.0.1","6.0.5-ubuntu-18.04-adoptopenjdk8","7.1.2-jdk11","7.0.1-adoptopenjdk8","6-ubuntu","6.0.5-jdk8","7.0.1-ubuntu","6.15-adoptopenjdk8","6.4.2-ubuntu-18.04-adoptopenjdk8","6.0.5-ubuntu","7.1.2-adoptopenjdk11","7.0.1-ubuntu-18.04-adoptopenjdk8","6.7.3-ubuntu-18.04-adoptopenjdk8","7.1-adoptopenjdk11","7.1-jdk11","6.4.2","6.4.2-ubuntu","6.7.3","6.7.3-jdk8","6.7-ubuntu","6.13","6.13.21-adoptopenjdk8","6.13.21-ubuntu-18.04-adoptopenjdk8","6.13-ubuntu","6.13.21-jdk8","6.13-adoptopenjdk8","6.13-ubuntu-18.04-adoptopenjdk8","6.13.21-ubuntu","6.13-jdk8","6.13.21","6.12.2-jdk8","6.12.3-ubuntu-18.04-adoptopenjdk8","6.12.3-adoptopenjdk8","6.12.3","6.12.3-ubuntu","7.13.0-m09-jdk11","7.13.0-m09","7.13.0-m09-adoptopenjdk11","7.13.0-m09-ubuntu-18.04-adoptopenjdk11","7.13.0-m09-ubuntu","6.11.2-ubuntu","6.11-adoptopenjdk8","6.11.2-adoptopenjdk8","6.11.2-ubuntu-18.04-adoptopenjdk8","6.11.2","6.11-ubuntu-18.04-adoptopenjdk8","6.11.2-jdk8","6.11-jdk8","6.11","6.11-ubuntu","6.15.8-ubuntu-18.04-adoptopenjdk8","6.15.8-ubuntu","6.15.8-adoptopenjdk8","6.15.8","6.15.8-jdk8","6.12.0-jdk8","6.5.3-ubuntu","6.12.0","6.5.3-jdk8","6.5","6.15.9","6.5-ubuntu","6.15.9-ubuntu","6.5-jdk8","6.15.9-jdk8","6.5.3-adoptopenjdk8","6.15.9-adoptopenjdk8","6.5.3-ubuntu-18.04-adoptopenjdk8","6.15.9-ubuntu-18.04-adoptopenjdk8","6.5-ubuntu-18.04-adoptopenjdk8","6.5-adoptopenjdk8","6.12.0-ubuntu","6.5.3","6.12.0-ubuntu-18.04-adoptopenjdk8","6.2.0","6.12.0-adoptopenjdk8","6.2.0-jdk8","6.2.0-adoptopenjdk8","6.2.0-ubuntu-18.04-adoptopenjdk8","6.12.1-ubuntu-18.04-adoptopenjdk8","6.2.0-ubuntu","6.12.1","6.12.1-ubuntu","6.12.1-adoptopenjdk8","6.2.1-jdk8","6.12.1-jdk8","6.2.1-adoptopenjdk8","6.2.1-ubuntu","6.2.1","6.2.1-ubuntu-18.04-adoptopenjdk8","6.12.2-ubuntu","6.12.2","6.12.2-adoptopenjdk8","6.2.2-ubuntu","6.12.2-ubuntu-18.04-adoptopenjdk8","6.2.2-ubuntu-18.04-adoptopenjdk8","6.2.2-adoptopenjdk8","6.2.2","6.2.2-jdk8","7.13.0-m08","7.13.0-m08-adoptopenjdk11","7.13.0-m08-ubuntu-18.04-adoptopenjdk11","7.13.0-m08-jdk11","7.13.0-m08-ubuntu","6.15.2-alpine-adoptopenjdk8","6.15.2-alpine","6.13.0-alpine-adoptopenjdk8","6.13.0-alpine","6.15.4-alpine","6.15.4-alpine-adoptopenjdk8","6.13.1-alpine","6.13.1-alpine-adoptopenjdk8","6.15.6-alpine","6.15.6-alpine-adoptopenjdk8","6.13.10-alpine","6.13.10-alpine-adoptopenjdk8","6.15.7-alpine","6.15.7-alpine-adoptopenjdk8","6.13.11-alpine","6.0.1-alpine","6.15.8-alpine-adoptopenjdk8","6.13.11-alpine-adoptopenjdk8","6.0.1-alpine-adoptopenjdk8","6.15.8-alpine","6.6.9-alpine","6.13.12-alpine-adoptopenjdk8","6.13.3-alpine","6.0.2-alpine","6.12.0-alpine-adoptopenjdk8","6.15.9-alpine-adoptopenjdk8","6.13.12-alpine","6.7.0-alpine-adoptopenjdk8","6.6.0-alpine","6.13.3-alpine-adoptopenjdk8","6.0.2-alpine-adoptopenjdk8","6.12.0-alpine","6.15.9-alpine","6.6.0-alpine-adoptopenjdk8","6.7.0-alpine","6.13.13-alpine","6.0.3-alpine-adoptopenjdk8","6.13.4-alpine-adoptopenjdk8","6.2.0-alpine","6.12.1-alpine-adoptopenjdk8","6.6.2-alpine-adoptopenjdk8","6.6.1-alpine","6.7.1-alpine","6.13.13-alpine-adoptopenjdk8","6.0.3-alpine","6.2.0-alpine-adoptopenjdk8","6.12.1-alpine","6.13.4-alpine","6.6.2-alpine","6.6.1-alpine-adoptopenjdk8","6.7.1-alpine-adoptopenjdk8","6.10.0-alpine-adoptopenjdk8","6.13.15-alpine","6.0.4-alpine","6.13.5-alpine","6.2.1-alpine","6.6.3-alpine","6.12.2-alpine-adoptopenjdk8","6.15.1-alpine-adoptopenjdk8","6.7.2-alpine-adoptopenjdk8","6.6.10-alpine-adoptopenjdk8","6.10.0-alpine","6.13.15-alpine-adoptopenjdk8","6.0.4-alpine-adoptopenjdk8","6.13.5-alpine-adoptopenjdk8","6.2.1-alpine-adoptopenjdk8","6.6.3-alpine-adoptopenjdk8","6.12.2-alpine","6.15.1-alpine","6.7.2-alpine","6.6.10-alpine","7.13.0-m07-jdk11","7.13.0-m07-ubuntu-18.04-adoptopenjdk11","7.13.0-m07-ubuntu","7.13.0-m07-adoptopenjdk11","7.13.0-m07","6.6.4-alpine","6.6.4-alpine-adoptopenjdk8","6.6.5-alpine-adoptopenjdk8","6.6.5-alpine","6.6.6-alpine","6.6.6-alpine-adoptopenjdk8","6.6.7-alpine-adoptopenjdk8","6.6.7-alpine","6.6.8-alpine","6.6.8-alpine-adoptopenjdk8","6.6.9-alpine-adoptopenjdk8","6.4.0-alpine","6.4.0-alpine-adoptopenjdk8","6.4.1-alpine","6.4.1-alpine-adoptopenjdk8","7.13.0-m06-ubuntu-18.04-adoptopenjdk11","7.13.0-m06-adoptopenjdk11","7.13.0-m06","7.13.0-m06-ubuntu","7.13.0-m06-jdk11","6.7-alpine-adoptopenjdk8","6.7.3-alpine-adoptopenjdk8","6.7.3-alpine","6.10.1-alpine","6.7-alpine","6.2.2-alpine","6.10.1-alpine-adoptopenjdk8","6.2.2-alpine-adoptopenjdk8","6.4.2-alpine","6-alpine-adoptopenjdk8","6.4.2-alpine-adoptopenjdk8","6.10.2-alpine","6.15.10-alpine","6.13.17-alpine","6.2.3-alpine-adoptopenjdk8","6.13.6-alpine-adoptopenjdk8","6.12.3-alpine","6.10.2-alpine-adoptopenjdk8","6.15-alpine","6.13.17-alpine-adoptopenjdk8","6.8.0-alpine","6.13.6-alpine","6.2.3-alpine","6.4.3-alpine","6.12.3-alpine-adoptopenjdk8","6.8.0-alpine-adoptopenjdk8","6.15-alpine-adoptopenjdk8","6.6.11-alpine-adoptopenjdk8","6.4-alpine-adoptopenjdk8","6-alpine","6.6.11-alpine","6.10.3-alpine","6.4-alpine","6.13.18-alpine-adoptopenjdk8","6.15.10-alpine-adoptopenjdk8","6.12-alpine","6.2-alpine","6.13.7-alpine-adoptopenjdk8","6.10-alpine","6.4.3-alpine-adoptopenjdk8","6.8.1-alpine-adoptopenjdk8","6.13.18-alpine","6.12-alpine-adoptopenjdk8","6.13.7-alpine","6.10.3-alpine-adoptopenjdk8","6.2.4-alpine","6.8.1-alpine","6.6.12-alpine-adoptopenjdk8","6.12.4-alpine-adoptopenjdk8","6.10-alpine-adoptopenjdk8","6.2-alpine-adoptopenjdk8","6.6.12-alpine","6.12.4-alpine","6.13.19-alpine-adoptopenjdk8","6.2.4-alpine-adoptopenjdk8","6.13.8-alpine","6.8.2-alpine-adoptopenjdk8","6.13.19-alpine","6.13.8-alpine-adoptopenjdk8","6.5.0-alpine-adoptopenjdk8","6.8.2-alpine","6.6.13-alpine","6.5.0-alpine","6.6.13-alpine-adoptopenjdk8","6.13.2-alpine","6.11.0-alpine-adoptopenjdk8","6.13.9-alpine-adoptopenjdk8","6.8.3-alpine-adoptopenjdk8","6.13.2-alpine-adoptopenjdk8","6.11.0-alpine","6.3.1-alpine","6.13.9-alpine","6.5.1-alpine","6.8.3-alpine","6.6.14-alpine","6.3.1-alpine-adoptopenjdk8","6.6.14-alpine-adoptopenjdk8","6.5.1-alpine-adoptopenjdk8","6.13.20-alpine","6.11.1-alpine","6.14.0-alpine","6.8-alpine-adoptopenjdk8","6.11.1-alpine-adoptopenjdk8","6.13.20-alpine-adoptopenjdk8","6.3.2-alpine-adoptopenjdk8","6.14.0-alpine-adoptopenjdk8","6.6.15-alpine","6.5.2-alpine-adoptopenjdk8","6.8.5-alpine","6.3.2-alpine","6.6.15-alpine-adoptopenjdk8","6.8.5-alpine-adoptopenjdk8","6.5.2-alpine","6.13.21-alpine-adoptopenjdk8","6.11.2-alpine","6.8-alpine","6.14.1-alpine-adoptopenjdk8","6.13.21-alpine","6.3.3-alpine-adoptopenjdk8","6.11-alpine","6.6.16-alpine","6.14.1-alpine","6.5-alpine","6.13-alpine","6.3.3-alpine","6.11.2-alpine-adoptopenjdk8","6.6.16-alpine-adoptopenjdk8","6.5-alpine-adoptopenjdk8","6.13-alpine-adoptopenjdk8","6.11-alpine-adoptopenjdk8","6.5.3-alpine","6.14.2-alpine","6.3-alpine","6.5.3-alpine-adoptopenjdk8","6.6-alpine-adoptopenjdk8","6.9.0-alpine-adoptopenjdk8","6.14.2-alpine-adoptopenjdk8","6.3-alpine-adoptopenjdk8","6.6.17-alpine","6.9.0-alpine","6.3.4-alpine-adoptopenjdk8","6.6-alpine","6.6.17-alpine-adoptopenjdk8","6.3.4-alpine","6.14.3-alpine","6.9.1-alpine","6.14-alpine-adoptopenjdk8","6.9.1-alpine-adoptopenjdk8","6.1-alpine","6.14.3-alpine-adoptopenjdk8","6.14-alpine","6.9-alpine","6.9.3-alpine-adoptopenjdk8","6.9.3-alpine","6.9-alpine-adoptopenjdk8","6.1.3-alpine","6.1.4-alpine","6.1.4-alpine-adoptopenjdk8","6.1-alpine-adoptopenjdk8","6.1.3-alpine-adoptopenjdk8","6.1.2-alpine-adoptopenjdk8","6.1.2-alpine","6.0.5-alpine-adoptopenjdk8","6.0.5-alpine","6.0.6-alpine-adoptopenjdk8","6.0.6-alpine","7.13.0-m04-jdk11","7.13.0-m04","7.13.0-m04-ubuntu-18.04-adoptopenjdk11","7.13.0-m04-ubuntu","7.13.0-m04-adoptopenjdk11","7.13.0-m03-ubuntu-18.04-adoptopenjdk11","7.13.0-m03","7.13.0-m03-ubuntu","7.13.0-m03-jdk11","7.13.0-m03-adoptopenjdk11","7.13.0-m02-jdk11","7.13.0-m02-adoptopenjdk11","7.13.0-m02-ubuntu","7.13.0-m02","7.13.0-m02-ubuntu-18.04-adoptopenjdk11","6.0.7-alpine","6.0-alpine","6.0-alpine-adoptopenjdk8","6.0.7-alpine-adoptopenjdk8","6.1.0-alpine","6.1.0-alpine-adoptopenjdk8","6.1.1-alpine","6.1.1-alpine-adoptopenjdk8"],"bamboo":["latest","9.4-ubuntu","9.4-jdk11","9.4.3-ubuntu","9.4","9.4.3","9.4.3-jdk11","ubuntu","jdk11","9-ubuntu","9-jdk11","9.5-ubuntu","9.5-jdk11","9.5.1-ubuntu","9.5.1-jdk11","9.5.1","9.5","9","9.2-ubuntu","9.2-jdk11","9.2","9.2.11-jdk11","9.2.11-ubuntu","9.2.11","9.3.3-ubuntu","9.3.3-jdk11","9.3.3","9.2.4-ubuntu","9.2.4-jdk11","9.3.2-ubuntu","9.3.2-jdk11","9.0-ubuntu","9.2.4","9.3.2","9.0-jdk11","9.0.4-ubuntu","9.0.4-jdk11","9.0.4","9.0","8.2.6-ubuntu","8.2.6","8.2.6-jdk11","8.1.11-ubuntu","8.1.11-jdk11","8.1.11","8.0.0-jdk11","8.0.0-ubuntu","8.1.8-ubuntu","9.3.4-ubuntu","8.1.8-jdk11","9.3.4-jdk11","8.1.8","8.0.0","8.0.4-ubuntu","9.3.4","8.0.4-jdk11","9.2.5-ubuntu","9.2.5-jdk11","8.0.4","9.2.5","9.1.0-ubuntu","9.1.0-jdk11","9.1.0","8.2.7-ubuntu","8.2.7-jdk11","8.2.7","8.1-ubuntu","8.1-jdk11","8.1.12-ubuntu","8.1.12-jdk11","8.1","8.1.12","8.0.5-ubuntu","8.0.5-jdk11","8.0.1-ubuntu","9.1.1-jdk11","8.1.9-ubuntu","8.0.1-jdk11","8.0.1","8.1.9-jdk11","9.1.1","8.1.9","8.2.8-ubuntu","8.2.8-jdk11","8.0.5","8.2.8","8.1.2-ubuntu","8.1.2-jdk11","8.1.2","8.0.10-ubuntu","9.3.5-ubuntu","8.0.10-jdk11","9.3.5-jdk11","8.0.10","9.3.5","9.2.6-jdk11","9.2.6-ubuntu","9.2.6","9.1.1-ubuntu","8.2.0-ubuntu","8.2.0-jdk11","9.1.2-ubuntu","8.2.0","9.1.2-jdk11","8.0.6-ubuntu","8.0.6-jdk11","9.1.2","8.0.6","8-ubuntu","8-jdk11","8.2-ubuntu","8.2-jdk11","8.2.9-ubuntu","8.2.9-jdk11","8.2.9","8.2","8.1.3-ubuntu","8.1.3-jdk11","8.1.3","8.0.11-jdk11","8.0.11-ubuntu","8.0.11","9.3-ubuntu","8","9.3-jdk11","9.3.6-ubuntu","9.3.6-jdk11","9.3.6","9.3","9.2.7-ubuntu","9.2.7-jdk11","9.2.7","8.2.1-ubuntu","8.2.1-jdk11","8.2.1","8.0.7-ubuntu","9.4.0-jdk11","8.0.7-jdk11","9.2.8-ubuntu","8.0.7","9.2.8-jdk11","9.2.8","9.1-ubuntu","9.1-jdk11","9.1.3-ubuntu","9.1.3-jdk11","9.1.3","9.1","9.0.0-jdk11","9.0.0-ubuntu","9.0.0","8.1.4-ubuntu","8.1.4-jdk11","8.1.4","8.0.8-ubuntu","8.0.8-jdk11","8.0.8","8.0.12-ubuntu","8.0.12","8.0.12-jdk11","8.2.2-ubuntu","8.2.2-jdk11","8.1.5-jdk11","8.2.2","8.1.5-ubuntu","8.1.5","8.0-ubuntu","8.0-jdk11","8.0.13-jdk11","8.0.13-ubuntu","8.0.13","9.4.1-ubuntu","8.0","9.4.1-jdk11","9.4.1","9.2.9-ubuntu","9.2.9","9.2.9-jdk11","9.2.1-ubuntu","9.2.1-jdk11","9.2.1","9.0.1-ubuntu","9.0.1-jdk11","9.0.1","8.2.3-ubuntu","8.2.3-jdk11","8.2.3","9.4.2-jdk11","8.0.9-ubuntu","8.0.9-jdk11","9.0.2-ubuntu","8.0.9","9.0.2-jdk11","9.0.2","8.1.6-ubuntu","8.1.6-jdk11","8.1.6","8.0.2-ubuntu","8.0.2-jdk11","8.0.2","7-ubuntu","7-jdk8","7.2-ubuntu","7.2-jdk8","7.2.7-jdk8","7.2.7-ubuntu","7.2.7","7.2.5-ubuntu","7.2.5-jdk8","7.2.5","7.2.3-ubuntu","7.2.3-jdk8","7.2.3","7.2.10-ubuntu","7.2.10-jdk8","7.2.10","7.2.0-ubuntu","7.2.0-jdk8","7.2.0","7.2","7.1.3-ubuntu","7.1.3-jdk8","7.1.3","7","9.3.0-ubuntu","9.3.0-jdk11","9.3.0","9.2.10-ubuntu","9.2.10-jdk11","9.2.10","8.2.4-ubuntu","8.2.4-jdk11","8.2.4","8.1.1-ubuntu","8.1.1-jdk11","8.1.1","8.1.7-ubuntu","8.1.7-jdk11","8.1.7","8.0.3-ubuntu","8.0.3-jdk11","8.0.3","jdk17","9-jdk17","9.5-jdk17","9.5.0-ubuntu","9.5.0-jdk17","9.5.0-jdk11","9.5.0","9.4-jdk17","9.4.2-ubuntu","9.4.0-ubuntu","9.4.2-jdk17","9.4.2","9.4.0","9.4.0-jdk17","9.3.1-ubuntu","9.3.1-jdk11","9.3.1","9.2.3-ubuntu","9.2.3-jdk11","9.2.3","9.0.3-ubuntu","9.0.3-jdk11","9.0.3","8.2.5-ubuntu","8.2.5-jdk11","8.2.5","8.1.10-ubuntu","8.1.10-jdk11","8.1.10","7.2.9-ubuntu","7.2.9","7.2.9-jdk8","7.2.6-ubuntu","7.2.6-jdk8","7.2.6","7.2.4-ubuntu","7.2.4-jdk8","7.2.4","7.2.2-ubuntu","7.2.2-jdk8","7.2.2","7.2.1-ubuntu","7.2.1-jdk8","7.2.1","7.1-ubuntu","7.1-jdk8","7.1.4-ubuntu","7.1.4-jdk8","7.1.4","7.1.2-ubuntu","7.1.2-jdk8","7.1.2","7.1.1-ubuntu","7.1.1-jdk8","7.1.1","7.1","9.4.1-jdk17","eap-jdk8","9.0.0-rc1-ubuntu","9.0.0-rc1-jdk11","9.0.0-rc1","eap-jdk11","eap-ubuntu","9.0.0-rc1-jdk8","eap","8.0.0-rc4","8.0.0-rc1","7.0.6","7.0","7.1.0-rc1","7.0.4","6.10","6.10.6","7.0.3","6.10.5","7.0.2","7.0.1","7.0.0-rc2","7.0.0-rc1","6.10.4","6.10.3","6.10.2","6.9.2","6.9","6.8.3","6.8","6.9.1","6.8.2","6.9.0","6.9.0-rc1","6.7","6.7.3","6.8.1","6.8.0","6.7.2","6.7.1","6.6.3","6.6","6.7.0-rc2","6.6.2","6.6.1","6.6.0"],"bitbucket":["8.2.1-ubuntu-jdk11","8.2.1-jdk11","8.2.1","8.16.2-ubuntu-jdk17","8.16.2-jdk17","8.16.2","8.1.1-ubuntu-jdk11","8.1.1-jdk11","8.1.1","8.8-ubuntu-jdk17","8.8-jdk17","8.8.7-ubuntu-jdk17","8.8.7-jdk17","8.8.7","8.8","8.3.2-ubuntu-jdk11","7.21.5-ubuntu-jdk11","8.3.2-jdk11","7.21.5-jdk11","8.3.2","8.2.2-ubuntu-jdk11","7.21.5","8.2.2-jdk11","8.2.2","8.1.2-ubuntu-jdk11","8.1.2-jdk11","8.1.2","7.21.2-ubuntu-jdk11","7.21.2-jdk11","7.21.2","8.3.3-ubuntu-jdk11","8.3.3-jdk11","8.3.3","8.16-ubuntu-jdk17","8.16-jdk17","8.16.3-ubuntu-jdk17","8.16.3-jdk17","8.16.3","8.16","8.14.2-ubuntu-jdk17","8.15.1-ubuntu-jdk17","8.15.1-jdk17","8.14.2-jdk17","8.14.3-ubuntu-jdk17","8.15.1","8.14.2","8.14.3-jdk17","8.9.4-ubuntu-jdk17","8.9.4-jdk17","8.14.4-ubuntu-jdk17","8.14.3","8.9.4","8.14.4-jdk17","8.14.5-ubuntu-jdk17","8.14.4","8.8.1-ubuntu-jdk17","8.8.1-jdk17","8.14-ubuntu-jdk17","8.8.1","8.14-jdk17","8.14.5-jdk17","8.11.3-ubuntu-jdk17","8.11.3-jdk17","8.15.0-ubuntu-jdk17","8.14.5","8.15.0-jdk17","8.11.3","8.14","8.0.1-ubuntu-jdk11","8.15.0","8.0.1-jdk11","8.0.1","8.7.0-ubuntu-jdk11","8.7.0-jdk11","8.7.0","8.10.5-ubuntu-jdk17","8.10.5-jdk17","8.10.5","7.21.14-ubuntu-jdk11","7.21.14-jdk11","8.9.0-ubuntu-jdk17","7.21.14","8.9.0-jdk17","8.9.0","8.5-ubuntu-jdk11","8.5-jdk11","8.5.4-ubuntu-jdk11","8.5.4-jdk11","8.5.4","8.5","8.4.3-ubuntu-jdk11","8.4.3-jdk11","8.4.3","7.21.6-ubuntu-jdk11","7.21.6-jdk11","7.21.6","7.21.20-ubuntu-jdk11","7.21.20-jdk11","7.21.20","7.21.1-ubuntu-jdk11","7.21.1-jdk11","7.21.1","8.3-ubuntu-jdk11","8.3-jdk11","8.3.4-ubuntu-jdk11","8.3.4-jdk11","8.3.4","8.3","8.2.3-ubuntu-jdk11","8.2.3-jdk11","8.2.3","8.15.2-ubuntu-jdk17","8.15.2-jdk17","8.15.2","8.1.3-ubuntu-jdk11","8.1.3-jdk11","8.1.3","8.9.5-ubuntu-jdk17","8.9.5-jdk17","8.10.0-ubuntu-jdk17","8.10.0-jdk17","8.10.0","8.0.2-ubuntu-jdk11","8.0.2-jdk11","8.0.2","7.20.0-ubuntu-jdk11","7.20.0-jdk11","7.20.0","8.9.5","8.8.2-ubuntu-jdk17","8.8.2-jdk17","8.8.2","8.17.0-ubuntu-jdk17","8.17.0-jdk17","8.17.0","8.11.4-ubuntu-jdk17","8.11.4-jdk17","8.11.4","8.10-ubuntu-jdk17","8.10-jdk17","8.10.6-ubuntu-jdk17","8.10.6-jdk17","8.10.6","8.10","8.9.1-ubuntu-jdk17","8.9.1-jdk17","8.7.1-ubuntu-jdk11","8.9.1","8.7.1-jdk11","8.7.1","8.4-ubuntu-jdk11","8.4-jdk11","8.4.4-ubuntu-jdk11","8.4.4-jdk11","8.4.4","8.4","8.12.1-ubuntu-jdk17","8.12.1-jdk17","8.12.1","7.21.7-ubuntu-jdk11","7.21.7-jdk11","7.21.7","7.21.21-ubuntu-jdk11","7.21.21-jdk11","7.21.21","7.21.15-ubuntu-jdk11","7.21.15-jdk11","7.21.15","7.21.10-jdk11","7.21.10-ubuntu-jdk11","7.21.10","8.2-ubuntu-jdk11","8.2-jdk11","8.2.4-ubuntu-jdk11","8.2.4-jdk11","8.2.4","8.2","8.15.3-ubuntu-jdk17","8.15.3-jdk17","8.15.3","8.12-ubuntu-jdk17","8.12-jdk17","8.12.6-ubuntu-jdk17","8.12.6-jdk17","8.12.6","8.12","8.6.0-ubuntu-jdk11","8.6.0","8.6.0-jdk11","8.1.4-ubuntu-jdk11","8.1.4-jdk11","8.1.4","8.13.4-ubuntu-jdk17","8.13.4-jdk17","8.13.4","8.0.3-ubuntu-jdk11","8.0.3-jdk11","8.0.3","7.20.1-ubuntu-jdk11","8.9.6-ubuntu-jdk17","7.20.1-jdk11","8.9.6-jdk17","7.20.1","8.9.6","8.17-ubuntu-jdk17","8.17-jdk17","8.17.1-ubuntu-jdk17","8.17.1-jdk17","8.17.1","8.17","8.11.5-ubuntu-jdk17","8.11.5-jdk17","8.11.5","8.10.1-ubuntu-jdk17","8.10.1-jdk17","8.10.1","8.8.3-ubuntu-jdk17","8.8.3-jdk17","8.8.3","8.7.2-ubuntu-jdk11","8.7.2-jdk11","8.7.2","7-ubuntu-jdk11","7-jdk11","7.21-ubuntu-jdk11","7.21-jdk11","7.21.8-ubuntu-jdk11","7.21.8-jdk11","7.21.8","7.21.22-ubuntu-jdk11","7.21.22-jdk11","7.21.22","7.21","7","8.9-ubuntu-jdk17","8.9-jdk17","8.9.10-ubuntu-jdk17","8.9.10-jdk17","8.9.10","8.9","8.12.2-ubuntu-jdk17","8.12.2-jdk17","8.12.2","7.21.16-ubuntu-jdk11","7.21.16-jdk11","7.21.16","8.6.1-ubuntu-jdk11","8.6.1-jdk11","8.6.1","8.4.0-ubuntu-jdk11","8.4.0-jdk11","8.4.0","8.15-ubuntu-jdk17","8.15-jdk17","8.15.4-ubuntu-jdk17","8.15.4-jdk17","8.15.4","8.15","8.13.5-ubuntu-jdk17","8.13.5-jdk17","8.13.5","8.0.4-ubuntu-jdk11","8.0.4-jdk11","8.0.4","7.21.11-ubuntu-jdk11","7.21.11-jdk11","7.21.11","7.20.2-ubuntu-jdk11","7.20.2-jdk11","7.20.2","8.9.7-ubuntu-jdk17","8.9.7-jdk17","8.9.7","8.5.0-ubuntu-jdk11","8.5.0-jdk11","8.5.0","8.1-ubuntu-jdk11","8.1-jdk11","8.1.5-ubuntu-jdk11","8.1.5-jdk11","8.1.5","8.11.0-ubuntu-jdk17","8.11.0-jdk17","8.11.0","8.1","8.8.4-ubuntu-jdk17","8.8.4-jdk17","8.8.4","8.7.3-ubuntu-jdk11","8.13.0-ubuntu-jdk17","8.13.0-jdk17","8.13.0","8.11-ubuntu-jdk17","8.11-jdk17","8.11.6-ubuntu-jdk17","8.11.6-jdk17","8.11.6","8.11","8.10.2-ubuntu-jdk17","8.10.2-jdk17","latest","8.10.2","jdk17","8-ubuntu-jdk17","8-jdk17","8.7.3-jdk11","8.7.3","8.18.0-ubuntu-jdk17","8.12.3-ubuntu-jdk17","8.12.3-jdk17","8.12.3","8","7.21.9-ubuntu-jdk11","7.21.9-jdk11","7.21.9","ubuntu-jdk17","7.21.17-ubuntu-jdk11","8.6.2-ubuntu-jdk11","8.6.2-jdk11","8.6.2","8.4.1-ubuntu-jdk11","8.4.1-jdk11","8.4.1","8.3.0-ubuntu-jdk11","8.3.0-jdk11","8.3.0","8.18-ubuntu-jdk17","8.18-jdk17","8.18.0-jdk17","8.18.0","8.18","8.13-ubuntu-jdk17","8.13-jdk17","8.13.6-ubuntu-jdk17","8.13.6-jdk17","8.13.6","8.13","8.0-ubuntu-jdk11","8.0-jdk11","8.0.5-ubuntu-jdk11","8.0.5-jdk11","8.0.5","8.0","7.21.17-jdk11","7.21.17","7.20-ubuntu-jdk11","7.20-jdk11","7.20.3-ubuntu-jdk11","7.20.3-jdk11","7.20.3","7.20","8.9.8-ubuntu-jdk17","8.9.8-jdk17","8.9.8","8.5.1-ubuntu-jdk11","8.5.1-jdk11","8.5.1","8.11.1-ubuntu-jdk17","8.11.1-jdk17","8.11.1","7.21.3-ubuntu-jdk11","7.21.3-jdk11","7.21.3","7.21.12-ubuntu-jdk11","7.21.12-jdk11","7.21.12","8.9.2-ubuntu-jdk17","8.9.2-jdk17","8.9.2","8.8.5-ubuntu-jdk17","8.8.5-jdk17","8.8.5","8.7.4-ubuntu-jdk11","8.7.4-jdk11","8.7.4","8.13.1-ubuntu-jdk17","8.13.1-jdk17","8.13.1","8.10.3-ubuntu-jdk17","8.6.3-ubuntu-jdk11","8.10.3-jdk17","8.6.3-jdk11","8.10.3","8.6.3","8.16.0-ubuntu-jdk17","8.16.0-jdk17","8.16.0","8.12.4-ubuntu-jdk17","8.12.4-jdk17","8.12.4","8.0.0","7.21.18-ubuntu-jdk11","7.21.18","8.2.0-ubuntu-jdk11","8.2.0-jdk11","8.2.0","8.12.0-ubuntu-jdk17","8.12.0-jdk17","8.12.0","8.0.0-ubuntu-jdk11","8.0.0-jdk11","7.21.4-ubuntu-jdk11","7.21.4-jdk11","7.21.4","7.21.18-jdk11","8.9.9","8.5.2-ubuntu-jdk11","8.5.2-jdk11","8.5.2","8.4.2-ubuntu-jdk11","8.4.2-jdk11","8.4.2","8.3.1-ubuntu-jdk11","8.3.1-jdk11","8.3.1","8.11.2-ubuntu-jdk17","8.11.2-jdk17","8.11.2","8.9.9-ubuntu-jdk17","8.9.9-jdk17","8.14.0-ubuntu-jdk17","8.14.0-jdk17","8.14.0","8.13.2-ubuntu-jdk17","8.13.2-jdk17","8.13.2","7.21.13-ubuntu-jdk11","7.21.13-jdk11","7.21.13","8.7-ubuntu-jdk11","8.7-jdk11","8.7.5-ubuntu-jdk11","8.7.5-jdk11","8.7.5","8.7","8.6-ubuntu-jdk11","8.6-jdk11","8.6.4-ubuntu-jdk11","8.6.4-jdk11","8.6.4","8.6","7.21.0-ubuntu-jdk11","7.21.0-jdk11","7.21.0","8.9.3-ubuntu-jdk17","8.9.3-jdk17","8.9.3","8.8.6-ubuntu-jdk17","8.8.6-jdk17","8.8.6","8.8.0-ubuntu-jdk17","8.8.0-jdk17","8.8.0","8.16.1-ubuntu-jdk17","8.16.1-jdk17","8.16.1","8.12.5-ubuntu-jdk17","8.12.5-jdk17","8.12.5","8.1.0-ubuntu-jdk11","8.1.0-jdk11","8.10.4-ubuntu-jdk17","8.10.4-jdk17","8.10.4","8.1.0","7.21.19-ubuntu-jdk11","7.21.19-jdk11","7.21.19","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.5.3","8.14.1-ubuntu-jdk17","8.14.1-jdk17","8.14.1","8.13.3-ubuntu-jdk17","8.13.3-jdk17","8.13.3","8.15-ubuntu-jdk11","8.15-jdk11","8.15.3-ubuntu-jdk11","8.15.3-jdk11","8.8.0-ubuntu-jdk11","8.8.0-jdk11","8.16.0-ubuntu-jdk11","8.16.0-jdk11","8.8.1-jdk11","8.12.3-ubuntu-jdk11","8.12.3-jdk11","8.8.1-ubuntu-jdk11","8.16.1-ubuntu-jdk11","8.10.0-ubuntu-jdk11","8.10.0-jdk11","8.16.1-jdk11","7.6.9-ubuntu-jdk11","7.6.9-jdk11","8.12.4-ubuntu-jdk11","8.12.4-jdk11","7.6.9","8.8.2-ubuntu-jdk11","8.8.2-jdk11","7.19-ubuntu-jdk11","7.19-jdk11","7.19.5-ubuntu-jdk11","7.19.5-jdk11","7.19.5","7.19","7.7.0-ubuntu-jdk11","8.16-ubuntu-jdk11","8.16-jdk11","8.16.2-ubuntu-jdk11","7.7.0","8.16.2-jdk11","8.10.1-ubuntu-jdk11","8.10.1-jdk11","7.7.0-jdk11","8.12.5-ubuntu-jdk11","8.8.3-ubuntu-jdk11","8.12.5-jdk11","8.8.3-jdk11","7.7-ubuntu-jdk11","7.7-jdk11","7.7.1-ubuntu-jdk11","7.7.1-jdk11","7.7.1","7.7","7.10.0-ubuntu-jdk11","7.10.0-jdk11","7.10.0","8.10.2-ubuntu-jdk11","8.10.2-jdk11","ubuntu-jdk11","jdk11","8-jdk11","8-ubuntu-jdk11","8.17-ubuntu-jdk11","8.17-jdk11","8.17.0-ubuntu-jdk11","8.17.0-jdk11","8.12-ubuntu-jdk11","8.12-jdk11","8.12.6-ubuntu-jdk11","8.12.6-jdk11","7.16.2-ubuntu-jdk11","7.16.2-jdk11","7.16.2","8.8.4-ubuntu-jdk11","8.8.4-jdk11","7.10-ubuntu-jdk11","7.10-jdk11","7.10.1-ubuntu-jdk11","7.10.1-jdk11","7.10.1","7.10","7.17.3-ubuntu-jdk11","7.17.3-jdk11","8.10.3-ubuntu-jdk11","7.17.3","8.10.3-jdk11","7.8.0-ubuntu-jdk11","7.8.0-jdk11","7.8.0","7.16-jdk11","7.16-ubuntu-jdk11","7.16.3-jdk11","7.16.3-ubuntu-jdk11","7.16.3","7.16","8.13.0-ubuntu-jdk11","8.13.0-jdk11","8.8.5-ubuntu-jdk11","8.8.5-jdk11","7.11.1","7.6.13-ubuntu-jdk11","7.6.13-jdk11","7.17.4-ubuntu-jdk11","7.6.13","7.17.4-jdk11","7.17.4","7.11.1-ubuntu-jdk11","7.11.1-jdk11","7.8-ubuntu-jdk11","8.10.4-ubuntu-jdk11","7.8-jdk11","7.8.1-jdk11","7.8.1-ubuntu-jdk11","7.8.1","7.8","8.10.4-jdk11","7.17.0-ubuntu-jdk11","7.17.0-jdk11","7.17.0","8.13.1-ubuntu-jdk11","8.13.1-jdk11","7.6.14-ubuntu-jdk11","7.6.14-jdk11","7.6.14","7.11-ubuntu-jdk11","7.11-jdk11","7.11.2-ubuntu-jdk11","7.11.2-jdk11","8.8.6-ubuntu-jdk11","8.8.6-jdk11","7.11.2","7.11","7.17.5-ubuntu-jdk11","7.17.5-jdk11","7.17.5","7.9.0-ubuntu-jdk11","7.9.0-jdk11","7.9.0","8.10.5-ubuntu-jdk11","8.10.5-jdk11","7.6.15-ubuntu-jdk11","7.6.15-jdk11","7.6.15","7.17.1-ubuntu-jdk11","7.17.1-jdk11","7.17.1","8.13.2-ubuntu-jdk11","8.13.2-jdk11","7.17.6-ubuntu-jdk11","7.17.6-jdk11","7.17.6","7.12.0-jdk11","7.12.0","8.8-ubuntu-jdk11","8.8-jdk11","7.12.0-ubuntu-jdk11","8.8.7-ubuntu-jdk11","8.8.7-jdk11","7.9-ubuntu-jdk11","7.9-jdk11","7.9.1-ubuntu-jdk11","7.9.1-jdk11","7.9.1","7.9","8.10-ubuntu-jdk11","8.10-jdk11","8.10.6-ubuntu-jdk11","8.10.6-jdk11","7.6.16-ubuntu-jdk11","7.6.16-jdk11","7.6.16","7.17.10-ubuntu-jdk11","7.17.10-jdk11","7.17.10","7.17.7-ubuntu-jdk11","7.17.7-jdk11","7.17.7","7.12-ubuntu-jdk11","7.12-jdk11","7.12.1-ubuntu-jdk11","7.12.1-jdk11","7.12","8.13.3-ubuntu-jdk11","8.13.3-jdk11","7.12.1","7.6.17-ubuntu-jdk11","7.6.17-jdk11","7.6.17","7.17.11-ubuntu-jdk11","7.17.11-jdk11","7.17.11","7.17.8-ubuntu-jdk11","7.17.8-jdk11","7.17.8","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.13.4-ubuntu-jdk11","8.13.4-jdk11","8.11.0-ubuntu-jdk11","8.11.0-jdk11","7.6.18-ubuntu-jdk11","7.6.18-jdk11","7.6.18","7.13.0-ubuntu-jdk11","7.13.0-jdk11","7.13.0","7.17.12-ubuntu-jdk11","7.17.12-jdk11","7.17.12","7.17.9-ubuntu-jdk11","7.17.9-jdk11","7.17.9","8.13-ubuntu-jdk11","8.13-jdk11","8.13.5-ubuntu-jdk11","8.13.5-jdk11","7.6.19-ubuntu-jdk11","7.6.19-jdk11","7.6.19","7.13-ubuntu-jdk11","7.13-jdk11","7.13.1-ubuntu-jdk11","7.13.1-jdk11","7.13.1","7.13","8.9.1-ubuntu-jdk11","8.9.1-jdk11","8.11.1-ubuntu-jdk11","8.11.1-jdk11","7.17.13-ubuntu-jdk11","7.17.13-jdk11","7.17.13","7.18.0-ubuntu-jdk11","7.18.0-jdk11","7.18.0","7.6.2-ubuntu-jdk11","7.6.2-jdk11","8.9.2-ubuntu-jdk11","7.6.2","8.9.2-jdk11","8.14.0-ubuntu-jdk11","8.14.0-jdk11","8.11.2-ubuntu-jdk11","8.11.2-jdk11","7.17.14-ubuntu-jdk11","7.17.14-jdk11","7.17.14","7.14.0-ubuntu-jdk11","7.14.0-jdk11","7.14.0","7.6.20-ubuntu-jdk11","7.6.20-jdk11","7.6.20","7.18.1-ubuntu-jdk11","7.18.1-jdk11","7.18.1","7.14.1-ubuntu-jdk11","7.14.1","7.18.2-ubuntu-jdk11","7.17.15-ubuntu-jdk11","7.17.15-jdk11","7.17.15","7.14.1-jdk11","8.9.3-ubuntu-jdk11","8.9.3-jdk11","8.14.1-ubuntu-jdk11","8.14.1-jdk11","8.11.3-ubuntu-jdk11","8.11.3-jdk11","7.6.21-ubuntu-jdk11","7.6.21-jdk11","7.6.21","7.18.2-jdk11","7.18.2","7.14-ubuntu-jdk11","7.14.2-ubuntu-jdk11","7.14.2-jdk11","7.6.22-ubuntu-jdk11","7.6.22-jdk11","7.6.22","7.5.0-ubuntu-jdk11","7.5.0-jdk11","7.5.0","7.18.3","7.17.16-ubuntu-jdk11","7.17.16-jdk11","7.17.16","7.14-jdk11","7.14.2","7.14","8.14.2-ubuntu-jdk11","8.14.2-jdk11","7.18.3-ubuntu-jdk11","7.18.3-jdk11","8.9.4-ubuntu-jdk11","8.9.4-jdk11","8.11.4-ubuntu-jdk11","8.11.4-jdk11","7.6-ubuntu-jdk11","7.6-jdk11","7.6.23-ubuntu-jdk11","7.6.23-jdk11","7.6.23","7.6","7.5.1-ubuntu-jdk11","7.5.1-jdk11","7.5.1","7.17.17-ubuntu-jdk11","7.17.17-jdk11","7.17.17","7.18-ubuntu-jdk11","7.18-jdk11","7.18.4-ubuntu-jdk11","7.18.4-jdk11","7.18.4","7.18","8.14.3-ubuntu-jdk11","7.15.0-jdk11","8.14.3-jdk11","8.11.5-ubuntu-jdk11","8.11.5-jdk11","7.15.0-ubuntu-jdk11","7.15.0","8.9.5-ubuntu-jdk11","8.9.5-jdk11","7.5-ubuntu-jdk11","7.5-jdk11","7.5.2-ubuntu-jdk11","7.5.2-jdk11","7.5.2","7.5","7.17.18-ubuntu-jdk11","7.17.18-jdk11","7.17.18","7.15.1-ubuntu-jdk11","7.15.1-jdk11","7.15.1","8.14-jdk11","8.14.4-jdk11","8.9.6-ubuntu-jdk11","7.6.3-ubuntu-jdk11","7.6.3-jdk11","8.9.6-jdk11","7.6.3","8.14-ubuntu-jdk11","8.14.4-ubuntu-jdk11","8.11-ubuntu-jdk11","8.11-jdk11","8.11.6-ubuntu-jdk11","8.11.6-jdk11","7.19.0-ubuntu-jdk11","7.19.0-jdk11","7.19.0","7.17.19-ubuntu-jdk11","7.17.19-jdk11","7.17.19","7.15.2-ubuntu-jdk11","7.15.2-jdk11","7.15.2","7.6.4-ubuntu-jdk11","7.6.4-jdk11","7.6.4","7.6.0-ubuntu-jdk11","7.6.0-jdk11","7.6.0","8.9.7-ubuntu-jdk11","8.9.7-jdk11","7.19.1-ubuntu-jdk11","7.19.1-jdk11","7.19.1","7.17.2-ubuntu-jdk11","7.17.2","7.17.2-jdk11","7.15-ubuntu-jdk11","7.15-jdk11","7.15.3-ubuntu-jdk11","7.15.3-jdk11","7.15.3","7.15","8.15.0-ubuntu-jdk11","8.15.0-jdk11","8.12.0-ubuntu-jdk11","8.12.0-jdk11","7.6.5-ubuntu-jdk11","7.6.5-jdk11","7.6.5","7.6.1-ubuntu-jdk11","7.6.1-jdk11","7.6.1","8.9.8-ubuntu-jdk11","8.9.8-jdk11","7.19.2-ubuntu-jdk11","7.19.2-jdk11","7.19.2","7.17.20-ubuntu-jdk11","7.17.20-jdk11","7.17.20","7.6.6-ubuntu-jdk11","7.6.6-jdk11","7.6.6","7.6.10-ubuntu-jdk11","7.6.10-jdk11","7.6.10","8.15.1-ubuntu-jdk11","8.15.1-jdk11","8.12.1-ubuntu-jdk11","8.12.1-jdk11","7.19.3-ubuntu-jdk11","7.19.3-jdk11","7.19.3","7.16.0-ubuntu-jdk11","7.16.0-jdk11","8.9-ubuntu-jdk11","7.16.0","8.9-jdk11","8.9.9-ubuntu-jdk11","8.9.9-jdk11","7.6.7-ubuntu-jdk11","7.6.7-jdk11","7.6.7","7.6.11-ubuntu-jdk11","7.6.11-jdk11","7.6.11","7.17-ubuntu-jdk11","7.17-jdk11","7.17.21-ubuntu-jdk11","7.17.21-jdk11","7.17.21","7.17","8.15.2-ubuntu-jdk11","8.15.2-jdk11","8.12.2-ubuntu-jdk11","8.12.2-jdk11","7.19.4-ubuntu-jdk11","7.19.4-jdk11","7.19.4","7.16.1-ubuntu-jdk11","7.16.1-jdk11","7.16.1","7.6.8-ubuntu-jdk11","7.6.8-jdk11","7.6.8","7.6.12-ubuntu-jdk11","7.6.12-jdk11","7.6.12","8.14.0-eap01","8.14.0-eap01-ubuntu-jdk11","8.14.0-eap01-jdk11","7.4-ubuntu-jdk11","7.4-jdk11","7.4.1-ubuntu-jdk11","7.4.1-jdk11","7.4.1","7.4.0-ubuntu-jdk11","7.4.0-jdk11","7.4.0","7.3-ubuntu-jdk11","7.3-jdk11","7.3.2-ubuntu-jdk11","7.3.2-jdk11","7.3.2","7.3","7.4.2-ubuntu-jdk11","7.4.2-jdk11","7.4.2","7.4","7.3.1-ubuntu-jdk11","7.3.1-jdk11","7.3.1","7.3.0-ubuntu-jdk11","7.3.0-jdk11","7.3.0","7.2.5-ubuntu-jdk11","7.2.5-jdk11","7.2.5","7.2.4-ubuntu-jdk11","7.2.4-jdk11","7.2.4","7.2.3-ubuntu-jdk11","7.2.3-jdk11","7.2.3","7.2.2-ubuntu-jdk11","7.2.2-jdk11","7.2.2","7.2.1-ubuntu-jdk11","7.2.1-jdk11","7.2.1","7.2.0-ubuntu-jdk11","7.2.0-jdk11","7.2.0","7.1-ubuntu-jdk11","7.1-jdk11","7.1.4-ubuntu-jdk11","7.1.4-jdk11","7.1.4","7.1.3-ubuntu-jdk11","7.1.3-jdk11","7.1.3","7.1.2-ubuntu-jdk11","7.1.2-jdk11","7.1.2","7.1.1-ubuntu-jdk11","7.1.1-jdk11","7.1.1","7.1.0-ubuntu-jdk11","7.1.0-jdk11","7.1.0","7.1","7.2-ubuntu-jdk11","7.2-jdk11","7.2.6-ubuntu-jdk11","7.2.6-jdk11","7.2.6","7.2","eap-ubuntu-jdk11","eap-jdk11","eap","8.0.0-eap05-ubuntu-jdk11","8.0.0-eap05-jdk11","8.0.0-eap05","7.0-jdk11","7.0-ubuntu-jdk11","7.0.5-ubuntu-jdk11","7.0.5-jdk11","7.0.5","7.0","6.10.17-ubuntu-jdk11","6.10.17","6.10.17-jdk11","6.10","6","6-ubuntu-jdk11","6-jdk11","6.10-ubuntu-jdk11","6.10-jdk11","6.8.2-jdk11","6.10.11-jdk11","6.8.2","6.10.11","6.8.2-ubuntu-jdk11","6.10.11-ubuntu-jdk11","7.0.0-jdk11","7.0.0-ubuntu-jdk11","7.0.0","6.8.3","6.7.1","6.7.1-ubuntu-jdk11","6.10.12-jdk11","6.8.3-ubuntu-jdk11","6.7.1-jdk11","6.10.12","6.8.3-jdk11","6.10.12-ubuntu-jdk11","7.0.1-ubuntu-jdk11","7.0.1-jdk11","7.0.1","6.7.2-jdk11","6.10.3","6.8.4-jdk11","6.7.2","6.10.3-jdk11","6.8","6.10.13-jdk11","6.7.2-ubuntu-jdk11","6.10.3-ubuntu-jdk11","6.8-ubuntu-jdk11","6.10.13-ubuntu-jdk11","6.8-jdk11","6.10.13","6.8.4-ubuntu-jdk11","7.0.2-ubuntu-jdk11","6.8.4","7.0.2","6.7.3-ubuntu-jdk11","7.0.2-jdk11","6.10.4-jdk11","6.7.3","6.10.4-ubuntu-jdk11","6.7.3-jdk11","6.10.14-ubuntu-jdk11","6.10.4","6.10.14","6.10.14-jdk11","7.0.3-jdk11","6.9.0-ubuntu-jdk11","7.0.3","6.7.4-ubuntu-jdk11","6.10.5-jdk11","6.9.0","7.0.3-ubuntu-jdk11","6.7.4","6.10.5-ubuntu-jdk11","6.9.0-jdk11","6.7.4-jdk11","6.10.15-jdk11","6.10.5","6.10.15","6.10.15-ubuntu-jdk11","7.0.4-jdk11","6.9.1-jdk11","6.7-jdk11","7.0.4-ubuntu-jdk11","6.10.7-jdk11","6.9.1","6.7","7.0.4","6.10.7","6.9.1-ubuntu-jdk11","6.7-ubuntu-jdk11","6.10.7-ubuntu-jdk11","6.7.5","6.10.16-jdk11","6.7.5-jdk11","6.7.5-ubuntu-jdk11","6.10.16","6.9.2-ubuntu-jdk11","6.10.16-ubuntu-jdk11","6.10.8","6.9.2-jdk11","6.10.8-ubuntu-jdk11","6.9.2","6.10.8-jdk11","6.9.3-ubuntu-jdk11","6.10.9","6.8.0-jdk11","6.9-jdk11","6.10.9-jdk11","6.8.0-ubuntu-jdk11","6.9.3","6.10.9-ubuntu-jdk11","6.8.0","6.9.3-jdk11","6.9","6.9-ubuntu-jdk11","6.10.2","6.7.0-jdk11","6.8.1-jdk11","6.10.2-jdk11","6.7.0-ubuntu-jdk11","6.8.1","6.10.2-ubuntu-jdk11","6.7.0","6.8.1-ubuntu-jdk11","6.10.0-jdk11","6.10.0-ubuntu-jdk11","6.10.0","6.10.1-jdk11","6.10.1-ubuntu-jdk11","6.10.1","6.10.10-jdk11","6.10.10","6.10.10-ubuntu-jdk11","7.14-ubuntu","7.14-jdk8","7.14.1-ubuntu","7.14-ubuntu-jdk8","7.14.1-jdk8","7.14.1-ubuntu-jdk8","7.11.2-jdk8","7.11-ubuntu","7.11.2-ubuntu","7.11-ubuntu-jdk8","7.11-jdk8","7.11.2-ubuntu-jdk8","7.15.0-jdk8","7.1.2-ubuntu","7.15.0-ubuntu","7.1.2-jdk8","7.15.0-ubuntu-jdk8","7.1.2-ubuntu-jdk8","7.6.4-ubuntu","6.7-jdk8","7.2.2-jdk8","7.6.4-jdk8","7.12.0-jdk8","6.7.5-ubuntu","7.5.1-ubuntu","7.6.4-ubuntu-jdk8","6.7.5-ubuntu-jdk8","7.2.2-ubuntu-jdk8","6.9.0-ubuntu","7.15-ubuntu-jdk8","7.12.0-ubuntu","7.5.1-jdk8","7.1.3-jdk8","7.2.2-ubuntu","6.9.0-jdk8","6.7-ubuntu","7.15.1-ubuntu","7.12.0-ubuntu-jdk8","7.5.1-ubuntu-jdk8","6.9.0-ubuntu-jdk8","6.7.5-jdk8","7.15-ubuntu","7.0.2-ubuntu-jdk8","7.1.3-ubuntu-jdk8","6.7-ubuntu-jdk8","7.15-jdk8","7.0.2-jdk8","7.1.3-ubuntu","7.0.2-ubuntu","7.15.1-jdk8","7.15.1-ubuntu-jdk8","7.2.3-jdk8","7.12.1-ubuntu","7.5.2-ubuntu-jdk8","7.6.5-jdk8","7.2.3-ubuntu","6.9.1-ubuntu","7.5-ubuntu-jdk8","7.6.5-ubuntu-jdk8","7.12.1-jdk8","6.9.1-ubuntu-jdk8","7.1.4-jdk8","7.5-jdk8","7.2.3-ubuntu-jdk8","7.12-jdk8","7.1.4-ubuntu","7.5.2-ubuntu","7.6.5-ubuntu","6.9.1-jdk8","7.0.3-ubuntu-jdk8","7.12-ubuntu-jdk8","6.10.2-jdk8","6.8.0-jdk8","7.12-ubuntu","7.1-ubuntu","7.5.2-jdk8","6.8.0-ubuntu-jdk8","6.10.9-ubuntu-jdk8","7.0.3-jdk8","6.10.2-ubuntu","7.12.1-ubuntu-jdk8","7.1-ubuntu-jdk8","7.5-ubuntu","6.10.9-jdk8","7.0.3-ubuntu","6.10.2-ubuntu-jdk8","7.1.4-ubuntu-jdk8","6.8.0-ubuntu","7.2.4-ubuntu","7.16.0-ubuntu","6.10.9-ubuntu","7.6.6-ubuntu","7.16-jdk8","6.9.2-jdk8","7.1-jdk8","7.16.0-jdk8","7.2.4-ubuntu-jdk8","7.6.6-ubuntu-jdk8","6.9.2-ubuntu-jdk8","7.2.4-jdk8","7.0.4-jdk8","7.6.6-jdk8","6.10.3-ubuntu","6.9.2-ubuntu","7.16.0-ubuntu-jdk8","6.8.1-ubuntu","6.10.3-ubuntu-jdk8","6.7.0-jdk8","7.0.4-ubuntu","6.8.1-ubuntu-jdk8","6.7.0-ubuntu-jdk8","7-ubuntu-jdk8","7.0.4-ubuntu-jdk8","6.10.3-jdk8","6.8.1-jdk8","7-jdk8","7.13.0-jdk8","6.7.0-ubuntu","ubuntu","7.2.5-jdk8","7.6.0-jdk8","7.13.0-ubuntu-jdk8","7.6.7-jdk8","6.9-ubuntu","7.6.0-ubuntu","7.2.5-ubuntu-jdk8","6.10.11-jdk8","7.13.0-ubuntu","7.10.0-ubuntu-jdk8","6.9-jdk8","jdk8","7.10.0-jdk8","6.9.3-ubuntu-jdk8","7.6.7-ubuntu","7.0.5-ubuntu","6.10.4-ubuntu-jdk8","6.8.2-ubuntu-jdk8","ubuntu-jdk8","7.2.5-ubuntu","7.6.0-ubuntu-jdk8","7.10.0-ubuntu","6.9.3-ubuntu","7.6.7-ubuntu-jdk8","6.10.4-ubuntu","6.8.2-ubuntu","7.0-ubuntu","7.16-ubuntu","6.10.4-jdk8","7.0-ubuntu-jdk8","6.7.1-ubuntu-jdk8","6.8.2-jdk8","7.0-jdk8","6.7.1-jdk8","7-ubuntu","6.9-ubuntu-jdk8","6.7.1-ubuntu","6.10.12-jdk8","7.16-ubuntu-jdk8","6.9.3-jdk8","7.13.1-ubuntu-jdk8","6.10.12-ubuntu","7.2.6-jdk8","7.6.1-jdk8","7.10-ubuntu-jdk8","7.6.8-jdk8","7.0.5-jdk8","6.10.12-ubuntu-jdk8","7.2-ubuntu-jdk8","7.10-ubuntu","7.6.8-ubuntu","7.13.1-ubuntu","7.0.5-ubuntu-jdk8","7.2-ubuntu","7.6.1-ubuntu","7.10.1-ubuntu-jdk8","6.8.3-jdk8","7.13-jdk8","6.10.5-ubuntu","7.2.6-ubuntu","7.6.1-ubuntu-jdk8","7.10.1-ubuntu","6.8.3-ubuntu-jdk8","6.7.2-ubuntu-jdk8","7.6.8-ubuntu-jdk8","7.13-ubuntu","7.2-jdk8","6.8.3-ubuntu","6.7.2-ubuntu","7.4.2-jdk8","6.10.5-jdk8","7.13-ubuntu-jdk8","7.2.6-ubuntu-jdk8","7.10-jdk8","7.4-ubuntu-jdk8","6.10.5-ubuntu-jdk8","7.13.1-jdk8","6-ubuntu","6.7.2-jdk8","7.10.1-jdk8","7.4-jdk8","7.4.2-ubuntu","7.6.2-jdk8","7.6-jdk8","7.0.0-ubuntu-jdk8","7.2.0-ubuntu","7.6.2-ubuntu-jdk8","7.0.0-ubuntu","6.10-ubuntu-jdk8","7.6.2-ubuntu","7.1.0-ubuntu","7.2.0-jdk8","6.10.7-jdk8","7.6.9-jdk8","7.0.0-jdk8","6.7.3-jdk8","6.10-ubuntu","7.1.0-ubuntu-jdk8","7.6-ubuntu-jdk8","7.2.0-ubuntu-jdk8","6.7.3-ubuntu-jdk8","6.10-jdk8","6.8-ubuntu-jdk8","7.1.0-jdk8","6.10.7-ubuntu","7.6-ubuntu","6.7.3-ubuntu","6-jdk8","6.8.4-jdk8","6.10.7-ubuntu-jdk8","6.10.13-jdk8","6.8-ubuntu","7.9.1-ubuntu-jdk8","7.6.9-ubuntu","6.10.13-ubuntu-jdk8","6.8.4-ubuntu","7.6.9-ubuntu-jdk8","6.10.13-ubuntu","6.8-jdk8","7.3.0-ubuntu-jdk8","7.6.3-jdk8","7.2.1-ubuntu","7.0.1-ubuntu-jdk8","7.5.0-ubuntu","6-ubuntu-jdk8","6.8.4-ubuntu-jdk8","7.14.0-ubuntu","7.11.1-ubuntu-jdk8","7.6.3-ubuntu-jdk8","7.3.0-ubuntu","7.0.1-jdk8","7.2.1-ubuntu-jdk8","7.5.0-jdk8","6.7.4-ubuntu","7.14.0-jdk8","7.11.1-jdk8","7.6.3-ubuntu","7.0.1-ubuntu","7.5.0-ubuntu-jdk8","7.1.1-ubuntu-jdk8","6.10.8-jdk8","7.14.0-ubuntu-jdk8","7.11.1-ubuntu","7.3.0-jdk8","7.2.1-jdk8","7.1.1-ubuntu","6.7.4-jdk8","6.10.8-ubuntu-jdk8","7.1.1-jdk8","6.7.4-ubuntu-jdk8","6.10.8-ubuntu","7.7.0-ubuntu-jdk8","7.7.0-ubuntu","7.7.0-jdk8","7.7.1-jdk8","7.7.1-ubuntu","7.7-jdk8","7.7-ubuntu","7.7-ubuntu-jdk8","6.10.0-ubuntu","7.7.1-ubuntu-jdk8","6.10.0-jdk8","6.10.0-ubuntu-jdk8","6.10.1-jdk8","6.10.1-ubuntu","7.8.0-ubuntu","6.10.1-ubuntu-jdk8","7.8.0-ubuntu-jdk8","7.8.0-jdk8","6.10.10-ubuntu","6.10.10-ubuntu-jdk8","7.8-ubuntu","6.10.10-jdk8","7.8.1-ubuntu","7.8.1-ubuntu-jdk8","7.8-ubuntu-jdk8","7.8-jdk8","6.10.11-ubuntu-jdk8","7.8.1-jdk8","6.10.11-ubuntu","7.9.0-jdk8","7.9.0-ubuntu","7.9.0-ubuntu-jdk8","7.9.1-jdk8","7.9-ubuntu-jdk8","7.9.1-ubuntu","7.9-ubuntu","7.9-jdk8","7.3.1-ubuntu-jdk8","7.3.1-ubuntu","7.3.1-jdk8","7.3.2-jdk8","7.3.2-ubuntu-jdk8","7.3-ubuntu","7.3-ubuntu-jdk8","7.3-jdk8","7.3.2-ubuntu","7.4.0-ubuntu-jdk8","7.4.0-ubuntu","7.4.0-jdk8","7.4.1-ubuntu-jdk8","7.4.1-ubuntu","7.4.1-jdk8","7.4.2-ubuntu-jdk8","7.4-ubuntu","6.5.0-ubuntu","6.5.0","6.5.0-jdk8","6.5.0-ubuntu-jdk8","6.5.1","6.6.3","6.5.1-ubuntu","6.6.3-ubuntu","6.5.1-ubuntu-jdk8","6.6.3-ubuntu-jdk8","6.5.1-jdk8","6.6.3-jdk8","6.5.2-jdk8","6.6.4-ubuntu","6.5.2-ubuntu-jdk8","6.6-ubuntu-jdk8","6.5.2-ubuntu","6.6","6.5.2","6.6.4-jdk8","6.6.4","6.6.4-ubuntu-jdk8","6.6-jdk8","6.5.1-ubuntu-jdk11","6.6.3-jdk11","6.6-ubuntu","6.5.1-jdk11","6.5-ubuntu","6.6.3-ubuntu-jdk11","6.5.3-jdk8","6.5.3-ubuntu-jdk8","6.5-ubuntu-jdk8","6.5","6.6-ubuntu-jdk11","6.5.2-jdk11","6.5.3-ubuntu","6.5.2-ubuntu-jdk11","6.6.4-jdk11","6.5.3","6.6.4-ubuntu-jdk11","6.5-jdk8","6.6-jdk11","6.5-jdk11","6.5.3-ubuntu-jdk11","6.5-ubuntu-jdk11","6.5.3-jdk11","6.6.0-ubuntu","6.6.0-jdk8","6.6.0-ubuntu-jdk8","6.6.0","6.6.0-jdk11","6.6.0-ubuntu-jdk11","6.6.1-jdk8","6.6.1","6.6.1-ubuntu","6.6.1-ubuntu-jdk8","6.6.1-jdk11","6.6.1-ubuntu-jdk11","6.6.2","6.6.2-ubuntu-jdk8","6.5.0-jdk11","6.6.2-ubuntu-jdk11","6.6.2-jdk8","6.5.0-ubuntu-jdk11","6.6.2-jdk11","6.6.2-ubuntu","6.4.0-ubuntu","6.4.0","6.4.0-ubuntu-jdk8","6.4.0-jdk8","6.0-ubuntu-jdk8","6.4.1-jdk8","6.0.11","6.4.1-ubuntu-jdk8","6.0-jdk8","6.4.1","6.0-ubuntu","6.4.1-ubuntu","6.0.11-ubuntu-jdk8","6.0","6.0.11-jdk8","6.0.11-ubuntu","6.4.2-jdk8","6.4.2-ubuntu-jdk8","6.4.2-ubuntu","6.4.2","6.0.2","6.4.3-jdk8","6.0.2-ubuntu","6.4.3-ubuntu","6.0.2-jdk8","6.4.3","6.0.2-ubuntu-jdk8","6.4.3-ubuntu-jdk8","6.0.3-ubuntu-jdk8","6.4.4-ubuntu","6.0.3","6.4.4-jdk8","6.0.3-jdk8","6.4-jdk8","6.0.3-ubuntu","6.4-ubuntu","6.4","6.4.4-ubuntu-jdk8","6.4-ubuntu-jdk8","6.4.4","6.0.4-jdk8","6.0.4","6.0.4-ubuntu","6.0.4-ubuntu-jdk8","6.0.5-jdk8","6.0.5-ubuntu-jdk8","6.0.5-ubuntu","6.0.5","6.0.6-jdk8","6.0.6-ubuntu-jdk8","6.0.6","6.0.6-ubuntu","6.0.7-ubuntu-jdk8","6.0.7","6.0.7-ubuntu","6.0.7-jdk8","6.0.9-ubuntu","6.0.9","6.0.9-jdk8","6.0.9-ubuntu-jdk8","6.1.0-ubuntu-jdk8","6.1.0-ubuntu","6.1.0","6.1.0-jdk8","6.2.6","6.2.6-ubuntu-jdk8","6.2.6-jdk8","6.2.6-ubuntu","6.2","6.2.7-ubuntu-jdk8","6.2-ubuntu","6.2.7-jdk8","6.2-ubuntu-jdk8","6.2-jdk8","6.2.7-ubuntu","6.2.7","6.3.0-jdk8","6.3.0-ubuntu-jdk8","6.3.0","6.3.0-ubuntu","6.3.1","6.3.1-jdk8","6.3.1-ubuntu-jdk8","6.3.1-ubuntu","6.3.2-ubuntu","6.3.2-jdk8","6.3.2-ubuntu-jdk8","6.3.2","6.3.3","6.3.3-jdk8","6.3.3-ubuntu-jdk8","6.3.3-ubuntu","6.3.4-ubuntu-jdk8","6.3.4-ubuntu","6.3.4","6.3.4-jdk8","6.3.5","6.3.5-ubuntu-jdk8","6.3.5-jdk8","6.3.5-ubuntu","6.3-ubuntu","6.3.6-ubuntu","6.3.6-ubuntu-jdk8","6.3.6","6.3","6.3.6-jdk8","6.3-ubuntu-jdk8","6.3-jdk8","6.1.1-ubuntu","6.1.1","6.1.1-jdk8","6.1.1-ubuntu-jdk8","6.1.2-jdk8","6.1.2","6.1.2-ubuntu","6.1.2-ubuntu-jdk8","6.1.3-ubuntu-jdk8","6.1.3","6.4.0-ubuntu-jdk11","6.2.6-jdk11","6.1.3-jdk8","6.4.0-jdk11","6.2.6-ubuntu-jdk11","6.1.3-ubuntu","6.0-jdk11","6.1.1-ubuntu-jdk11","6.4.1-jdk11","6.0.11-jdk11","6.1.4","6.2.7-ubuntu-jdk11","6.4.1-ubuntu-jdk11","6.0-ubuntu-jdk11","6.1.4-ubuntu-jdk8","6.1.1-jdk11","6.2-ubuntu-jdk11","6.0.11-ubuntu-jdk11","6.1.4-jdk8","6.2-jdk11","6.1.4-ubuntu","6.2.7-jdk11","6.4.2-ubuntu-jdk11","6.1.2-jdk11","6.4.2-jdk11","6.1.2-ubuntu-jdk11","6.2.0-ubuntu-jdk8","6.1.5-jdk8","6.2.0-jdk8","6.1.5-ubuntu","6.2.0-ubuntu","6.1.5","6.2.0","6.0.2-jdk11","6.3.0-ubuntu-jdk11","6.1.5-ubuntu-jdk8","6.4.3-jdk11","6.1.3-ubuntu-jdk11","6.0.2-ubuntu-jdk11","6.3.0-jdk11","6.1.3-jdk11","6.4.3-ubuntu-jdk11","6.2.1","6.1.6-ubuntu-jdk8","6.2.1-ubuntu-jdk8","6.0.3-ubuntu-jdk11","6.1.6-ubuntu","6.3.1-jdk11","6.2.1-ubuntu","6.1.4-ubuntu-jdk11","6.0.3-jdk11","6.1.6","6.3.1-ubuntu-jdk11","6.4.4-ubuntu-jdk11","6.2.1-jdk8","6.1.4-jdk11","6.1.6-jdk8","6.4.4-jdk11","6.4-ubuntu-jdk11","6.4-jdk11","6.0.4-ubuntu-jdk11","6.3.2-ubuntu-jdk11","6.2.0-jdk11","6.1.5-jdk11","6.2.2-ubuntu","6.0.4-jdk11","6.1.7","6.3.2-jdk11","6.2.0-ubuntu-jdk11","6.1.5-ubuntu-jdk11","6.2.2-ubuntu-jdk8","6.1.7-ubuntu-jdk8","6.2.2","6.1.7-ubuntu","6.2.2-jdk8","6.1.7-jdk8","6.0.5-jdk11","6.3.3-ubuntu-jdk11","6.2.1-jdk11","6.1.6-ubuntu-jdk11","6.0.5-ubuntu-jdk11","6.3.3-jdk11","6.2.1-ubuntu-jdk11","6.1.6-jdk11","6.2.3-ubuntu","6.1.8-jdk8","6.0.0-ubuntu","6.2.3-ubuntu-jdk8","6.1.8-ubuntu-jdk8","6.0.0-ubuntu-jdk8","6.2.3","6.1.8-ubuntu","6.0.0-jdk8","6.0.6-ubuntu-jdk11","6.3.4-ubuntu-jdk11","6.2.2-ubuntu-jdk11","6.1.8","6.2.3-jdk8","6.0.0","6.1.7-jdk11","6.0.6-jdk11","6.3.4-jdk11","6.2.2-jdk11","6.1.7-ubuntu-jdk11","6.2.3-ubuntu-jdk11","6.3.5-ubuntu-jdk11","6.0.7-ubuntu-jdk11","6.0.0-ubuntu-jdk11","6.1.8-jdk11","6.2.3-jdk11","6.0.7-jdk11","6.3.5-jdk11","6.0.0-jdk11","6.1.8-ubuntu-jdk11","6.1.9-ubuntu","6.2.4-jdk8","6.0.1-ubuntu-jdk8","6.1-ubuntu-jdk8","6.2.4-ubuntu-jdk8","6.0.1-jdk8","6.1.9-jdk8","6.2.4","6.0.1-ubuntu","6.1-ubuntu","6.2.4-ubuntu","6.0.1","6.0.9-jdk11","6.2.4-ubuntu-jdk11","6.1-jdk8","6.3-jdk11","6.0.1-ubuntu-jdk11","6.1.9-ubuntu-jdk11","6.0.9-ubuntu-jdk11","6.1.9-ubuntu-jdk8","6.2.4-jdk11","6.3.6-jdk11","6.0.1-jdk11","6.1-jdk11","6.1","6.3-ubuntu-jdk11","6.1-ubuntu-jdk11","6.1.9","6.3.6-ubuntu-jdk11","6.1.9-jdk11","6.2.5","6.0.10-ubuntu-jdk8","6.2.5-ubuntu","6.0.10-jdk8","6.1.0-ubuntu-jdk11","6.2.5-jdk11","6.2.5-jdk8","6.0.10-jdk11","6.0.10-ubuntu","6.2.5-ubuntu-jdk8","6.2.5-ubuntu-jdk11","6.1.0-jdk11","6.0.10-ubuntu-jdk11","6.0.10","7-eap","5.16.11-alpine","5-alpine","5.16.11","5.16","5","5.16-alpine","5.16.10","5.16.10-alpine","5.16.9","5.16.9-alpine","5.16.8","5.16.8-alpine","5.16.7-alpine","5.16.7","5.16.6-alpine","5.16.6","5.16.5-alpine","5.16.5","5.9.2-alpine","5.9.2","5.9-alpine","5.9","5.9.1-alpine","5.9.1","5.9.0-alpine","5.9.0","5.8.3-alpine","5.8.3","5.8.2-alpine","5.8.2","5.8.1-alpine","5.8.1","5.8.0-alpine","5.8.0","5.7.4-alpine","5.7.4","5.7-alpine","5.7","5.7.3-alpine","5.7.3","5.7.2-alpine","5.7.2","5.7.1-alpine","5.7.1","5.7.0-alpine","5.7.0","5.6.6-alpine","5.6.6","5.6-alpine","5.6","5.6.5-alpine","5.6.5","5.6.4-alpine","5.6.4","5.6.3-alpine","5.6.3","5.6.2-alpine","5.6.2","5.6.1-alpine","5.6.1","5.6.0-alpine","5.6.0","5.5.9-alpine","5.5.9","5.5-alpine","5.5","5.5.8-alpine","5.5.8","5.5.7-alpine","5.5.7","5.5.6-alpine","5.5.6","5.5.5-alpine","5.5.5","5.5.4-alpine","5.5.4","5.5.3-alpine","5.5.3","5.5.2-alpine","5.5.2","5.5.1-alpine","5.5.1","5.5.0-alpine","5.5.0","5.4.9-alpine","5.4.9","5.4-alpine","5.4","5.4.8-alpine","5.4.8","5.4.7-alpine","5.4.7","5.4.6-alpine","5.4.6","5.4.4-alpine","5.4.4","5.4.3-alpine","5.4.3","5.4.2-alpine","5.4.2","5.4.1-alpine","5.4.1","5.4.0-alpine","5.4.0","5.3.7-alpine","5.3.7","5.3-alpine","5.3","5.3.6-alpine","5.3.6","5.3.5-alpine","5.3.5","5.3.4-alpine","5.3.4","5.3.3-alpine","5.3.3","5.3.2-alpine","5.3.2","5.3.1-alpine","5.3.1","5.3.0-alpine","5.3.0","5.2.8-alpine","5.2.8","5.2-alpine","5.2","5.2.7-alpine","5.2.7","5.2.6-alpine","5.2.6","5.2.5-alpine","5.2.5","5.2.4-alpine","5.2.4","5.2.3-alpine","5.2.3","5.2.2-alpine","5.2.2","5.2.1-alpine","5.2.1","5.2.0-alpine","5.2.0","5.16.4-alpine","5.16.4","5.16.3-alpine","5.16.3","5.16.2-alpine","5.16.2","5.16.1-alpine","5.16.1","5.16.0-alpine","5.16.0","5.15.3-alpine","5.15.3","5.15-alpine","5.15","5.15.2-alpine","5.15.2","5.15.1-alpine","5.15.1","5.15.0-alpine","5.15.0","5.14.4-alpine","5.14.4","5.14-alpine","5.14","5.14.3-alpine","5.14.3","5.14.2-alpine","5.14.2","5.14.1-alpine","5.14.1","5.14.0-alpine","5.14.0","5.13.6-alpine","5.13.6","5.13-alpine","5.13","5.13.5-alpine","5.13.5","5.13.4-alpine","5.13.4","5.13.3-alpine","5.13.3","5.13.1-alpine","5.13.1","5.13.0-alpine","5.13.0","5.12.4-alpine","5.12.4","5.12-alpine","5.12","5.12.3-alpine","5.12.3","5.12.2-alpine","5.12.2","5.12.1-alpine","5.12.1","5.12.0-alpine","5.12.0","5.11.4-alpine","5.11.4","5.11-alpine","5.11","5.11.3-alpine","5.11.3","5.11.2-alpine","5.11.2","5.11.1-alpine","5.11.1","5.10.4-alpine","5.10.4","5.10-alpine","5.10","5.10.3-alpine","5.10.3","5.10.2-alpine","5.10.2","5.10.1-alpine","5.10.1","5.10.0-alpine","5.10.0","5.1.9-alpine","5.1.9","5.1-alpine","5.1","5.1.8-alpine","5.1.8","5.1.7-alpine","5.1.7","5.1.6-alpine","5.1.6","5.1.5-alpine","5.1.5","5.1.4-alpine","5.1.4","5.1.3-alpine","5.1.3","5.1.2-alpine","5.1.2","5.1.1-alpine","5.1.1","5.1.0-alpine","5.1.0","5.0.9-alpine","5.0.9","5.0.8-alpine","5.0.8","5.0.7-alpine","5.0.7","5.0.6-alpine","5.0.6","5.0.5-alpine","5.0.5","5.0.4-alpine","5.0.4","5.0.2-alpine","5.0.2","5.0.10-alpine","5.0.10","5.0-alpine","5.0","5.0.1-alpine","5.0.1","5.0.0-alpine","5.0.0","4.14.5-alpine","4.14.5","4.1.6-alpine","4.1.6","4.1-alpine","4.1","4.11.2-alpine","4.11.2","4.11-alpine","4.11","5.8.4","5.8","4.14","4.13","4.12","4.10","4.9","4.8","4.6","4.7","4.4","4.5","4.3","4.2","4.0"],"mysql":["latest","oraclelinux8","oracle","innovation-oraclelinux8","innovation-oracle","innovation","8.3.0-oraclelinux8","8.3.0-oracle","8.3.0","8.3-oraclelinux8","8.3-oracle","8.3","8.0.36-oraclelinux8","8.0.36-oracle","8.0.36","8.0-oraclelinux8","8.0-oracle","8.0","8-oraclelinux8","8-oracle","8","8.0.36-debian","8.0.36-bookworm","8.0-debian","8.0-bookworm","8.0.36-bullseye","8.0-bullseye","8.2.0-oraclelinux8","8.2.0-oracle","8.2.0","8.2-oraclelinux8","8.2-oracle","8.2","8.0.35-oraclelinux8","8.0.35-oracle","8.0.35","8.0.35-debian","8.0.35-bullseye","5.7.44-oraclelinux7","5.7-oraclelinux7","5-oraclelinux7","5.7.44-oracle","5.7.44","5.7-oracle","5.7","5-oracle","5","8.1.0-oracle","8.1.0","8.1-oracle","8.1","8.0.34-oracle","8.0.34","5.7.43-oracle","5.7.43","8.0.34-debian","debian","8-debian","5.7.42-debian","5.7-debian","5-debian","5.7.42-oracle","5.7.42","8.0.33-oracle","8.0.33","8.0.33-debian","8.0.32-debian","5.7.41-debian","8.0.32-oracle","8.0.32","5.7.41-oracle","5.7.41","8.0.31-debian","5.7.40-debian","8.0.31-oracle","8.0.31","5.7.40-oracle","5.7.40","8.0.30-oracle","8.0.30","5.7.39-oracle","5.7.39","8.0.30-debian","5.7.39-debian","8.0.29-oracle","8.0.29","5.7.38-oracle","5.7.38","8.0.29-debian","5.7.38-debian","8.0.28-debian","8.0.28","5.7.37-debian","5.7.37","8.0.28-oracle","5.7.37-oracle","8.0.27","5.7.36","5.6.51","5.6","8.0.26","5.7.35","8.0.25","5.7.34","8.0.24","8.0.23","5.7.33","8.0.22","5.7.32","5.6.50","8.0.21","5.7.31","5.6.49","8.0.20","5.7.30","5.6.48","8.0.19","5.7.29","5.6.47","8.0.18","5.7.28","5.6.46","8.0.17","5.7.27","5.6.45","8.0.16","5.7.26","5.6.44","5.5.62","5.5","5.6.43","5.7.25","8.0.15","8.0.14","5.6.42","5.7.24","8.0.13","5.5.61","5.6.41","5.7.23","8.0.12","5.5.60","5.6.40","5.7.22","8.0.11","5.5.59","5.6.39","5.7.21","8.0.4","8.0.4-rc","8.0.3","5.5.58","5.6.38","5.7.20","5.5.57","5.6.37","5.7.19","8.0.2","5.5.56","5.6.36","5.7.18","8.0.1","5.5.55","5.6.35","5.7.17","8.0.0","5.5.54","5.5.53","5.7.16","5.6.34","5.5.52","5.6.33","5.7.15","5.5.51","5.6.32","5.7.14","5.5.50","5.6.31","5.7.13","5.7.12","5.6.30","5.5.49","5.7.11","5.6.29","5.5.48","5.7.10","5.6.28","5.5.47","5.6.27","5.7.9","5.5.46","5.5.40","5.7.4","5.6.17","5.5.41","5.7.5-m15","5.6.21","5.6.20","5.6.22","5.7.4-m14","5.7.5","5.7.8","5.7.8-rc","5.6.26","5.5.45","5.7.7","5.7.7-rc","5.6.25","5.5.44","5.6.24","5.5.43","5.7.6","5.7.6-m16","5.6.23","5.5.42"],"postgres":["latest","bullseye","bookworm","16.2-bullseye","16.2-bookworm","16.2","16-bullseye","16-bookworm","16","15.6-bullseye","15.6-bookworm","15.6","15-bullseye","15-bookworm","15","14.11-bullseye","14.11-bookworm","14.11","14-bullseye","14-bookworm","14","13.14-bullseye","13.14-bookworm","13.14","13-bullseye","13-bookworm","13","12.18-bullseye","12.18-bookworm","12.18","12-bullseye","12-bookworm","12","alpine3.19","alpine3.18","alpine","16.2-alpine3.19","16.2-alpine3.18","16.2-alpine","16-alpine3.19","16-alpine3.18","16-alpine","15.6-alpine3.19","15.6-alpine3.18","15.6-alpine","15-alpine3.19","15-alpine3.18","15-alpine","14.11-alpine3.19","14.11-alpine3.18","14.11-alpine","14-alpine3.19","14-alpine3.18","14-alpine","13.14-alpine3.19","13.14-alpine3.18","13.14-alpine","13-alpine3.19","13-alpine3.18","13-alpine","12.18-alpine3.19","12.18-alpine3.18","12.18-alpine","12-alpine3.19","12-alpine3.18","12-alpine","16.1-bullseye","15.5-bullseye","15.5-bookworm","15.5","14.10-bullseye","14.10-bookworm","14.10","13.13-bullseye","13.13-bookworm","13.13","12.17-bullseye","12.17-bookworm","12.17","16.1-bookworm","16.1","16.1-alpine3.19","16.1-alpine3.18","16.1-alpine","15.5-alpine3.19","15.5-alpine3.18","15.5-alpine","14.10-alpine3.19","14.10-alpine3.18","14.10-alpine","13.13-alpine3.19","13.13-alpine3.18","13.13-alpine","12.17-alpine3.19","12.17-alpine3.18","12.17-alpine","11.22-bullseye","11.22-bookworm","11-bullseye","11-bookworm","11.22-alpine3.19","11.22-alpine","11-alpine3.19","11-alpine","12.17-alpine3.17","12-alpine3.17","11.22-alpine3.18","11.22-alpine3.17","11-alpine3.18","11-alpine3.17","alpine3.17","16.1-alpine3.17","16-alpine3.17","15.5-alpine3.17","15-alpine3.17","13.13-alpine3.17","13-alpine3.17","14.10-alpine3.17","14-alpine3.17","12.16-bullseye","11.21-bullseye","11.21-bookworm","13.12-bullseye","13.12-bookworm","13.12","12.16-bookworm","12.16","14.9-bullseye","14.9-bookworm","14.9","16.0-bullseye","15.4-bullseye","15.4-bookworm","15.4","16.0-bookworm","16.0","16.0-alpine3.18","16.0-alpine3.17","16.0-alpine","15.4-alpine3.18","15.4-alpine3.17","15.4-alpine","14.9-alpine3.18","14.9-alpine3.17","14.9-alpine","13.12-alpine3.18","13.12-alpine3.17","13.12-alpine","12.16-alpine3.18","12.16-alpine3.17","12.16-alpine","11.21-alpine3.18","11.21-alpine3.17","11.21-alpine","16rc1-bullseye","16rc1-bookworm","16rc1","16rc1-alpine3.18","16rc1-alpine3.17","16rc1-alpine","16beta3-bullseye","16beta3-bookworm","16beta3","16beta3-alpine3.18","16beta3-alpine3.17","16beta3-alpine","16beta2-alpine3.18","16beta2-alpine3.17","16beta2-alpine","15.3-alpine3.18","15.3-alpine3.17","15.3-alpine","14.8-alpine3.18","14.8-alpine3.17","14.8-alpine","13.11-alpine3.18","13.11-alpine3.17","13.11-alpine","12.15-alpine3.18","12.15-alpine3.17","12.15-alpine","11.20-alpine3.18","11.20-alpine3.17","11.20-alpine","16beta2-bullseye","16beta2-bookworm","16beta2","15.3-bullseye","15.3-bookworm","15.3","14.8-bullseye","14.8-bookworm","14.8","13.11-bullseye","13.11-bookworm","13.11","12.15-bullseye","12.15-bookworm","12.15","11.20-bullseye","11.20-bookworm","16beta1-bookworm","16beta1-alpine3.18","16beta1-alpine3.17","16beta1-alpine","16beta1","16beta1-bullseye","15.2-bullseye","15.2-alpine3.17","15.2-alpine","15.2","14.7-bullseye","14.7-alpine3.17","14.7-alpine","14.7","13.10-bullseye","13.10-alpine3.17","13.10-alpine","13.10","12.14-bullseye","12.14-alpine3.17","12.14-alpine","12.14","11.19-bullseye","11.19-alpine3.17","11.19-alpine","15.1-bullseye","15.1","14.6-bullseye","14.6","13.9-bullseye","13.9","12.13-bullseye","12.13","11.18-bullseye","15.1-alpine3.17","15.1-alpine","14.6-alpine3.17","14.6-alpine","13.9-alpine3.17","13.9-alpine","12.13-alpine3.17","12.13-alpine","11.18-alpine3.17","11.18-alpine","alpine3.16","15.1-alpine3.16","15-alpine3.16","14.6-alpine3.16","14-alpine3.16","13.9-alpine3.16","13-alpine3.16","12.13-alpine3.16","12-alpine3.16","11.18-alpine3.16","11-alpine3.16","10.23-alpine3.16","10.23-alpine","10-alpine3.16","10-alpine","10.23-bullseye","10-bullseye","15.0-bullseye","14.5-bullseye","14.5","13.8-bullseye","13.8","12.12-bullseye","12.12","11.17-bullseye","10.22-bullseye","15.0","15.0-alpine3.16","15.0-alpine","14.5-alpine3.16","14.5-alpine","13.8-alpine3.16","13.8-alpine","12.12-alpine3.16","12.12-alpine","11.17-alpine3.16","11.17-alpine","10.22-alpine3.16","10.22-alpine","15rc2-alpine3.16","15rc2-alpine","15rc2-bullseye","15rc2","15rc1-alpine3.16","15rc1-alpine","15rc1-bullseye","15rc1","15beta4-bullseye","15beta4","15beta4-alpine3.16","15beta4-alpine","15beta3-bullseye","15beta3","15beta3-alpine3.16","15beta3-alpine","15beta2-alpine3.16","15beta2-alpine","14.4-alpine3.16","14.4-alpine","13.7-alpine3.16","13.7-alpine","12.11-alpine3.16","12.11-alpine","11.16-alpine3.16","11.16-alpine","10.21-alpine3.16","10.21-alpine","12.11-bullseye","12.11","15beta2-bullseye","15beta2","14.4-bullseye","14.4","13.7-bullseye","13.7","11.16-bullseye","10.21-bullseye","15beta1-bullseye","15beta1","11.16-stretch","11.16","11-stretch","11","10.21-stretch","10.21","10-stretch","10","15beta1-alpine3.16","15beta1-alpine","14.3-alpine3.16","14.3-alpine","14.3-bullseye","14.3","15beta1-alpine3.15","alpine3.15","14.3-alpine3.15","14-alpine3.15","13.7-alpine3.15","13-alpine3.15","12.11-alpine3.15","12-alpine3.15","11.16-alpine3.15","11-alpine3.15","10.21-alpine3.15","10-alpine3.15","14.2-bullseye","14.2","13.6-bullseye","13.6","12.10-bullseye","12.10","11.15-bullseye","10.20-bullseye","11.15-stretch","11.15","10.20-stretch","10.20","14.2-alpine3.15","14.2-alpine","13.6-alpine3.15","13.6-alpine","12.10-alpine3.15","12.10-alpine","11.15-alpine3.15","11.15-alpine","10.20-alpine3.15","10.20-alpine","9.6.24-bullseye","9.6-bullseye","9-bullseye","9.6.24-stretch","9.6.24","9.6-stretch","9.6","9-stretch","9","14.1-bullseye","14.1","13.5-bullseye","13.5","12.9-bullseye","12.9","11.14-stretch","11.14-bullseye","11.14","10.19-stretch","10.19-bullseye","10.19","9.6.24-alpine3.15","9.6.24-alpine","9.6-alpine3.15","9.6-alpine","9-alpine3.15","9-alpine","14.1-alpine3.15","14.1-alpine","13.5-alpine3.15","13.5-alpine","12.9-alpine3.15","12.9-alpine","11.14-alpine3.15","11.14-alpine","10.19-alpine3.15","10.19-alpine","alpine3.14","9.6.24-alpine3.14","9.6-alpine3.14","9-alpine3.14","14.1-alpine3.14","14-alpine3.14","13.5-alpine3.14","13-alpine3.14","12.9-alpine3.14","12-alpine3.14","11.14-alpine3.14","11-alpine3.14","10.19-alpine3.14","10-alpine3.14","9.6.23-stretch","9.6.23-bullseye","9.6.23-alpine3.14","9.6.23-alpine","9.6.23","14.0-bullseye","14.0-alpine3.14","14.0-alpine","14.0","13.4-bullseye","13.4-alpine3.14","13.4-alpine","13.4","12.8-bullseye","12.8-alpine3.14","12.8-alpine","12.8","11.13-stretch","11.13-bullseye","11.13-alpine3.14","11.13-alpine","11.13","10.18-stretch","10.18-bullseye","10.18-alpine3.14","10.18-alpine","10.18","14rc1-bullseye","14rc1","14rc1-alpine3.14","14rc1-alpine","buster","9.6.23-buster","9.6-buster","9-buster","14beta3-buster","14beta3","13.4-buster","13-buster","12.8-buster","12-buster","11.13-buster","11-buster","10.18-buster","10-buster","14beta3-alpine3.14","14beta3-alpine","9.6.22-alpine3.14","9.6.22-alpine","14beta2-alpine3.14","14beta2-alpine","13.3-alpine3.14","13.3-alpine","12.7-alpine3.14","12.7-alpine","11.12-alpine3.14","11.12-alpine","10.17-alpine3.14","10.17-alpine","9.6.22-stretch","9.6.22-buster","9.6.22","14beta2-buster","14beta2","13.3-buster","13.3","12.7-buster","12.7","11.12-stretch","11.12-buster","11.12","10.17-stretch","10.17-buster","10.17","alpine3.13","9-alpine3.13","13.3-alpine3.13","13-alpine3.13","12.7-alpine3.13","12-alpine3.13","11.12-alpine3.13","11-alpine3.13","10.17-alpine3.13","10-alpine3.13","9.6.22-alpine3.13","9.6-alpine3.13","9.6.21","9.5.25","9.5","13.2","12.6","11.11","10.16","9.6.21-alpine","9.5.25-alpine","9.5-alpine","13.2-alpine","12.6-alpine","11.11-alpine","10.16-alpine","9.6.20","9.5.24","13.1","12.5","11.10","10.15","9.6.20-alpine","9.5.24-alpine","13.1-alpine","12.5-alpine","11.10-alpine","10.15-alpine","9.6.19-alpine","9.5.23-alpine","13.0-alpine","12.4-alpine","11.9-alpine","10.14-alpine","9.6.19","9.5.23","13.0","12.4","11.9","10.14","13-rc1-alpine","13-rc1","13-beta3","13-beta3-alpine","9.6.18","9.5.22","13-beta2","12.3","11.8","10.13","9.6.18-alpine","9.5.22-alpine","13-beta2-alpine","12.3-alpine","11.8-alpine","10.13-alpine","13-beta1-alpine","13-beta1","9.6.17","9.5.21","11.7","10.12","9.6.17-alpine","9.5.21-alpine","12.2-alpine","11.7-alpine","10.12-alpine","12.2","9.4.26-alpine","9.4.26","9.4-alpine","9.4","9.6.16","9.5.20","9.4.25","12.1","11.6","10.11","9.6.16-alpine","9.5.20-alpine","9.4.25-alpine","12.1-alpine","11.6-alpine","10.11-alpine","9.6.15-alpine","9.5.19-alpine","9.4.24-alpine","12.0-alpine","11.5-alpine","10.10-alpine","9.6.15","9.5.19","9.4.24","12.0","11.5","10.10","12-rc1-alpine","12-rc1","12-beta4-alpine","12-beta4","12-beta3","12-beta3-alpine","9.6.14-alpine","9.5.18-alpine","9.4.23-alpine","12-beta2-alpine","11.4-alpine","10.9-alpine","9.6.14","9.5.18","9.4.23","11.4","10.9","12-beta2","9.6.13-alpine","9.5.17-alpine","9.4.22-alpine","12-beta1-alpine","11.3-alpine","10.8-alpine","9.6.13","9.5.17","9.4.22","12-beta1","11.3","10.8","11.2","9.4.21","9.5.16","9.6.12","10.7","9.4.21-alpine","9.5.16-alpine","9.6.12-alpine","10.7-alpine","11.2-alpine","9.4.20","9.5.15","9.6.11","10.6","11.1","9.4.20-alpine","9.5.15-alpine","9.6.11-alpine","10.6-alpine","11.1-alpine","9.3","9.3.25","9.3-alpine","9.3.25-alpine","10.5-alpine","10.5","11.0","11.0-alpine","9.3.24","9.4.19","9.5.14","9.6.10","11-rc1","11-rc1-alpine","11-beta4-alpine","9.4.19-alpine","11-beta4","9.3.24-alpine","9.5.14-alpine","9.6.10-alpine","11-beta3-alpine","11-beta3","9.3.23","9.5.13","9.5.13-alpine","9.4.18","9.6.9","10.4-alpine","10.4","11-beta2-alpine","11-beta2","9.4.18-alpine","9.3.23-alpine","9.6.9-alpine","11-beta1","11-beta1-alpine","9.6.8","10.3","9.3.22-alpine","9.3.22","9.4.17-alpine","9.4.17","9.5.12-alpine","9.5.12","9.6.8-alpine","10.3-alpine","9.3.21","9.4.16","9.5.11","9.6.7","10.2-alpine","10.2","9.5.11-alpine","9.5.10","9.3.21-alpine","9.4.16-alpine","9.6.7-alpine","9.3.20-alpine","9.4.15-alpine","9.5.10-alpine","9.6.6-alpine","10.1-alpine","9.3.20","9.4.15","9.6.6","10.1","9.3.19-alpine","9.3.19","9.4.14-alpine","9.4.14","9.5.9-alpine","9.5.9","9.6.5-alpine","9.6.5","10.0-alpine","10.0","9.2","9.2.23","10-rc1","9.2-alpine","9.2.23-alpine","10-rc1-alpine","10-beta4","10-beta4-alpine","9.2.22-alpine","9.2.22","9.3.18-alpine","9.3.18","9.4.13-alpine","9.4.13","9.5.8-alpine","9.5.8","9.6.4-alpine","9.6.4","10-beta3-alpine","10-beta3","9.2.21-alpine","9.2.21","9.3.17-alpine","9.3.17","9.4.12-alpine","9.4.12","9.5.7-alpine","9.5.7","9.6.3-alpine","9.6.3","10-beta2-alpine","10-beta2","10-beta1-alpine","10-beta1","9.2.20-alpine","9.2.20","9.3.16-alpine","9.3.16","9.4.11-alpine","9.4.11","9.5.6-alpine","9.5.6","9.6.2-alpine","9.6.2","9.2.19-alpine","9.2.19","9.3.15-alpine","9.3.15","9.4.10-alpine","9.4.10","9.5.5-alpine","9.5.5","9.6.1-alpine","9.6.1","9.1.24","9.1","9.5.4","9.4.9","9.3.14","9.2.18","9.1.23","9.6.0","9.6-rc1","9.6-beta4","9.4.8","9.1.22","9.6-beta3","9.5.3","9.3.13","9.2.17","9.6-beta2","9.6-beta1","9.5.2","9.4.7","9.3.12","9.2.16","9.1.21","9.5.1","9.4.6","9.3.11","9.2.15","9.1.20","9.5.0","9.4.5","9.3.10","9.2.14","9.1.19","9.0","9.0.22","9.5-rc1","9.5-beta2","9.5-beta1","9.4-rc1","9.3.5","9.4-beta2","9.2.9","9.4.0","9.1.14","9.4-beta3","9.0.18","9.5-alpha2","9.4.4","9.3.9","9.2.13","9.1.18","9.5-alpha1","9.4.3","9.3.8","9.2.12","9.1.17","9.0.21","9.4.2","9.3.7","9.2.11","9.1.16","9.0.20","8","8.4","8.4.22","9.4.1","9.3.6","9.2.10","9.1.15","9.0.19"],"mssql":["2017-CU1-ubuntu","2017-CU10","2017-CU10-ubuntu","2017-CU11","2017-CU11-ubuntu","2017-CU12","2017-CU12-ubuntu","2017-CU13","2017-CU13-ubuntu","2017-CU14","2017-CU14-ubuntu","2017-CU15","2017-CU15-GDR","2017-CU15-GDR-ubuntu","2017-CU15-GDR1-ubuntu-16.04","2017-CU15-ubuntu","2017-CU16","2017-CU16-ubuntu","2017-CU17","2017-CU17-ubuntu","2017-CU18-ubuntu-16.04","2017-CU19-ubuntu-16.04","2017-CU2-ubuntu","2017-CU20","2017-CU20-ubuntu","2017-CU20-ubuntu-16.04","2017-CU21-ubuntu-16.04","2017-CU22-GDR1-ubuntu-16.04","2017-CU22-OD1-ubuntu-16.04","2017-CU22-ubuntu-16.04","2017-CU23-ubuntu-16.04","2017-CU24-ubuntu-16.04","2017-CU25-ubuntu-16.04","2017-CU26-ubuntu-16.04","2017-CU27-ubuntu-16.04","2017-CU28-ubuntu-16.04","2017-CU29-GDR1-ubuntu-16.04","2017-CU29-ubuntu-16.04","2017-CU3-ubuntu","2017-CU30-ubuntu-18.04","2017-CU31-GDR1-ubuntu-18.04","2017-CU31-GDR2-ubuntu-18.04","2017-CU31-ubuntu-18.04","2017-CU4-ubuntu","2017-CU5-ubuntu","2017-CU6-ubuntu","2017-CU7-ubuntu","2017-CU8-ubuntu","2017-CU9-ubuntu","2017-GA-ubuntu","2017-GDR-ubuntu","2017-GDR3","2017-GDR3-ubuntu","2017-GDR4","2017-GDR4-ubuntu","2017-cu16","2017-cu16-ubuntu","2017-cu17","2017-cu17-ubuntu","2017-cu19","2017-cu19-ubuntu","2017-latest","2017-latest-ubuntu","2019-CU1-ubuntu-16.04","2019-CU10-ubuntu-16.04","2019-CU10-ubuntu-18.04","2019-CU10-ubuntu-20.04","2019-CU11-ubuntu-16.04","2019-CU11-ubuntu-18.04","2019-CU11-ubuntu-20.04","2019-CU12-ubuntu-16.04","2019-CU12-ubuntu-18.04","2019-CU12-ubuntu-20.04","2019-CU13-ubuntu-16.04","2019-CU13-ubuntu-18.04","2019-CU13-ubuntu-20.04","2019-CU14-ubuntu-16.04","2019-CU14-ubuntu-18.04","2019-CU14-ubuntu-20.04","2019-CU15-ubuntu-16.04","2019-CU15-ubuntu-18.04","2019-CU15-ubuntu-20.04","2019-CU16-GDR1-ubuntu-16.04","2019-CU16-GDR1-ubuntu-18.04","2019-CU16-GDR1-ubuntu-20.04","2019-CU16-ubuntu-16.04","2019-CU16-ubuntu-18.04","2019-CU16-ubuntu-20.04","2019-CU17-ubuntu-18.04","2019-CU17-ubuntu-20.04","2019-CU18-GDR1-ubuntu-18.04","2019-CU18-GDR1-ubuntu-20.04","2019-CU18-ubuntu-18.04","2019-CU18-ubuntu-20.04","2019-CU19-ubuntu-18.04","2019-CU19-ubuntu-20.04","2019-CU2-ubuntu-16.04","2019-CU20-ubuntu-18.04","2019-CU20-ubuntu-20.04","2019-CU21-ubuntu-20.04","2019-CU22-GDR1-ubuntu-20.04","2019-CU22-ubuntu-20.04","2019-CU23-ubuntu-20.04","2019-CU24-ubuntu-20.04","2019-CU25-GDR1-ubuntu-20.04","2019-CU25-ubuntu-20.04","2019-CU26-ubuntu-20.04","2019-CU3-ubuntu-16.04","2019-CU3-ubuntu-18.04","2019-CU4-ubuntu-16.04","2019-CU4-ubuntu-18.04","2019-CU5-ubuntu-16.04","2019-CU5-ubuntu-18.04","2019-CU6-ubuntu-16.04","2019-CU6-ubuntu-18.04","2019-CU8-GDR1-ubuntu-16.04","2019-CU8-GDR1-ubuntu-18.04","2019-CU8-OD2-ubuntu-16.04","2019-CU8-OD2-ubuntu-18.04","2019-CU8-ubuntu-16.04","2019-CU8-ubuntu-18.04","2019-CU9-ubuntu-16.04","2019-CU9-ubuntu-18.04","2019-GA-ubuntu-16.04","2019-GDR1-ubuntu-16.04","2019-GDR2-ubuntu-16.04","2019-gdr2-ubuntu-16.04","2019-gdr3-ubuntu-16.04","2019-latest","2022-CTP2.0-ubuntu","2022-CTP2.0-ubuntu-20.04","2022-CU1-ubuntu-20.04","2022-CU10-GDR1-ubuntu-20.04","2022-CU10-GDR1-ubuntu-22.04","2022-CU10-ubuntu-20.04","2022-CU10-ubuntu-22.04","2022-CU11-ubuntu-20.04","2022-CU11-ubuntu-22.04","2022-CU12-GDR1-ubuntu-20.04","2022-CU12-GDR1-ubuntu-22.04","2022-CU12-ubuntu-20.04","2022-CU12-ubuntu-22.04","2022-CU3-ubuntu-20.04","2022-CU4-ubuntu-20.04","2022-CU5-ubuntu-20.04","2022-CU6-ubuntu-20.04","2022-CU7-ubuntu-20.04","2022-CU8-GDR1-ubuntu-20.04","2022-CU8-ubuntu-20.04","2022-CU9-ubuntu-20.04","2022-RC0-ubuntu-20.04","2022-RC1-ubuntu-20.04","2022-RTM-CU1-ubuntu-20.04","2022-RTM-CU2-ubuntu-20.04","2022-RTM-GDR1-ubuntu-20.04","2022-RTM-ubuntu-20.04","2022-latest","2022-preview-ubuntu-22.04","latest","latest-ubuntu"]} \ No newline at end of file diff --git a/generateVersionList.mjs b/generateVersionList.mjs new file mode 100644 index 0000000..6e06813 --- /dev/null +++ b/generateVersionList.mjs @@ -0,0 +1,49 @@ + +import axios from 'axios'; +import console from 'console'; +import { writeFileSync } from 'fs'; +import { setTimeout } from 'timers'; + +const repositories = [ + { name: 'jira', repository: 'atlassian/jira-software' }, + { name: 'confluence', repository: 'atlassian/confluence' }, + { name: 'bamboo', repository: 'atlassian/bamboo-server' }, + { name: 'bitbucket', repository: 'atlassian/bitbucket-server' }, + { name: 'mysql', repository: 'library/mysql' }, + { name: 'postgres', repository: 'library/postgres' }, +]; + +const getListOfTagsPaginated = async (url) => { + const tags = []; + const { data } = await axios.get(url); + if (data) { + tags.push(...data.results.map(item => item.name)); + + if (data.next) { + // Add a backoff period to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 100)); + tags.push(...await getListOfTagsPaginated(data.next)); + } + } + + return tags; +} + +(async () => { + const result = await repositories.reduce(async (previous, item) => { + const result = await previous; + console.log(`Retrieving list of tags for ${item.name}...`); + const tags = await getListOfTagsPaginated(`https://hub.docker.com/v2/repositories/${item.repository}/tags/?page_size=100`); + result[item.name] = tags; + return result; + }, Promise.resolve({})); + + // Microsft SQL Server has it's own registry with a different API + console.log(`Retrieving list of tags for mssql...`); + const { data } = await axios.get('https://mcr.microsoft.com/v2/mssql/server/tags/list'); + result['mssql'] = data.tags; + + const output = {}; + Object.entries(result).forEach(([ key, value ]) => output[key] = value); + writeFileSync('assets/versions.json', JSON.stringify(output)); +})(); \ No newline at end of file diff --git a/package.json b/package.json index e27fe0c..a4ba282 100644 --- a/package.json +++ b/package.json @@ -19,14 +19,15 @@ }, "scripts": { "clean": "rm -rf dist && rm -rf lib", + "reinstall": "yarn clean && yarn install", "lint": "yarn run eslint .", "build": "yarn clean && yarn lint && yarn build:esm && yarn build:types", "build:esm": "yarn run tsc && yarn run rollup -c rollup.config.js", "build:types": "yarn run tsc --emitDeclarationOnly --outDir lib/types/", + "generate:versions": "node ./generateVersionList.mjs", "watch": "yarn run nodemon --watch src -e '.ts' --exec 'yarn build'", "start": "./lib/index.js", - "reinstall": "yarn clean && yarn install", - "prepack": "yarn build" + "prepack": "yarn generate:versions && yarn build" }, "repository": { "type": "git", diff --git a/src/commands/database-mssql.ts b/src/commands/database-mssql.ts index 1ec6781..1a49ea6 100644 --- a/src/commands/database-mssql.ts +++ b/src/commands/database-mssql.ts @@ -3,12 +3,13 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { mssql as versions } from '../../assets/versions.json'; import { MSSQL, MSSQLOptions } from '../databases/mssql'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of Microsoft SQL Server').choices([ '2017', '2019', '2022' ]).default('2022')) + .addOption(new Option('-v, --version ', 'The version of Microsoft SQL Server').choices(versions).default('latest')) .addOption(new Option('-e, --edition ', 'The edition of Microsoft SQL Server').choices([ 'Developer', 'Express', 'Standard', 'Enterprise', 'EnterpriseCore' ]).default('Developer')) .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('1433')) .addOption(new Option('-P, --password ', 'The value passed to MSSQL_SA_PASSWORD environment variable. MS SQL Server password policy applies.').default('DataCenterDX!')) diff --git a/src/commands/database-mysql.ts b/src/commands/database-mysql.ts index 6c4609e..5e8c474 100644 --- a/src/commands/database-mysql.ts +++ b/src/commands/database-mysql.ts @@ -3,12 +3,13 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { mysql as versions } from '../../assets/versions.json'; import { MySQL } from '../databases/mysql'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of Postgres').choices([ '8.0', '8.3' ]).default('8.3')) + .addOption(new Option('-v, --version ', 'The version of MySQL').choices(versions).default('latest')) .addOption(new Option('-d, --database ', 'The value passed to MYSQL_DATABASE environment variable').default('dcdx')) .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('3306')) .addOption(new Option('-U, --username ', 'The value passed to MYSQL_USER environment variable').default('dcdx')) diff --git a/src/commands/database-postgres.ts b/src/commands/database-postgres.ts index 7e09a6b..5939e84 100644 --- a/src/commands/database-postgres.ts +++ b/src/commands/database-postgres.ts @@ -3,12 +3,13 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { postgres as versions } from '../../assets/versions.json'; import { Postgres } from '../databases/postgres'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of Postgres').choices([ '12', '13', '14', '15']).default('15')) + .addOption(new Option('-v, --version ', 'The version of Postgres').choices(versions).default('latest')) .addOption(new Option('-d, --database ', 'The value passed to POSTGRES_DB environment variable').default('dcdx')) .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('5432')) .addOption(new Option('-U, --username ', 'The value passed to POSTGRES_USER environment variable').default('dcdx')) diff --git a/src/commands/reset-bamboo.ts b/src/commands/reset-bamboo.ts index e70a6cb..eecd907 100644 --- a/src/commands/reset-bamboo.ts +++ b/src/commands/reset-bamboo.ts @@ -3,15 +3,16 @@ import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; +import { bamboo as versions } from '../../assets/versions.json'; import { AMPS } from '../applications/amps'; import { Bamboo } from '../applications/bamboo'; -const version = AMPS.getApplicationVersion() || '9.6.1'; +const version = AMPS.getApplicationVersion() || 'latest'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.4.3' ]).default(version)) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default(version)) .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .parse(process.argv) .opts(); diff --git a/src/commands/reset-bitbucket.ts b/src/commands/reset-bitbucket.ts index 1439075..a9af885 100644 --- a/src/commands/reset-bitbucket.ts +++ b/src/commands/reset-bitbucket.ts @@ -3,15 +3,16 @@ import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; +import { bitbucket as versions } from '../../assets/versions.json'; import { AMPS } from '../applications/amps'; import { Bitbucket } from '../applications/bitbucket'; -const version = AMPS.getApplicationVersion() || '9.4.3'; +const version = AMPS.getApplicationVersion() || 'latest'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.4.3' ]).default(version)) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default(version)) .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .parse(process.argv) .opts(); diff --git a/src/commands/reset-confluence.ts b/src/commands/reset-confluence.ts index 8723ca7..0722c26 100644 --- a/src/commands/reset-confluence.ts +++ b/src/commands/reset-confluence.ts @@ -3,15 +3,16 @@ import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; +import { confluence as versions } from '../../assets/versions.json'; import { AMPS } from '../applications/amps'; import { Confluence } from '../applications/confluence'; -const version = AMPS.getApplicationVersion() || '8.9.0'; +const version = AMPS.getApplicationVersion() || 'latest'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '8.9.0' ]).default(version)) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default(version)) .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .parse(process.argv) .opts(); diff --git a/src/commands/reset-jira.ts b/src/commands/reset-jira.ts index 0dddec7..965a182 100644 --- a/src/commands/reset-jira.ts +++ b/src/commands/reset-jira.ts @@ -3,15 +3,16 @@ import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; +import { jira as versions } from '../../assets/versions.json'; import { AMPS } from '../applications/amps'; import { Jira } from '../applications/jira'; -const version = AMPS.getApplicationVersion() || '9.15.0'; +const version = AMPS.getApplicationVersion() || 'latest'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.15.0' ]).default(version)) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default(version)) .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .parse(process.argv) .opts(); diff --git a/src/commands/run-bamboo.ts b/src/commands/run-bamboo.ts index 0694f16..751804e 100644 --- a/src/commands/run-bamboo.ts +++ b/src/commands/run-bamboo.ts @@ -3,12 +3,13 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { bamboo as versions } from '../../assets/versions.json'; import { Bamboo } from '../applications/bamboo'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.4.3' ]).default('9.6.1')) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) diff --git a/src/commands/run-bitbucket.ts b/src/commands/run-bitbucket.ts index 39e470b..f92239f 100644 --- a/src/commands/run-bitbucket.ts +++ b/src/commands/run-bitbucket.ts @@ -3,12 +3,13 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { bitbucket as versions } from '../../assets/versions.json'; import { Bitbucket } from '../applications/bitbucket'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '8.19.1' ]).default('8.19.1')) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) diff --git a/src/commands/run-confluence.ts b/src/commands/run-confluence.ts index 7e9d731..ecd06d8 100644 --- a/src/commands/run-confluence.ts +++ b/src/commands/run-confluence.ts @@ -3,12 +3,13 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { confluence as versions } from '../../assets/versions.json'; import { Confluence } from '../applications/confluence'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '8.9.0' ]).default('8.9.0')) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) diff --git a/src/commands/run-jira.ts b/src/commands/run-jira.ts index 0cbfb70..99367ca 100644 --- a/src/commands/run-jira.ts +++ b/src/commands/run-jira.ts @@ -3,12 +3,13 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { jira as versions } from '../../assets/versions.json'; import { Jira } from '../applications/jira'; (async () => { const options = program .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices([ '9.15.0' ]).default('9.15.0')) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) diff --git a/src/index.ts b/src/index.ts index 157833b..1edbc65 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,12 +2,12 @@ import { program } from 'commander'; -import pkg from '../package.json'; +import { version } from '../package.json'; program .name('dcdx') .description('The Unofficial Atlassian Data Center Plugin Development CLI') - .version(pkg.version) + .version(version) .showHelpAfterError(true); // ------------------------------------------------------------------------------------------ Start From ff345c6f75e2def138562f33a9a214ff3a8e9de3 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Wed, 17 Apr 2024 14:29:45 +0200 Subject: [PATCH 10/18] fix: the application state can also be 'RUNNING' --- src/applications/base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/base.ts b/src/applications/base.ts index 2377ba6..2c50c5b 100644 --- a/src/applications/base.ts +++ b/src/applications/base.ts @@ -109,7 +109,7 @@ export abstract class Base extends EventEmitter { if (response) { if (response.status === 200) { const { data } = response; - if (data.state === 'FIRST_RUN') { + if (data.state === 'FIRST_RUN' || data.state === 'RUNNING') { console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`); return true; } From a42b0d930fc205fa68cb25789989691da4508dd6 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Sat, 20 Apr 2024 08:58:10 +0200 Subject: [PATCH 11/18] feat: add build command --- package.json | 2 + src/commands/build.ts | 109 +++++++++++ src/{applications => helpers}/amps.ts | 0 src/helpers/docker.ts | 31 ++++ src/index.ts | 5 + yarn.lock | 256 +++++++++++++++++++++++++- 6 files changed, 397 insertions(+), 6 deletions(-) create mode 100644 src/commands/build.ts rename src/{applications => helpers}/amps.ts (100%) create mode 100644 src/helpers/docker.ts diff --git a/package.json b/package.json index a4ba282..4f547e5 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "15.2.3", "@rollup/plugin-terser": "0.4.4", + "@types/dockerode": "3.3.28", "@types/js-yaml": "4", "@types/node": "18.16.0", "@types/pg": "8", @@ -66,6 +67,7 @@ "chokidar": "3.6.0", "commander": "12.0.0", "docker-compose": "0.24.8", + "dockerode": "4.0.2", "exit-hook": "4.0.0", "fast-xml-parser": "4.3.6", "js-yaml": "4.1.0", diff --git a/src/commands/build.ts b/src/commands/build.ts new file mode 100644 index 0000000..fe83950 --- /dev/null +++ b/src/commands/build.ts @@ -0,0 +1,109 @@ +#!/usr/bin/env node + +import { watch } from 'chokidar'; +import { Option, program } from 'commander'; +import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { resolve } from 'path'; +import { cwd } from 'process'; + +import { AMPS } from '../helpers/amps'; +import { Docker } from '../helpers/docker'; +import { getApplicationByName } from '../helpers/getApplication'; +import { isRecursiveBuild } from '../helpers/isRecursiveBuild'; +import { showRecursiveBuildWarning } from '../helpers/showRecursiveBuildWarning'; + +(async () => { + const options = program + .name('dcdx build') + .description('Build & install the Atlassian Data Center plugin from the current directory.\nYou can add Maven build arguments after the command options.') + .usage('[options] [...maven_arguments]') + .addOption(new Option('-w, --watch ', 'Additional list of glob patterns used to watch for file changes')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('-o, --outputDirectory ', 'Output directory where QuickReload will look for generated JAR files').default('target')) + .allowUnknownOption(true) + .parse(process.argv) + .opts(); + + if (!AMPS.isAtlassianPlugin()) { + console.log('Unable to find an Atlassian Plugin project in the current directory 🤔'); + gracefulExit(); + } + + const application = AMPS.getApplication(); + if (!application) { + console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + gracefulExit(); + process.exit(); + } + + const Application = getApplicationByName(application); + if (!Application) { + console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + process.exit(); + } + + const mavenOpts = program.args.slice(); + if (options.activateProfiles) { + mavenOpts.push(...[ '-P', options.activateProfiles ]); + } + + console.log(`Looking for running instances of ${application}`); + const containerIds = await Docker.getRunningContainerIds(application); + if (containerIds.length > 1) { + console.log(`There are multple running instance of ${application}, unable to determine which one to use`); + gracefulExit(0); + return; + } + + const containerId = containerIds[0]; + if (!containerId) { + console.log(`Could not find running instance of ${application}, please make sure they are running first!`); + gracefulExit(0); + return; + } + + console.log('Watching filesystem for changes to source files (QuickReload)'); + let lastBuildCompleted = new Date().getTime(); + const patterns = Array.isArray(options.watch) ? options.watch : [ options.watch ]; + const quickReload = watch([ '**/*', ...patterns ], { + cwd: cwd(), + usePolling: true, + interval: 2 * 1000, + binaryInterval: 2 * 1000, + awaitWriteFinish: true, + persistent: true, + atomic: true + }); + + asyncExitHook(async () => { + console.log(`Stopping filesystem watcher... ⏳`); + await quickReload.close(); + console.log(`Successfully stopped all running processes 💪`); + }, { wait: 30 * 1000 }); + + quickReload.on('change', async (path) => { + if (path.startsWith(options.outputDirectory) && path.toLowerCase().endsWith('.jar')) { + console.log('Found updated JAR file, uploading them to QuickReload'); + await Docker.copy(resolve(path), `${containerId}:/opt/quickreload/`) + .then(() => console.log('Finished uploading JAR file to QuickReload')) + .catch(err => console.log('Failed to upload JAR file to QuickReload', err)); + lastBuildCompleted = new Date().getTime(); + } else if (!path.startsWith(options.outputDirectory)) { + if (isRecursiveBuild(lastBuildCompleted)) { + showRecursiveBuildWarning(options.outputDirectory); + } else { + console.log('Detected file change, rebuilding Atlasian Plugin for QuickReload'); + await AMPS.build(mavenOpts).catch(() => Promise.resolve()); + } + } + }); + + await AMPS.build(mavenOpts).catch(() => Promise.resolve()); + lastBuildCompleted = new Date().getTime(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); + diff --git a/src/applications/amps.ts b/src/helpers/amps.ts similarity index 100% rename from src/applications/amps.ts rename to src/helpers/amps.ts diff --git a/src/helpers/docker.ts b/src/helpers/docker.ts new file mode 100644 index 0000000..84a3ceb --- /dev/null +++ b/src/helpers/docker.ts @@ -0,0 +1,31 @@ +import { spawn } from 'child_process'; +import Dockerode from 'dockerode'; +import { cwd } from 'process'; + +import { SupportedApplications } from '../types/SupportedApplications'; + +const docker = new Dockerode(); + +export class Docker { + + public static async getRunningContainerIds(application: SupportedApplications) { + const containers = await docker.listContainers({ filters: { + status: [ 'running' ], + label: [ `com.docker.compose.service=${application}` ] + }}); + return containers.map(item => item.Id); + } + + public static async copy(sourcePath: string, destinationPath:string) { + return new Promise((resolve, reject) => { + const docker = spawn( + 'docker', + [ 'cp', sourcePath, destinationPath ], + { cwd: cwd(), stdio: 'inherit' } + ); + docker.on('error', reject) + docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); + }); + } + +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 1edbc65..c49e809 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,11 @@ program .version(version) .showHelpAfterError(true); +// ------------------------------------------------------------------------------------------ Build + +program + .command('build', 'Build & install the Atlassian Data Center plugin from the current directory', { executableFile: './commands/build.js' }); + // ------------------------------------------------------------------------------------------ Start program diff --git a/yarn.lock b/yarn.lock index 9648ccc..0987d91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -238,6 +238,13 @@ __metadata: languageName: node linkType: hard +"@balena/dockerignore@npm:^1.0.2": + version: 1.0.2 + resolution: "@balena/dockerignore@npm:1.0.2" + checksum: 10c0/0bcb067e86f6734ab943ce4ce9a7c8611f2e983a70bccebf9d2309db57695c09dded7faf5be49c929c4c9e9a9174ae55fc625626de0fb9958823c37423d12f4e + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -1173,6 +1180,27 @@ __metadata: languageName: node linkType: hard +"@types/docker-modem@npm:*": + version: 3.0.6 + resolution: "@types/docker-modem@npm:3.0.6" + dependencies: + "@types/node": "npm:*" + "@types/ssh2": "npm:*" + checksum: 10c0/d3ffd273148bc883ff9b1a972b1f84c1add6d9a197d2f4fc9774db4c814f39c2e51cc649385b55d781c790c16fb0bf9c1f4c62499bd0f372a4b920190919445d + languageName: node + linkType: hard + +"@types/dockerode@npm:3.3.28": + version: 3.3.28 + resolution: "@types/dockerode@npm:3.3.28" + dependencies: + "@types/docker-modem": "npm:*" + "@types/node": "npm:*" + "@types/ssh2": "npm:*" + checksum: 10c0/ce43da3cd269e11e999366aa9019a067d97dc012d7c460b560dfe75d4ccacc618077f2dfc5f34da2c915d4d86ed883d00d3bb741fd8440ef4ca1136afbc29b9f + languageName: node + linkType: hard + "@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" @@ -1217,6 +1245,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^18.11.18": + version: 18.19.31 + resolution: "@types/node@npm:18.19.31" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10c0/bfebae8389220c0188492c82eaf328f4ba15e6e9b4abee33d6bf36d3b13f188c2f53eb695d427feb882fff09834f467405e2ed9be6aeb6ad4705509822d2ea08 + languageName: node + linkType: hard + "@types/normalize-package-data@npm:^2.4.3": version: 2.4.4 resolution: "@types/normalize-package-data@npm:2.4.4" @@ -1259,6 +1296,15 @@ __metadata: languageName: node linkType: hard +"@types/ssh2@npm:*": + version: 1.15.0 + resolution: "@types/ssh2@npm:1.15.0" + dependencies: + "@types/node": "npm:^18.11.18" + checksum: 10c0/055c271845847867c365b0c002e59536608e400864aea4f54ebc72e8588b92dbc4b6572e3095092dba0d86d49898e2180a389810269d119f897972ebbddb4a7f + languageName: node + linkType: hard + "@types/validator@npm:^13.7.17": version: 13.11.9 resolution: "@types/validator@npm:13.11.9" @@ -1644,6 +1690,15 @@ __metadata: languageName: node linkType: hard +"asn1@npm:^0.2.6": + version: 0.2.6 + resolution: "asn1@npm:0.2.6" + dependencies: + safer-buffer: "npm:~2.1.0" + checksum: 10c0/00c8a06c37e548762306bcb1488388d2f76c74c36f70c803f0c081a01d3bdf26090fc088cd812afc5e56a6d49e33765d451a5f8a68ab9c2b087eba65d2e980e0 + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -1685,6 +1740,15 @@ __metadata: languageName: node linkType: hard +"bcrypt-pbkdf@npm:^1.0.2": + version: 1.0.2 + resolution: "bcrypt-pbkdf@npm:1.0.2" + dependencies: + tweetnacl: "npm:^0.14.3" + checksum: 10c0/ddfe85230b32df25aeebfdccfbc61d3bc493ace49c884c9c68575de1f5dcf733a5d7de9def3b0f318b786616b8d85bad50a28b1da1750c43e0012c93badcc148 + languageName: node + linkType: hard + "before-after-hook@npm:^3.0.2": version: 3.0.2 resolution: "before-after-hook@npm:3.0.2" @@ -1711,6 +1775,17 @@ __metadata: languageName: node linkType: hard +"bl@npm:^4.0.3": + version: 4.1.0 + resolution: "bl@npm:4.1.0" + dependencies: + buffer: "npm:^5.5.0" + inherits: "npm:^2.0.4" + readable-stream: "npm:^3.4.0" + checksum: 10c0/02847e1d2cb089c9dc6958add42e3cdeaf07d13f575973963335ac0fdece563a50ac770ac4c8fa06492d2dd276f6cc3b7f08c7cd9c7a7ad0f8d388b2a28def5f + languageName: node + linkType: hard + "bl@npm:^6.0.11": version: 6.0.12 resolution: "bl@npm:6.0.12" @@ -1772,6 +1847,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^5.5.0": + version: 5.7.1 + resolution: "buffer@npm:5.7.1" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.1.13" + checksum: 10c0/27cac81cff434ed2876058d72e7c4789d11ff1120ef32c9de48f59eab58179b66710c488987d295ae89a228f835fc66d088652dffeb8e3ba8659f80eb091d55e + languageName: node + linkType: hard + "buffer@npm:^6.0.3": version: 6.0.3 resolution: "buffer@npm:6.0.3" @@ -1782,6 +1867,13 @@ __metadata: languageName: node linkType: hard +"buildcheck@npm:~0.0.6": + version: 0.0.6 + resolution: "buildcheck@npm:0.0.6" + checksum: 10c0/8cbdb89f41bc484b8325f4828db4135b206a0dffb641eb6cdb2b7022483c45dd0e5aac6d820c9a67bdd2caab3a02c76d7ceec7bd9ec494b5a2270d2806b01a76 + languageName: node + linkType: hard + "builtin-modules@npm:^3.3.0": version: 3.3.0 resolution: "builtin-modules@npm:3.3.0" @@ -1892,6 +1984,13 @@ __metadata: languageName: node linkType: hard +"chownr@npm:^1.1.1": + version: 1.1.4 + resolution: "chownr@npm:1.1.4" + checksum: 10c0/ed57952a84cc0c802af900cf7136de643d3aba2eecb59d29344bc2f3f9bf703a301b9d84cdc71f82c3ffc9ccde831b0d92f5b45f91727d6c9da62f23aef9d9db + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -2205,6 +2304,17 @@ __metadata: languageName: node linkType: hard +"cpu-features@npm:~0.0.9": + version: 0.0.9 + resolution: "cpu-features@npm:0.0.9" + dependencies: + buildcheck: "npm:~0.0.6" + nan: "npm:^2.17.0" + node-gyp: "npm:latest" + checksum: 10c0/e12aa8b791d2db6572d23553c2eb14dfe90c80fa69c788fcf8f2a5dc373b7ac1c99dd04292677751746cafe51a67d62eb09fd9f88502e37830fe602c7e582ca8 + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -2276,6 +2386,7 @@ __metadata: "@rollup/plugin-json": "npm:6.1.0" "@rollup/plugin-node-resolve": "npm:15.2.3" "@rollup/plugin-terser": "npm:0.4.4" + "@types/dockerode": "npm:3.3.28" "@types/js-yaml": "npm:4" "@types/node": "npm:18.16.0" "@types/pg": "npm:8" @@ -2287,6 +2398,7 @@ __metadata: chokidar: "npm:3.6.0" commander: "npm:12.0.0" docker-compose: "npm:0.24.8" + dockerode: "npm:4.0.2" eslint: "npm:9.0.0" eslint-plugin-simple-import-sort: "npm:12.0.0" exit-hook: "npm:4.0.0" @@ -2420,6 +2532,29 @@ __metadata: languageName: node linkType: hard +"docker-modem@npm:^5.0.3": + version: 5.0.3 + resolution: "docker-modem@npm:5.0.3" + dependencies: + debug: "npm:^4.1.1" + readable-stream: "npm:^3.5.0" + split-ca: "npm:^1.0.1" + ssh2: "npm:^1.15.0" + checksum: 10c0/86d18b1b1e92954f4f5632b82453588670c11265a60d982c57bfcd737fe0362f4aa68176edae6d3c3f92c17a59bcfe8840fc741c06baea55e2003a78d7d16045 + languageName: node + linkType: hard + +"dockerode@npm:4.0.2": + version: 4.0.2 + resolution: "dockerode@npm:4.0.2" + dependencies: + "@balena/dockerignore": "npm:^1.0.2" + docker-modem: "npm:^5.0.3" + tar-fs: "npm:~2.0.1" + checksum: 10c0/69ece79408aca8523726fcec9d9c168b9a987b60b7143502583cc0b731dd2abf8b69b9d7d71c20d2bddcc508ebb477d82849d0bd368df260fedd8d62de5c5937 + languageName: node + linkType: hard + "dot-prop@npm:^5.1.0": version: 5.3.0 resolution: "dot-prop@npm:5.3.0" @@ -2491,6 +2626,15 @@ __metadata: languageName: node linkType: hard +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: "npm:^1.4.0" + checksum: 10c0/870b423afb2d54bb8d243c63e07c170409d41e20b47eeef0727547aea5740bd6717aca45597a9f2745525667a6b804c1e7bede41f856818faee5806dd9ff3975 + languageName: node + linkType: hard + "env-ci@npm:^11.0.0": version: 11.0.0 resolution: "env-ci@npm:11.0.0" @@ -3025,6 +3169,13 @@ __metadata: languageName: node linkType: hard +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0" + checksum: 10c0/a0cde99085f0872f4d244e83e03a46aa387b74f5a5af750896c6b05e9077fac00e9932fdf5aef84f2f16634cd473c63037d7a512576da7d5c2b9163d1909f3a8 + languageName: node + linkType: hard + "fs-extra@npm:^11.0.0": version: 11.2.0 resolution: "fs-extra@npm:11.2.0" @@ -3477,7 +3628,7 @@ __metadata: languageName: node linkType: hard -"ieee754@npm:^1.2.1": +"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb @@ -3579,7 +3730,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.4, inherits@npm:~2.0.3": +"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 @@ -4743,6 +4894,13 @@ __metadata: languageName: node linkType: hard +"mkdirp-classic@npm:^0.5.2": + version: 0.5.3 + resolution: "mkdirp-classic@npm:0.5.3" + checksum: 10c0/95371d831d196960ddc3833cc6907e6b8f67ac5501a6582f47dfae5eb0f092e9f8ce88e0d83afcae95d6e2b61a01741ba03714eeafb6f7a6e9dcc158ac85b168 + languageName: node + linkType: hard + "mkdirp@npm:^1.0.3": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" @@ -4825,6 +4983,15 @@ __metadata: languageName: node linkType: hard +"nan@npm:^2.17.0, nan@npm:^2.18.0": + version: 2.19.0 + resolution: "nan@npm:2.19.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/b8d05d75f92ee9d94affa50d0aa41b6c698254c848529452d7ab67c2e0d160a83f563bfe2cbd53e077944eceb48c757f83c93634c7c9ff404c9ec1ed4e5ced1a + languageName: node + linkType: hard + "native-duplexpair@npm:^1.0.0": version: 1.0.0 resolution: "native-duplexpair@npm:1.0.0" @@ -5201,7 +5368,7 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0": +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -5810,6 +5977,16 @@ __metadata: languageName: node linkType: hard +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: "npm:^1.1.0" + once: "npm:^1.3.1" + checksum: 10c0/bbdeda4f747cdf47db97428f3a135728669e56a0ae5f354a9ac5b74556556f5446a46f720a8f14ca2ece5be9b4d5d23c346db02b555f46739934cc6c093a5478 + languageName: node + linkType: hard + "punycode@npm:^2.1.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -5944,6 +6121,17 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: "npm:^2.0.3" + string_decoder: "npm:^1.1.1" + util-deprecate: "npm:^1.0.1" + checksum: 10c0/e37be5c79c376fdd088a45fa31ea2e423e5d48854be7a22a58869b4e84d25047b193f6acb54f1012331e1bcd667ffb569c01b99d36b0bd59658fb33f513511b7 + languageName: node + linkType: hard + "readable-stream@npm:^4.2.0": version: 4.5.2 resolution: "readable-stream@npm:4.5.2" @@ -6177,7 +6365,7 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3.0.0": +"safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 @@ -6555,6 +6743,13 @@ __metadata: languageName: node linkType: hard +"split-ca@npm:^1.0.1": + version: 1.0.1 + resolution: "split-ca@npm:1.0.1" + checksum: 10c0/f339170b84c6b4706fcf4c60cc84acb36574c0447566bd713301a8d9b4feff7f4627efc8c334bec24944a3e2f35bc596bd58c673c9980d6bfe3137aae1116ba7 + languageName: node + linkType: hard + "split2@npm:^4.0.0, split2@npm:^4.1.0": version: 4.2.0 resolution: "split2@npm:4.2.0" @@ -6585,6 +6780,23 @@ __metadata: languageName: node linkType: hard +"ssh2@npm:^1.15.0": + version: 1.15.0 + resolution: "ssh2@npm:1.15.0" + dependencies: + asn1: "npm:^0.2.6" + bcrypt-pbkdf: "npm:^1.0.2" + cpu-features: "npm:~0.0.9" + nan: "npm:^2.18.0" + dependenciesMeta: + cpu-features: + optional: true + nan: + optional: true + checksum: 10c0/7c76888fbfa1c15660cf51086a6e5699b3c1caad516e29adb1d2a00fc1ef6b48946ca7ec811b4bb50456984967c4346115c7ddd3dbf981a1193bd1f40fa4529a + languageName: node + linkType: hard + "ssri@npm:^10.0.0, ssri@npm:^10.0.5": version: 10.0.5 resolution: "ssri@npm:10.0.5" @@ -6667,7 +6879,7 @@ __metadata: languageName: node linkType: hard -"string_decoder@npm:^1.3.0": +"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" dependencies: @@ -6790,6 +7002,31 @@ __metadata: languageName: node linkType: hard +"tar-fs@npm:~2.0.1": + version: 2.0.1 + resolution: "tar-fs@npm:2.0.1" + dependencies: + chownr: "npm:^1.1.1" + mkdirp-classic: "npm:^0.5.2" + pump: "npm:^3.0.0" + tar-stream: "npm:^2.0.0" + checksum: 10c0/0128e888b61c7c4e8e7997d66ceccc3c79d73c01e87cfcc3d9f6b8555b0c88b8d67d91ff167f00b067f726dde497b2d1fb2bba0cfcb3ccb95ae413cb86c715bc + languageName: node + linkType: hard + +"tar-stream@npm:^2.0.0": + version: 2.2.0 + resolution: "tar-stream@npm:2.2.0" + dependencies: + bl: "npm:^4.0.3" + end-of-stream: "npm:^1.4.1" + fs-constants: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + checksum: 10c0/2f4c910b3ee7196502e1ff015a7ba321ec6ea837667220d7bcb8d0852d51cb04b87f7ae471008a6fb8f5b1a1b5078f62f3a82d30c706f20ada1238ac797e7692 + languageName: node + linkType: hard + "tar@npm:^6.1.11, tar@npm:^6.1.2, tar@npm:^6.2.1": version: 6.2.1 resolution: "tar@npm:6.2.1" @@ -6991,6 +7228,13 @@ __metadata: languageName: node linkType: hard +"tweetnacl@npm:^0.14.3": + version: 0.14.5 + resolution: "tweetnacl@npm:0.14.5" + checksum: 10c0/4612772653512c7bc19e61923fbf42903f5e0389ec76a4a1f17195859d114671ea4aa3b734c2029ce7e1fa7e5cc8b80580f67b071ecf0b46b5636d030a0102a2 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -7229,7 +7473,7 @@ __metadata: languageName: node linkType: hard -"util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": +"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 From 4554349d0bd5959efc70a020031e7a204888cb54 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Sat, 20 Apr 2024 09:02:06 +0200 Subject: [PATCH 12/18] feat: add stop command In addition, this commit includes some refactoring and bug fixes that are difficult to untie and turn into atomic commits --- src/applications/bamboo.ts | 26 ++--- src/applications/base.ts | 17 ++++ src/applications/bitbucket.ts | 27 +++--- src/applications/confluence.ts | 34 +++---- src/applications/jira.ts | 27 ++---- src/commands/database.ts | 6 +- .../{database-mssql.ts => database/mssql.ts} | 4 +- .../{database-mysql.ts => database/mysql.ts} | 4 +- .../postgres.ts} | 4 +- src/commands/reset.ts | 2 +- .../{reset-bamboo.ts => reset/bamboo.ts} | 6 +- .../bitbucket.ts} | 6 +- .../confluence.ts} | 6 +- src/commands/{reset-jira.ts => reset/jira.ts} | 6 +- src/commands/run.ts | 10 +- src/commands/{run-bamboo.ts => run/bamboo.ts} | 4 +- .../{run-bitbucket.ts => run/bitbucket.ts} | 4 +- .../{run-confluence.ts => run/confluence.ts} | 4 +- src/commands/{run-jira.ts => run/jira.ts} | 4 +- src/commands/start.ts | 96 ++++++++++--------- src/commands/stop.ts | 28 ++++++ src/commands/stop/bamboo.ts | 38 ++++++++ src/commands/stop/bitbucket.ts | 38 ++++++++ src/commands/stop/confluence.ts | 38 ++++++++ src/commands/stop/jira.ts | 38 ++++++++ src/index.ts | 7 +- src/types/ApplicationOptions.ts | 1 + 27 files changed, 336 insertions(+), 149 deletions(-) rename src/commands/{database-mssql.ts => database/mssql.ts} (93%) rename src/commands/{database-mysql.ts => database/mysql.ts} (93%) rename src/commands/{database-postgres.ts => database/postgres.ts} (93%) rename src/commands/{reset-bamboo.ts => reset/bamboo.ts} (83%) rename src/commands/{reset-bitbucket.ts => reset/bitbucket.ts} (82%) rename src/commands/{reset-confluence.ts => reset/confluence.ts} (82%) rename src/commands/{reset-jira.ts => reset/jira.ts} (83%) rename src/commands/{run-bamboo.ts => run/bamboo.ts} (93%) rename src/commands/{run-bitbucket.ts => run/bitbucket.ts} (92%) rename src/commands/{run-confluence.ts => run/confluence.ts} (93%) rename src/commands/{run-jira.ts => run/jira.ts} (94%) create mode 100644 src/commands/stop.ts create mode 100644 src/commands/stop/bamboo.ts create mode 100644 src/commands/stop/bitbucket.ts create mode 100644 src/commands/stop/confluence.ts create mode 100644 src/commands/stop/jira.ts diff --git a/src/applications/bamboo.ts b/src/applications/bamboo.ts index 123639b..9d64dde 100644 --- a/src/applications/bamboo.ts +++ b/src/applications/bamboo.ts @@ -52,6 +52,15 @@ RUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo` } } + protected getJVMArgs(): Array { + const JVM_SUPPORT_RECOMMENDED_ARGS = super.getJVMArgs(); + if (this.options.debug) { + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xdebug'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005'); + } + return JVM_SUPPORT_RECOMMENDED_ARGS; + } + protected async isApplicationReady(): Promise { try { const response = await axios.get(`${this.baseUrl}/setup/setupGeneralConfiguration.action`, { validateStatus: () => true }).catch(() => null); @@ -66,7 +75,7 @@ RUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo` private getEnvironmentVariables() { return { ...this.options.contextPath ? { 'ATL_TOMCAT_CONTEXTPATH': this.options.contextPath } : '', - ...this.options.debug ? { 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs() } : '', + 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs().join(' '), 'ATL_BAMBOO_ENABLE_UNATTENDED_SETUP': 'true', 'ATL_LICENSE': this.options.license || timebomb.bamboo, 'ATL_JDBC_URL': this.database.url, @@ -76,21 +85,6 @@ RUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo` } }; - private getJVMArgs(): string { - const JVM_SUPPORT_RECOMMENDED_ARGS = [] - if (this.options.debug) { - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dupm.plugin.upload.enabled=true'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xdebug'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005'); - } - - if (this.options.quickReload) { - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dquickreload.dirs=/opt/quickreload'); - } - - return JVM_SUPPORT_RECOMMENDED_ARGS.join(' '); - } - private getVolumes() { return [ ...this.options.quickReload ? [ `${this.options.quickReload}:/opt/quickreload` ] : '' diff --git a/src/applications/base.ts b/src/applications/base.ts index 2c50c5b..eded3e0 100644 --- a/src/applications/base.ts +++ b/src/applications/base.ts @@ -102,6 +102,23 @@ export abstract class Base extends EventEmitter { protected abstract getService(): Service; + protected getJVMArgs(): Array { + const JVM_SUPPORT_RECOMMENDED_ARGS = []; + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dupm.plugin.upload.enabled=true'); + + if (this.options.devMode) { + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Djira.dev.mode=true'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Datlassian.dev.mode=true'); + } + + if (this.options.quickReload) { + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dquickreload.dirs=/opt/quickreload'); + } + + return JVM_SUPPORT_RECOMMENDED_ARGS; + } + + protected async isApplicationReady(): Promise { try { const response = await axios.get<{ state: string }>(`${this.baseUrl}/status`, { validateStatus: () => true }).catch(() => null); diff --git a/src/applications/bitbucket.ts b/src/applications/bitbucket.ts index 22ec401..46ea5f8 100644 --- a/src/applications/bitbucket.ts +++ b/src/applications/bitbucket.ts @@ -54,11 +54,21 @@ RUN chown -R bitbucket:bitbucket /var/atlassian/application-data/bitbucket` } } + protected getJVMArgs(): Array { + const JVM_SUPPORT_RECOMMENDED_ARGS = super.getJVMArgs(); + if (this.options.debug) { + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xdebug'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005'); + } + return JVM_SUPPORT_RECOMMENDED_ARGS; + } + + // ------------------------------------------------------------------------------------------ Private Methods private getEnvironmentVariables() { return { - ...this.options.debug ? { 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs() } : '', + 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs().join(' '), 'JDBC_URL': this.database.url, 'JDBC_USER': this.database.options.username, 'JDBC_PASSWORD': this.database.options.password, @@ -66,21 +76,6 @@ RUN chown -R bitbucket:bitbucket /var/atlassian/application-data/bitbucket` } }; - private getJVMArgs(): string { - const JVM_SUPPORT_RECOMMENDED_ARGS = [] - if (this.options.debug) { - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dupm.plugin.upload.enabled=true'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xdebug'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005'); - } - - if (this.options.quickReload) { - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dquickreload.dirs=/opt/quickreload'); - } - - return JVM_SUPPORT_RECOMMENDED_ARGS.join(' '); - } - private getVolumes() { return [ ...this.options.quickReload ? [ `${this.options.quickReload}:/opt/quickreload` ] : '' diff --git a/src/applications/confluence.ts b/src/applications/confluence.ts index adf6a45..4718073 100644 --- a/src/applications/confluence.ts +++ b/src/applications/confluence.ts @@ -50,12 +50,25 @@ RUN chown -R confluence:confluence /opt/atlassian/confluence` } } + protected getJVMArgs(): Array { + const JVM_SUPPORT_RECOMMENDED_ARGS = super.getJVMArgs(); + if (this.options.debug) { + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xdebug'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xrunjdwp:transport=dt_socket,address=*:5005,server=y,suspend=n'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dcom.sun.management.jmxremote.port=9999'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dcom.sun.management.jmxremote.rmi.port=9998'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dcom.sun.management.jmxremote.authenticate=false'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dcom.sun.management.jmxremote.ssl=false'); + } + return JVM_SUPPORT_RECOMMENDED_ARGS; + } + // ------------------------------------------------------------------------------------------ Private Methods private getEnvironmentVariables() { return { ...this.options.contextPath ? { 'ATL_TOMCAT_CONTEXTPATH': this.options.contextPath } : '', - ...this.options.debug ? { 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs() } : '', + 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs().join(' '), 'ATL_LICENSE_KEY': this.options.license || timebomb.confluence, 'ATL_JDBC_URL': this.database.url, 'ATL_JDBC_USER': this.database.options.username, @@ -64,25 +77,6 @@ RUN chown -R confluence:confluence /opt/atlassian/confluence` } }; - private getJVMArgs(): string { - const JVM_SUPPORT_RECOMMENDED_ARGS = [] - if (this.options.debug) { - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dupm.plugin.upload.enabled=true'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xdebug'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xrunjdwp:transport=dt_socket,address=*:5005,server=y,suspend=n'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dcom.sun.management.jmxremote.port=9999'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dcom.sun.management.jmxremote.rmi.port=9998'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dcom.sun.management.jmxremote.authenticate=false'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dcom.sun.management.jmxremote.ssl=false'); - } - - if (this.options.quickReload) { - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dquickreload.dirs=/opt/quickreload'); - } - - return JVM_SUPPORT_RECOMMENDED_ARGS.join(' '); - } - private getVolumes() { return [ ...this.options.quickReload ? [ `${this.options.quickReload}:/opt/quickreload` ] : '' diff --git a/src/applications/jira.ts b/src/applications/jira.ts index c33eada..bd95f80 100644 --- a/src/applications/jira.ts +++ b/src/applications/jira.ts @@ -50,6 +50,15 @@ RUN chown -R jira:jira /var/atlassian/application-data/jira` } } + protected getJVMArgs(): Array { + const JVM_SUPPORT_RECOMMENDED_ARGS = super.getJVMArgs(); + if (this.options.debug) { + JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xdebug'); + JVM_SUPPORT_RECOMMENDED_ARGS.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005'); + } + return JVM_SUPPORT_RECOMMENDED_ARGS; + } + // ------------------------------------------------------------------------------------------ Private Methods private getEnvironmentVariables() { @@ -59,7 +68,7 @@ RUN chown -R jira:jira /var/atlassian/application-data/jira` return { ...this.options.contextPath ? { 'ATL_TOMCAT_CONTEXTPATH': this.options.contextPath } : '', - ...this.options.debug ? { 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs() } : '', + 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs().join(' '), 'ATL_LICENSE_KEY': this.options.license || timebomb.confluence, 'ATL_JDBC_URL': this.database.url, 'ATL_JDBC_USER': this.database.options.username, @@ -70,22 +79,6 @@ RUN chown -R jira:jira /var/atlassian/application-data/jira` } }; - private getJVMArgs(): string { - const JVM_SUPPORT_RECOMMENDED_ARGS = [] - if (this.options.debug) { - - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dupm.plugin.upload.enabled=true'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Xdebug'); - JVM_SUPPORT_RECOMMENDED_ARGS.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005'); - } - - if (this.options.quickReload) { - JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dquickreload.dirs=/opt/quickreload'); - } - - return JVM_SUPPORT_RECOMMENDED_ARGS.join(' '); - } - private getVolumes() { return [ ...this.options.quickReload ? [ `${this.options.quickReload}:/opt/quickreload` ] : '' diff --git a/src/commands/database.ts b/src/commands/database.ts index 2c267c1..4376bc8 100644 --- a/src/commands/database.ts +++ b/src/commands/database.ts @@ -4,9 +4,9 @@ import { program } from 'commander'; program .name('dcdx database') - .command('postgresql', 'Start PostgreSQL', { executableFile: './database-postgres.js'}) - .command('mysql', 'Start MySQL', { executableFile: './database-mysql.js'}) - .command('mssql', 'Start Microsoft SQL Server', { executableFile: './database-mssql.js'}) + .command('postgresql', 'Start PostgreSQL', { executableFile: './database/postgres.js'}) + .command('mysql', 'Start MySQL', { executableFile: './database/mysql.js'}) + .command('mssql', 'Start Microsoft SQL Server', { executableFile: './database/mssql.js'}) .showHelpAfterError(true); program.parse(); diff --git a/src/commands/database-mssql.ts b/src/commands/database/mssql.ts similarity index 93% rename from src/commands/database-mssql.ts rename to src/commands/database/mssql.ts index 1a49ea6..0f97d21 100644 --- a/src/commands/database-mssql.ts +++ b/src/commands/database/mssql.ts @@ -3,8 +3,8 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { mssql as versions } from '../../assets/versions.json'; -import { MSSQL, MSSQLOptions } from '../databases/mssql'; +import { mssql as versions } from '../../../assets/versions.json'; +import { MSSQL, MSSQLOptions } from '../../databases/mssql'; (async () => { const options = program diff --git a/src/commands/database-mysql.ts b/src/commands/database/mysql.ts similarity index 93% rename from src/commands/database-mysql.ts rename to src/commands/database/mysql.ts index 5e8c474..bc6a113 100644 --- a/src/commands/database-mysql.ts +++ b/src/commands/database/mysql.ts @@ -3,8 +3,8 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { mysql as versions } from '../../assets/versions.json'; -import { MySQL } from '../databases/mysql'; +import { mysql as versions } from '../../../assets/versions.json'; +import { MySQL } from '../../databases/mysql'; (async () => { const options = program diff --git a/src/commands/database-postgres.ts b/src/commands/database/postgres.ts similarity index 93% rename from src/commands/database-postgres.ts rename to src/commands/database/postgres.ts index 5939e84..d3a5ad0 100644 --- a/src/commands/database-postgres.ts +++ b/src/commands/database/postgres.ts @@ -3,8 +3,8 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { postgres as versions } from '../../assets/versions.json'; -import { Postgres } from '../databases/postgres'; +import { postgres as versions } from '../../../assets/versions.json'; +import { Postgres } from '../../databases/postgres'; (async () => { const options = program diff --git a/src/commands/reset.ts b/src/commands/reset.ts index 2458499..501a3e0 100644 --- a/src/commands/reset.ts +++ b/src/commands/reset.ts @@ -2,7 +2,7 @@ import { Option, program } from 'commander'; -import { AMPS } from '../applications/amps'; +import { AMPS } from '../helpers/amps'; import { SupportedApplications } from '../types/SupportedApplications'; // Check if there is a command in the arguments diff --git a/src/commands/reset-bamboo.ts b/src/commands/reset/bamboo.ts similarity index 83% rename from src/commands/reset-bamboo.ts rename to src/commands/reset/bamboo.ts index eecd907..f50f7cd 100644 --- a/src/commands/reset-bamboo.ts +++ b/src/commands/reset/bamboo.ts @@ -3,9 +3,9 @@ import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; -import { bamboo as versions } from '../../assets/versions.json'; -import { AMPS } from '../applications/amps'; -import { Bamboo } from '../applications/bamboo'; +import { bamboo as versions } from '../../../assets/versions.json'; +import { Bamboo } from '../../applications/bamboo'; +import { AMPS } from '../../helpers/amps'; const version = AMPS.getApplicationVersion() || 'latest'; diff --git a/src/commands/reset-bitbucket.ts b/src/commands/reset/bitbucket.ts similarity index 82% rename from src/commands/reset-bitbucket.ts rename to src/commands/reset/bitbucket.ts index a9af885..bd7d5c5 100644 --- a/src/commands/reset-bitbucket.ts +++ b/src/commands/reset/bitbucket.ts @@ -3,9 +3,9 @@ import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; -import { bitbucket as versions } from '../../assets/versions.json'; -import { AMPS } from '../applications/amps'; -import { Bitbucket } from '../applications/bitbucket'; +import { bitbucket as versions } from '../../../assets/versions.json'; +import { Bitbucket } from '../../applications/bitbucket'; +import { AMPS } from '../../helpers/amps'; const version = AMPS.getApplicationVersion() || 'latest'; diff --git a/src/commands/reset-confluence.ts b/src/commands/reset/confluence.ts similarity index 82% rename from src/commands/reset-confluence.ts rename to src/commands/reset/confluence.ts index 0722c26..917055a 100644 --- a/src/commands/reset-confluence.ts +++ b/src/commands/reset/confluence.ts @@ -3,9 +3,9 @@ import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; -import { confluence as versions } from '../../assets/versions.json'; -import { AMPS } from '../applications/amps'; -import { Confluence } from '../applications/confluence'; +import { confluence as versions } from '../../../assets/versions.json'; +import { Confluence } from '../../applications/confluence'; +import { AMPS } from '../../helpers/amps'; const version = AMPS.getApplicationVersion() || 'latest'; diff --git a/src/commands/reset-jira.ts b/src/commands/reset/jira.ts similarity index 83% rename from src/commands/reset-jira.ts rename to src/commands/reset/jira.ts index 965a182..f9955f7 100644 --- a/src/commands/reset-jira.ts +++ b/src/commands/reset/jira.ts @@ -3,9 +3,9 @@ import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; -import { jira as versions } from '../../assets/versions.json'; -import { AMPS } from '../applications/amps'; -import { Jira } from '../applications/jira'; +import { jira as versions } from '../../../assets/versions.json'; +import { Jira } from '../../applications/jira'; +import { AMPS } from '../../helpers/amps'; const version = AMPS.getApplicationVersion() || 'latest'; diff --git a/src/commands/run.ts b/src/commands/run.ts index c539997..fd53e30 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -2,7 +2,7 @@ import { Option, program } from 'commander'; -import { AMPS } from '../applications/amps'; +import { AMPS } from '../helpers/amps'; import { SupportedApplications } from '../types/SupportedApplications'; // Check if there is a command in the arguments @@ -19,10 +19,10 @@ if (isDefaultCommand) { program .name('dcdx run') .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) - .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'}) + .command('bamboo', 'Start Atlassian Bamboo (standalone)', { executableFile: './run/bamboo.js'}) + .command('bitbucket', 'Start Atlassian Bitbucket (standalone)', { executableFile: './run/bitbucket.js'}) + .command('confluence', 'Start Atlassian Confluence (standalone)', { executableFile: './run/confluence.js'}) + .command('jira', 'Start Atlassian Jira (standalone)', { executableFile: './run/jira.js'}) .showHelpAfterError(true); program.parse(process.argv); \ No newline at end of file diff --git a/src/commands/run-bamboo.ts b/src/commands/run/bamboo.ts similarity index 93% rename from src/commands/run-bamboo.ts rename to src/commands/run/bamboo.ts index 751804e..e90befb 100644 --- a/src/commands/run-bamboo.ts +++ b/src/commands/run/bamboo.ts @@ -3,8 +3,8 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { bamboo as versions } from '../../assets/versions.json'; -import { Bamboo } from '../applications/bamboo'; +import { bamboo as versions } from '../../../assets/versions.json'; +import { Bamboo } from '../../applications/bamboo'; (async () => { const options = program diff --git a/src/commands/run-bitbucket.ts b/src/commands/run/bitbucket.ts similarity index 92% rename from src/commands/run-bitbucket.ts rename to src/commands/run/bitbucket.ts index f92239f..9413a90 100644 --- a/src/commands/run-bitbucket.ts +++ b/src/commands/run/bitbucket.ts @@ -3,8 +3,8 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { bitbucket as versions } from '../../assets/versions.json'; -import { Bitbucket } from '../applications/bitbucket'; +import { bitbucket as versions } from '../../../assets/versions.json'; +import { Bitbucket } from '../../applications/bitbucket'; (async () => { const options = program diff --git a/src/commands/run-confluence.ts b/src/commands/run/confluence.ts similarity index 93% rename from src/commands/run-confluence.ts rename to src/commands/run/confluence.ts index ecd06d8..4442225 100644 --- a/src/commands/run-confluence.ts +++ b/src/commands/run/confluence.ts @@ -3,8 +3,8 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { confluence as versions } from '../../assets/versions.json'; -import { Confluence } from '../applications/confluence'; +import { confluence as versions } from '../../../assets/versions.json'; +import { Confluence } from '../../applications/confluence'; (async () => { const options = program diff --git a/src/commands/run-jira.ts b/src/commands/run/jira.ts similarity index 94% rename from src/commands/run-jira.ts rename to src/commands/run/jira.ts index 99367ca..6358937 100644 --- a/src/commands/run-jira.ts +++ b/src/commands/run/jira.ts @@ -3,8 +3,8 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { jira as versions } from '../../assets/versions.json'; -import { Jira } from '../applications/jira'; +import { jira as versions } from '../../../assets/versions.json'; +import { Jira } from '../../applications/jira'; (async () => { const options = program diff --git a/src/commands/start.ts b/src/commands/start.ts index eec4b77..1a80482 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -5,50 +5,52 @@ import { Option, program } from 'commander'; import { asyncExitHook, gracefulExit } from 'exit-hook'; import { cwd } from 'process'; -import { AMPS } from '../applications/amps'; +import { AMPS } from '../helpers/amps'; import { getApplicationByName } from '../helpers/getApplication'; import { isRecursiveBuild } from '../helpers/isRecursiveBuild'; import { showRecursiveBuildWarning } from '../helpers/showRecursiveBuildWarning'; -if (!AMPS.isAtlassianPlugin()) { - console.log('Unable to find an Atlassian Plugin project in the current directory 🤔'); - gracefulExit(); -} - -const application = AMPS.getApplication(); -if (!application) { - console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); - gracefulExit(); - process.exit(); -} - -const Application = getApplicationByName(application); -if (!Application) { - console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); - process.exit(); -} - const version = AMPS.getApplicationVersion(); (async () => { const options = program .name('dcdx start') - .description('Build & install the Atlassian Data Center plugin from the current directory.\nYou can add Maven build arguments after the command options.') + .description('Start the host application in dev mode based on the Atlassian Maven Plugin Suite (AMPS) configuration.\nYou can add Maven build arguments after the command options.') .usage('[options] [...maven_arguments]') .addOption(new Option('-v, --version ', 'The version of the host application').default(version)) .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('-qr, --quickReload', 'Build & install the app automatically using QuickReload')) .addOption(new Option('-o, --outputDirectory ', 'Output directory where QuickReload will look for generated JAR files').default('target')) .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(true)) + .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(false)) .allowUnknownOption(true) .parse(process.argv) .opts(); + if (!AMPS.isAtlassianPlugin()) { + console.log('Unable to find an Atlassian Plugin project in the current directory 🤔'); + gracefulExit(); + } + + const application = AMPS.getApplication(); + if (!application) { + console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + gracefulExit(); + process.exit(); + } + + const Application = getApplicationByName(application); + if (!Application) { + console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + process.exit(); + } + const instance = new Application({ + devMode: true, version: options.version, database: options.database, port: Number(options.port), @@ -63,32 +65,38 @@ const version = AMPS.getApplicationVersion(); mavenOpts.push(...[ '-P', options.activateProfiles ]); } - console.log('Watching filesystem for changes to source files (QuickReload)'); - let lastBuildCompleted = new Date().getTime(); - const quickReload = watch('**/*', { - cwd: cwd(), - usePolling: true, - interval: 2 * 1000, - binaryInterval: 2 * 1000, - awaitWriteFinish: true - }).on('change', async (path) => { - if (path.startsWith(options.outputDirectory) && path.toLowerCase().endsWith('.jar')) { - console.log('Found updated JAR file(s), uploading them to QuickReload'); - await instance.cp(path); - lastBuildCompleted = new Date().getTime(); - } else if (!path.startsWith(options.outputDirectory)) { - if (isRecursiveBuild(lastBuildCompleted)) { - showRecursiveBuildWarning(options.outputDirectory); - } else { - console.log('Detected file change, rebuilding Atlasian Plugin for QuickReload'); - await AMPS.build(mavenOpts).catch(() => Promise.resolve()); + let quickReload = null; + if (options.quickReload) { + console.log('Watching filesystem for changes to source files (QuickReload)'); + let lastBuildCompleted = new Date().getTime(); + quickReload = watch('**/*', { + cwd: cwd(), + usePolling: true, + interval: 2 * 1000, + binaryInterval: 2 * 1000, + awaitWriteFinish: true, + atomic: true + }).on('change', async (path) => { + if (path.startsWith(options.outputDirectory) && path.toLowerCase().endsWith('.jar')) { + console.log('Found updated JAR file(s), uploading them to QuickReload'); + await instance.cp(path); + lastBuildCompleted = new Date().getTime(); + } else if (!path.startsWith(options.outputDirectory)) { + if (isRecursiveBuild(lastBuildCompleted)) { + showRecursiveBuildWarning(options.outputDirectory); + } else { + console.log('Detected file change, rebuilding Atlasian Plugin for QuickReload'); + await AMPS.build(mavenOpts).catch(() => Promise.resolve()); + } } - } - }); + }); + } asyncExitHook(async () => { - console.log(`Stopping filesystem watcher... ⏳`); - await quickReload.close(); + if (quickReload) { + console.log(`Stopping filesystem watcher... ⏳`); + await quickReload.close(); + } console.log(`Stopping ${instance.name}... ⏳`); await instance.stop(); console.log(`Successfully stopped all running processes 💪`); diff --git a/src/commands/stop.ts b/src/commands/stop.ts new file mode 100644 index 0000000..fa68397 --- /dev/null +++ b/src/commands/stop.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; + +import { AMPS } from '../helpers/amps'; +import { SupportedApplications } from '../types/SupportedApplications'; + +// Check if there is a command in the arguments +const isDefaultCommand = !process.argv.some(item => Object.values(SupportedApplications).includes(item as SupportedApplications)); +// If there is no command, check if we are running this within the context of an Atlassian Plugin project +if (isDefaultCommand) { + const application = AMPS.getApplication(); + if (application) { + const args = [ application, ...process.argv.splice(2) ]; + process.argv = [ ...process.argv.slice(0, 2), ...args ]; + } +} + +program + .name('dcdx stop') + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .command('bamboo', 'Stop Atlassian Bamboo (standalone)', { executableFile: './stop/bamboo.js'}) + .command('bitbucket', 'Stop Atlassian Bitbucket (standalone)', { executableFile: './stop/bitbucket.js'}) + .command('confluence', 'Stop Atlassian Confluence (standalone)', { executableFile: './stop/confluence.js'}) + .command('jira', 'Stop Atlassian Jira (standalone)', { executableFile: './stop/jira.js'}) + .showHelpAfterError(true); + +program.parse(process.argv); \ No newline at end of file diff --git a/src/commands/stop/bamboo.ts b/src/commands/stop/bamboo.ts new file mode 100644 index 0000000..b293d8e --- /dev/null +++ b/src/commands/stop/bamboo.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { asyncExitHook, gracefulExit } from 'exit-hook'; + +import { bamboo as versions } from '../../../assets/versions.json'; +import { Bamboo as Application } from '../../applications/bamboo'; + +(async () => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .parse(process.argv) + .opts(); + + const instance = new Application({ + version: options.version, + database: options.database, + prune: options.prune + }); + + asyncExitHook(async () => { + console.log(`Stopping ${instance.name}... ⏳`); + await instance.stop(); + console.log(`Stopped ${instance.name} 💪`); + }, { + wait: 30 * 1000 + }); + + gracefulExit(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/commands/stop/bitbucket.ts b/src/commands/stop/bitbucket.ts new file mode 100644 index 0000000..40c1194 --- /dev/null +++ b/src/commands/stop/bitbucket.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { asyncExitHook, gracefulExit } from 'exit-hook'; + +import { bitbucket as versions } from '../../../assets/versions.json'; +import { Bitbucket as Application } from '../../applications/bitbucket'; + +(async () => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .parse(process.argv) + .opts(); + + const instance = new Application({ + version: options.version, + database: options.database, + prune: options.prune + }); + + asyncExitHook(async () => { + console.log(`Stopping ${instance.name}... ⏳`); + await instance.stop(); + console.log(`Stopped ${instance.name} 💪`); + }, { + wait: 30 * 1000 + }); + + gracefulExit(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/commands/stop/confluence.ts b/src/commands/stop/confluence.ts new file mode 100644 index 0000000..d729064 --- /dev/null +++ b/src/commands/stop/confluence.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { asyncExitHook, gracefulExit } from 'exit-hook'; + +import { confluence as versions } from '../../../assets/versions.json'; +import { Confluence as Application } from '../../applications/confluence'; + +(async () => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .parse(process.argv) + .opts(); + + const instance = new Application({ + version: options.version, + database: options.database, + prune: options.prune + }); + + asyncExitHook(async () => { + console.log(`Stopping ${instance.name}... ⏳`); + await instance.stop(); + console.log(`Stopped ${instance.name} 💪`); + }, { + wait: 30 * 1000 + }); + + gracefulExit(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/commands/stop/jira.ts b/src/commands/stop/jira.ts new file mode 100644 index 0000000..e8bb181 --- /dev/null +++ b/src/commands/stop/jira.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { asyncExitHook, gracefulExit } from 'exit-hook'; + +import { jira as versions } from '../../../assets/versions.json'; +import { Jira as Application } from '../../applications/jira'; + +(async () => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .parse(process.argv) + .opts(); + + const instance = new Application({ + version: options.version, + database: options.database, + prune: options.prune + }); + + asyncExitHook(async () => { + console.log(`Stopping ${instance.name}... ⏳`); + await instance.stop(); + console.log(`Stopped ${instance.name} 💪`); + }, { + wait: 30 * 1000 + }); + + gracefulExit(); +})(); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index c49e809..8a995ee 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,7 @@ program // ------------------------------------------------------------------------------------------ Start program - .command('start', 'Build & install the Atlassian Data Center plugin from the current directory', { executableFile: './commands/start.js' }); + .command('start', 'Start the host application based on the Atlassian Maven Plugin Suite (AMPS) configuration', { executableFile: './commands/start.js' }); // ------------------------------------------------------------------------------------------ Run @@ -94,6 +94,11 @@ program program.parse(process.argv); }); +// ------------------------------------------------------------------------------------------ Stop + +program + .command('stop', 'Stop the host application and database', { executableFile: './commands/stop.js' }); + // ------------------------------------------------------------------------------------------ Reset program diff --git a/src/types/ApplicationOptions.ts b/src/types/ApplicationOptions.ts index 1f77fb4..f8ac837 100644 --- a/src/types/ApplicationOptions.ts +++ b/src/types/ApplicationOptions.ts @@ -9,5 +9,6 @@ export type ApplicationOptions = { license?: string; clean?: boolean; prune?: boolean; + devMode?: boolean; debug?: boolean; } \ No newline at end of file From f217b75d0778f5b2b0621b0a95c523fc328bf0d1 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Sat, 20 Apr 2024 09:07:30 +0200 Subject: [PATCH 13/18] feat: add stop command In addition, this commit includes some refactoring and bug fixes that are difficult to untie and turn into atomic commits --- rollup.config.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 20512a5..e33121d 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -4,12 +4,26 @@ import json from '@rollup/plugin-json'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import { readdirSync } from 'fs'; +import path from 'path'; import executable from 'rollup-plugin-executable'; -const commands = readdirSync('./src/commands'); +const getCommands = (dir) => { + const result = []; + const commands = readdirSync(dir, { withFileTypes: true }); + commands.forEach(item => { + if (item.isDirectory()) { + result.push(...getCommands(path.join(dir, item.name))); + } else { + result.push(path.join(dir, item.name)); + } + }); + return result; +} + +const commands = getCommands('./src/commands'); export default [ - ...[ 'index.js', ...commands.map(item => `commands/${item.replace('ts', 'js')}`) ].map(file => ({ + ...[ 'index.js', ...commands.map(item => item.replace('src/', '').replace('ts', 'js')) ].map(file => ({ input: `./dist/src/${file}`, output: { file: `./lib/${file}`, From d97709ddd0f3e2d60c6c3042ecd17926dbafd39f Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Tue, 23 Apr 2024 12:21:08 +0200 Subject: [PATCH 14/18] chore: update QuickReload to support Platform 7 --- ...reload-5.0.2.jar => quickreload-5.0.4.jar} | Bin 2550133 -> 2338789 bytes src/applications/bamboo.ts | 2 +- src/applications/bitbucket.ts | 2 +- src/applications/confluence.ts | 2 +- src/applications/jira.ts | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename assets/{quickreload-5.0.2.jar => quickreload-5.0.4.jar} (88%) diff --git a/assets/quickreload-5.0.2.jar b/assets/quickreload-5.0.4.jar similarity index 88% rename from assets/quickreload-5.0.2.jar rename to assets/quickreload-5.0.4.jar index 385f1f0054d0eab64c17d6fe449b2a0aae6cfb66..227c1e3e25724fdefff6ee71c2a49b23ab8d466a 100644 GIT binary patch delta 44868 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

EDLyrKses- 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 GfBysKC9Ycl delta 258151 zcmZU(19T<9_B|Y%6Ki751QXk~ZBA_C#gCno>W*fTJ;!-%NQl?EIu4pwkD&nVzO_K@K%@+U!oIe5V2mwQS(G>JvUb)`>^Id% zup+`c`03%v>YNDif~S-_?t!1`b!neyzPm&m#ll(O_ZtugG9bT)PS`I=0W6`WYvpf8 zQi$8pq64Q+9Y%l9X@>vmMJubRok6O>NQ>&+rP4{9YJk=+cIZc?8@Bf8R4mM%uoYlN z#8Dy4Zt0$onoxHAazeMONqOD`sV1vl!aveLJhN<`uJ$#sed2GQsgCb8l0RC6Z1toM$}7VJpv#;ERiIO6g|5(L z$ixnVWU?%>(Z&*Qa5vFm;-{*=Rp`Fh*ZY`%^ymOIRIcjQG*I_8q9SA7O73t67n1}b zJ6IeS+`%2yCETUWCHO8up4wP2#lw!9cse{>Cw5KB?M?WO(BmC81d@OYvLX?b|FmTZ z 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 z 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ξHfqED!_5;|xcsbV-!(BLIC*afoekwoyQ%fCMFDcG_1W3E0HQ%?A| zMes3T-hXbz?3oi^E79RW1nsRt7!9%X8g&oIkMrp6^bJATH*+7b`6MBOtW`cC?(7}FZ+{A3(Y|64+y43l zpStcbzH7|;5tU-hkMa{EzWq56Rt&qFAjB0FEm zB8SuDvkSF7F@&QDq)hMQS40j-8WO2DXPeT}S?D2%nNqjz-IrYIF+RV@ZA*bq&P}(VE+ENC@CNh-zssDq>zE54$8- zF56l6rO~7Ze>Q4Gx;#)e=k>~2W&?)zOb2T_ApO^|p zu2R!d`-dld%i0Q&1@RY}Gc=Yi0uZF<)_BOciY2a9fSsD4&4*cC3cy+S;_aEHjx-(H-N6g*a7tY<^!of*MERI zf05ty{-nR5f*9&)YSzk!2y~cXmarC%3>9dZoGFSG8mkfvGnZE@=mvU|46BQwi#2AK zob$}FET#xR0hJ{q&szy5r-igu?V8ysD`ZR2S(X;{)jTOSQ!E&(Hh|e&Q}btrfug~@ zrJd`)Mi5cy6I4PINv5UDMN6bh<9`!UoKwOj(jv-MjSH7 z$85`sl3~Lpa4iDY!6;J6HuFvu+v|{)^)3x5o^ooaTvJO&Q>aU$&&y+r{D`9dGYVMG z;QA07T;&+k-C%fb8DKEvC3vH;hmCaDAv%U6gzER6+lZDGh>jEj6^bS<&wskpM8}QQ zrP##7_gR&x%DggnQCk+3T8H~w+83O?9%qOvny9f9O(4>Lrq?g$fde@EioPG5FFqA`Tc@X{gcCB`#qSYQIF!HeV~25*F+tbhjG> z7M`S%zr>v#EXi8t#6+y03g$PNUSXe*I+7?OSssKIzeGQAB{apu9el0-NIRkN;{vlZQHhO+cqj~+qP}nwryvnGwZx> z_vv%-#UK4e$KRKG?|*f*W6d?knsdxCjEG&GN~LjkN!pdjwVDsa>&N6Ua5^0(wTmy} z$`UV&t!hgRE_dNe&693=X=fHP|T)cRg|;M6efH~@D0X=%G5exHQ&0y zY~QISvb|#_zx4^m@g8{v?2pXr^VsG0^6aqS+C>insAk2nZGRRCvYc}a-rg?N&=Yog zR`3yMHbu?MN^7E~sH2Wh9p5rnO-bqb>DFE8yMEvmbQvyawI!p;y#HSG#@1Qtktp;? zxLdT7dLU76xCQebe}#VaE2hBPDPWBNYW~?wI-p+IdMD((DBao#Tk7!B-V-~7g9q-Y z@LS_)j;_Cs@qapcE+b(>P(LI?pA-Rh7+4FzS2)h**{+ZR`6?Ey6>%mg!KXg%&wx}I zhQFsj^k$f~zyvdiUtsJrtJ#3x-dYzG0J%-LiUQR)JS7SpnGKS3FU@zyzX`F(60e_A zq%aCuxxDiZ)J{po7fp^K$d2CMu0HG*yLCmIsnV8$*neZjL8_liJdR6XON1`r$QQJA zE)*r-#Q^(j2|D44e!m2f@EYwL>lZ)z=~?a=PzD5rY~!s0J*veYk__Z~-t!(wMcM)I zpuLN)x{4Q~g-gpxpi_aVmQZ|1vWlc@eI}BX6CxL`SF%p^TYKmLGNq20n6% zvIywqU?+$psv$vs(sA)zCC9)|#BjJ^=S2AWM~#N~_cSIp7!Xi1?7usL1^+X&CSvPi z^Z!vgEmqq6+uV=mQhN(MwJ5(SNva)8d`mHvSAP(Nf>IecIC2SzU>-5C~phD*h|@P!)0ob1}p``0Y$iI!B`!_s8$kQ#zoPI#of$)@8@hUoo9OE!)iI z?t5auIWIUdVT%2cms-H!xkG|yYMAV=`r>LgcG50= z4ojNOCMLTrX@HuO9KG1zueg%j0nC`xz{0ikO9=0ZND1A zkzicB%Z!GDqE(R9>i7%ob7B~jNrM@vzje~#B)vZWsHhB%W+YBKvof@IXr9iZQ-6k> zr;*vL9y?HnFe&-fB31eILfOtAF7?xcRVymsfSH#*FL=$&tM$WpR2p{&rx2J}og++X z@&=ju2;K;xx`(e2f(d?vXf&LXrwKZwQyZrg5YrfEQr@hMF3Djfy9;Pcn5pCCAIHX- zfsbB=7%ue~Nq)qupf{isrMyWLEq{>iR1)p5UQeji#mKY29tMYw2P--E#&g%qSL}1z z8h;SlnwCl6bcw4b(<7snl$$doM(O>%amDJjE+v40fKdOfeF^=4v~mBrO`A0=^s$x^ zbK+yo>{vrZ;7&ufT8X&O!ja=lI27co@W%tLv=gXUutF=$4NYY7Kv~$|MXlPE3EVEkpn(uhq?XW%W%ybQUykCOj z0{?RC5)8nq3(D64#!4L(kBbe&fQ!#V2xY`36+ld3#HRHhI);EWj)_gu2THliRT#GC zi7{mtCFDq<8fD~`4It?93V)mDiP?9jC8N(G?-!@upCArNBtbVKSBfyemNj9IZj?S9 zg9%|GNi%nzr3@+W>Wrdl#NZ@OvwccJ6T-MH-O$LLBP1Wa( zwdg@y6by-Mt1@m=4;bz;tDkQS+K1H68JnZ3C3Z)kDgj!00;|%QrlL4jkw3=WuhGDiw+3(tez8O8t9%v9S$r>U7>56%B4Kr)pmhNP;?+HFw zjdaDzIcw<7n!1UeL)4E8!kxPBp`9~p-wC>kxd#U0M&A{;F9Oj(hN^^AV_sG*L!gI7 z1mirqp%s8r9nr|Jl%b3S0Y+QPgust}4Lhnek#TTeQl_}Fgnt^FoIZt0fC5e^Q!n4Z zlaD`jGug7x$>3~SMvWQ4l6+o+Mw7q)<-+!xMOJO@R40wvdgN6i79L8~$THMoB;)V{R_#u3l=1g}Y_oO|dPX2U@lC zE|A$=>%d`!TqgCb%kjXydK*u?yy19*JmU@$WGaWtemF@oDdOJT%@xmf3EFGeb_SZS zx!ZHpX1~Tg`*)mhdG1YF z`9AN7)_?DMgFt(RA}pm5A>HiU6<1TZE(Q_}(UFl1#X_vmQK-<;$ic+@IG*OFKOB{n zU$#Ayrw{4u>7l+^(b141*1Lhz%q3N|m5<6qNynNtTU%?V?o@+HM-`7B+s(9=sz(#Y z3zMwY%BOj>z)Yo!mo?R{@74Ap1(H<^NeEd1eSgZXx_IC;S)x1cY3$L;Y(F!5rXPOQ zw(MNH)Y`8uNJ7cMJ6u9t7BB0#cY4&4^@mtZhy zWPhJWWR*T=V9&P>quejzrAFT^8bbZ;0Z7l-oF?FtARlnW!-#(qWj(U4> z-VUr{M2ES^Ei5&9T)k@O2-1X>`G~AFAeyJqOc%l96?Hn<5o1}#`-V|Bm#X<&C!pW8NPJ{!!ypkr>r((m@s2de2do$Lh7i0fg z*eWdo`TeuB;l(>~@~*^$bARHtwc&io3ERNk$?87zv8LC6cWFo7x0j|V>9o_#QheE{vRu_UjM&6Y_)x_MHIvhvqyK?7IQ-56*cl#HR}KH|BXQ z^d~Ixx7Wfh%BvRXr_I7H;%g`ACwx&a+1-oGD|nF~`CY8@I7D$MhQ?=jQ*aT1gvSOi zKdiOYv%rpk&k@-ZRXnJ@Ag^+NZhNu;w4Z-(0(A-W+wOS4V}BJ1mMxV*wbbu+u!4=fS;zKP6pb%b?gb_j&w6!Lu{!YHl z#SzN(I97OnP8NgwaQU6T)%>uA7h%t@7IH$fTnM`94*OHy749lXfHG{yNBjGHD~UG*5X{%$z>qPJuFNMIWYlq{7}BDu1RiR;vH(i(WSv5>uMkI{_+D ziGG-u1uW6UJLBgfWN!Yz6v|XXYO#Jv%fFjK(H!}bVC%3fUdjQRO;GUAY ziB7U#kS9PA78SXt2w7HsU$~PAjO@Za`6ul_C0Bp7V6=|~l%=zG`cK?})Jpq=pSqUS z^7*PDcz?~R74{83eJ#P1AxiRQdMLpJwm&E^t4O`0p{!Wc#j2H5*kuBLFIA}RJA09X zS-HIbgdG57t;kzrE8?H;Cxy*`j4G>}@1X$0a-pvffc9qq96Ya}47uFFz#@URl*cu- zmg1KOnpxJR_t8)~ueFxs8S!Nm53FNa6Ww70W`CMx6=#uhEM4 z1aW%R1G9tl3I=FDik~MH>#X>+YX$(ASER3!fSLwBGBCCCmfdB2cz?A@`{*w(iZ;=g zXCK%ak~>a-uRYx{n>U@eDa&ljF(v+Kgdr=uFyu1QVQGiDj z)RBec zf3pw5`Y(UQfA&FcWE!MWii3bGt(#HmWPqJ1l+FDM#LK~i%7O#*D{05G>*}r!OogPx zMpx;c@m|57sD6V?tznGXO}!RYoqyT_Pcx(IlX=awo6T~(aL~QTe!soU_5#81o0|`& zg`n7qFvP$YJ>?T4Q}SB&75xNjB}q>>iiwD2VLqrK{_7#dSacAdcYZD+inx)of${`B ziD9z-(;-Bjv3U$3$N4cJpTmfQP-{aQuoKX3nMj)B;8fCW?wO*kmSo^JO@GOFwirXy zC9z433PuwxJ+sXWRijlGtU%roW5CgKhrP(EN<(HxePtaU-qkMBVffNFM?QQXlx~JD zyW)=O^WJ1J73ZC69mJL~QI)w_qVPbo2|*7D)khPWe~Ge_RENVp9u)vRK*GPk-*S!; z<+0nC1?&_3eiLF}RXSkAuGg%zuD^eON2I|jKANDBCvG`I!_m_!w9rBwcgVy%{b6cr zw6X;yS$vQaA&1daPR3DxtW~3=?2t?Fu-51uVP9x_BDFAvRV}0KN_~{_iR@VWy6C!D zqT`wD=|T>8G_&HDZ*5<8iGLKEP2z7Zl*Z?@17I)Gd4OJN$|O}7U{s3m$ZCICJ=%^> zhI6G@TT1=5*GYb6o>G#XagAK+)!46X_W(e1Zrq2&fU7Il7acNDU#iDv%-+Wl-1qvJ z>FY`EigES;N$5#Du3gp@1yhVy#9-rIw1srVxZTyn@M@c!aV@^(2~;Vegh-LKTp<`P zmoYW{q?}|aIgJTv@U3%1!&rYP4V4q~k_v+jxiPNT`2`AAqU)MvEcBxho)IHtW--Xh zeh8UQtHCA%i30azCq){^?X{NJs<|1HqjB2G)M4fKP!bJm@F7~@rBkq}#*N~S4PR;I zbFW9`PVC#wz0Ob$7(BFZlu!g5=dV@)UPRAx>|gsb2`~rJmWv!i0%7jvKXfGQg7;CQ&77PRHpY|b40RM-A8pqOK!nOO zqfok&G2iOvxFH<|YO+Ya`VI4g2+o=v&sXVN>*f8F7p541&zJ-I5zD>~0= z6G#Xobamzp=_W*16sU+uif9HS$-fl_TvjtY#TlfvoE4|u#5#Wt*UNAVbMGvA0VRLI zHM_x2dxg>P@KOE}W!>w|3+99Fb4%ih=^HIS{}PZpbQXh+YX)`s;jKBmC&dT{*7j57 z9i$&5$2XXiRp6^vS71>tYtlkOX;91cQaV;~y_-`OZ>M&sot7}tiuh0Sh-ZQbR z8#nSyUY{=t>qmcYc>LZb`TV0lc%l$N?mq>wrtjY=Wk7*|^dbKp^W%T#YDxa}O4-rG zM9tCS|BVb~t3i9A90PpG&`jMJwG-kH25iD1#z~FABDH~=AR!yh8}fP@7wpEvq&pwaQymXcNGUU{_Q%Z7iBs|IB~+?F}xL-m)>ROEnPmlgXKy z_TGA*cAfIHrpJE2%yY*R>S5l&O?%;^!Ak0&PloF0b_9&9eDvP9e^J! zg}GxQZt{Pjh~)xOPlJ}iqVPQKx&k<}P&Vnt&{gVGKiv`eQ03^tk5d6%0l8t5awc6D z{TBYGW>728{c3kk1h3Fwwfj*4dIv5TcJ6mnV43!c_@B*DyX0$>+MY@yT8uN2@_{ME zqi*S%g!yE|Nq7)=8+cb1Hqb8Ei10WcB-Btii!pyzKZsD-QP>hZyF>;CZer}^=(i9# z8>p6+x94dMRLt>NQNl(H6_ntGkr}Aw#wUB)7>tRd4>@O(NE0^tcraYbiwu4WiV}{~ z*tH@yHyKmbNI^)i@94@qgqFjft2UUB$)&#HD-J|pRV32ebsAudXCp;iV!=t0uLC0G&j;cR&i9w(dvR~)-(@zXt@)YYEgghD zUD2cx#9$NFxTix5;@PB_K)yl%^)$~zE<8eJ4$5RrD!*YHe`W5{kZ)DO%57k+xG-vR zXB{}`eZk5>+no%*$DRiSAA^Z>Ug-#=-e7+l&c#@nMx-S9@>r@@-?zC64pf8vB2946 z!D?7G-lAN_elr29UY{T@cHUykgC~nl6S?4qDYhz}VqHHbe0DT?Xdh}I>TdV-EIa)< zWmPOaGV^S0bcrW5DF`ViU^RW)q zYD6=kZ5^z9CD>u}~F5m7in&U1r*Vp5RI9K}?;V0c%4s74NhD%jEPFMrV57Gjmf@H#=>w=SP2e-l-<_s;oU=;@}}R%)SG#_pLnOkuF<{rJCBu zT1tuFsuvsn5U+gAZo}qp4cbVTk{$+s?G7qtN(n|_FFMF;^ZvzG_A0Nho{ojQxP{iZ zDG3KsK@PJUgTouYOZn3Q4QmJXqamu2@;i(V(vMH^o?(IUyXt_ht>mD#P4s`FWPS%T zwj^kk!&PlF32x-zOO$vY+R7l;RKz=kg}h;K4tdks^A9Pt4BqYD4+4C(s1wH z?ReH+6r}4hc@2za5TEsh#5P(uY!P;g*8B*m7ZZ=`^iNhkF01rS88)YcnQ2X?Aq_qk z(Fj*9r7%xKjuct?t?fn3o#lUiZi+RWV00;4fp2 z{kH5v5>3+aZd`w&k9fw@8i@@YzMKUll>vhIrsf0a^EO=q1Sr;NwrQ|K;>PO~r`%U7 z9GWsYC{kH4!lrM?f-+RX2 zYnAzFVLHWW1aFZv9{AaV*u98atz0A+U9Q%9Tt{#{BB>7~Dcf0c%>}3)wFuEzNSv?Q zLEE2ZVoka0gC4aBikKqb4k#}r3XX`y+UahH4;&xXYb)9V zm#kw=y8hGZ5va}%A}-AGt7`74Yek(p*Q4%&S74uZg-?v!NV|Dzv4fjjaKWk*luHa# zXkJF@^dZ3()ERqpZ`83Uo1|!WaGWIzEPUG&8PIJfp4J-Ua#4TajINv7wU$A)qploA z*zHjfgxUhf>rrx#*6lnT5mws6A_d2t@842Bz~X!^G~Wo{BMi#Xg7>lco+y!D7S!zl z8Qn65sajHB3nJ9Kfn1Ebu?17R;T}Lu%}IK$KTQIXm{a~rXl*_5 z?tSdz!t%q(fT4f3l(CHQTUO5vQNLT_5qgE^2f4vjYr4-PZ?Imwo(UGj56vfr8xi2{ z%XD|nDVA|kJAxAxngpuaLQTf>}nh_n)_;qH>^c0U* zqgloCqESS5d$to~S=ea^{^gjTt8Jl@8;eUTLM-D7H=#Jb@i=qzZatI{s z(Jh-esW7x6A^r{;&}mL@;A zEX5abAAJS4RpehUH#z~xupk|iiPg$6+K>#UPA&JG98{bZwX^d|iH#@_ zglll$wzPjm@;b-p+Q61~+yRfH3aeTUQkYhxruRZiGSOvRD+>p}ZL5se{$dul46ZSw z^_X&bKT6dZq$UmB!rSaY+W9GtW0<Ws9-T*HcyO{J%Y|xe#B3{N#XlqD%lwHn*K~u7 zmZpE($q=n~#J*%2EK46W-$n<1Cho8q2obA?wXmiRx|1RFf^OZ%2ETj54ywiostz*f zfH?P7xkd>S!nnZ+gL|o@-G9bTY~Mo0SYUFrlR4x)!9+jI_=P>;XqQ;;zjzAW?-?5Z zJ`lw$J7by}KVsp$RIlK#XG!9o`XjTCFlK*^(l=mxfUYk4Q~P3QS$bTh9zRqff*)@p z4<7N7tzml53r8xFPl#8?L6T#K8Q4`&YF!Q4EXpq{@mkT}TFzJ*ri~85%8?W9W3FT; zf@@*T`X&Ey?V#!UO5F9p7xuub3Nv@EU(SiTX55dce1_rLATrncuwxsJWzh{=OX7c_ z1v(D28bD)>j#hV^70rY7z`2w8mekSS0D8ImZ5pixIT7c$JZ{R^Xz{>FrT$V8CsL-2rU#HN(y1iqZifTYz( z!~b;%v+~Rl2+t&KErq4YJ$+JBRT#~xnD+E(!E3cB7CE8DG)-7 z57I!=kZ9wK>YsnXT4p8@6Eo{$iwkN4(~Pyy1B*DCU_f;9+wlc}>rPUgqo8l3%kFY6 z=FYv$++lG_pSv07qaU#Id)#@>a(KDEY;Mly{`r2u`sH>P7@%uxjIA~oU=8j`PcQ}v zM#ix2PazF`qYTP4HvTG1Eep*^b(0;=->*^ssoZbIPBwq$QC})TAE6=eAXtMB7XkJA zB0xLNK_P@7UyAKF$F^hX>Eye-0##=R(SEgbe~wg=b*J$~xZ-dNlFKLsB#x5k_`+62 zq14)jG%F8J2If()cz+9JjS@{8>+)!-HUl-3Rz`Z-nBD^P%~%059i9*5{S*I5f~$y> zvd*A%)LMVZxY1yXr7wis#zKfmTOd-6SZW!MU_!+<8%t%9{S)*-4>w8m$ zZSM%migQno+qeySJ|eHK6W9Yb}b zmhN9*kdg%Q)QnA0{RoKSYfGTnK`Kf$s%_rdR392DqP2jhhPbk^HsbxcN+Dm%g5MQJ zZOVVywiT3QL0QU1)P&#-di6slYTE$9W2;05je$9;&1wRKk(XX!^qlDnf3#LzKLvE2 zcw8A;9t>;EPbwH0lZhHmFrO)e_8Y5}0Yg0g>RuOi4LdP~g#wj{zsTkUNOSK%J4NBKUv z?WH*atBnFwyr;jFvS&~Q5PDaS&_;2z3z#Ip*p)*6{(~I(wM6`hwBiW_!^Oah=#2E? zgSf&z%zS$fBO;Q}I2qkjQoF?Hq(FY9RWHvmJJB&Svxa+Zr<%3+Y@#z2PeU~>Z}5Mt zOuBf6T;Si~8Y!pLV%bn)arD~y3q#mG=$3^@fDG>2-0~7 zkI)fnUq$8(Uk%L-V*qY}Hc&)V6gnQ8k0n-@$aTAnqK{_yYZMk=mMCB|nunQ~eP4O( zFo?^5c+BqJ&2`@ARWTE!oL6!=Ye&rH$|46M70*G;osruf&xTLn8xiMdqqu*wK6m~d z0SC!9)Gp8NkMuckit96qR1g4&}3u~=WyG{ob3CBCpR<@0_G6p>P4^z1l+xrgkLLB?&kxr zn+&B$IkOs+V0P?0h0yu1z$||sWS}oLFX3$c$>=`acjOq^p`SbA*%VD&PkbbwFq#-l zeND1G^tSOzecOO8y63=4{r5~_FGV{+E5hDxINX9p%}gAZ6y1yg?98H2H1FxTlRtaJ zj~ksNbW+>+P~%@*;}`5#CcJOhHyPfHal4n3bO)-4inv6CMo)MJKDd8x>Zft;V4npz zC%t7AE$9Z;U0BNInPwk(r^OfdHa&LXX0FekialVKCzJbDxP_$gSR||nW`J$67eVCf z38d?Z#|@9x5dExWlJD63-NZ!oLy{HMTfe_g_IPtNX=5-TAPKntW(}YDUn7iKe~ZF@ zm(b{_tG0Xo5dI5SfuVo?I9`wxgdk0$3`cO^u_ZGZW0Z0=L;OzeQ9ca3U&k}yZq%yJ zZ`M!Db~?G@*mbkr%MC6(r8iyy3l1o7}ek)ATx5r#*FS<|G&bT`lsC(uGt zERzXwwtbExXuMYA5Yp2S|K{bazT4|7cQ?kM{-`;`;q;$-31NRIj1d{rzm^;iw3&qz zl2z-xdM|aIezC%BwaTZnAppP^9j*fiDxdq0F?K6B&5WVVQdA>?knKw+>T;~YVF2Bn z&ASQT{@(8LSv0xqr!hDb`|Fzu2=?c?NWzlhA?I2{Ole$bw0uefsrmjUAvLl7^2*bq zV(&!PC@7otmm`10U*g+w{2=}ivp95w1N+a#Ro`M*++r(N-4l>*0Fj#Qo`4hnY`@u_ zG6^w}AIHsG0Sn9xik(GlE?O^NT;n%lz{L?2@P<^wOQH6Xph>g)onQnk|1|jBucg&?7v8>~3##sv(5F zpRr*Q*<@odN#24t)PAr8xZ`OEIWxZI2ep4#pVhzky3NE1egN^lb2xGL4>^2& z{5X{YU?3p2pZ^ZrF#m^z;s2-Js-R=F#E-(m_7TsbQ{7^D=~fMPsmQsZRxFDB3|E9@>f_lG+MY^4`jr^Uz=0D!;bdtq%nxNO)`vp`NEQYArhIZf^ z)VF^OPM`{LjV4)XnK`HK*C1y8Jgts&5-K^#_IqD^W7}AnzJZ?ELh*`3wfzysS`}FZ zsiS^n;W@I1nglNwPRWVf(da=iqKjFRTt}iZQ7|eAH&PAeT9e|X+R*)a-AVAW@e*?X zU4Sn}C+@jyhS37DUm#bGbW&0Sk=#_#($arytf8uG5|RMZTjID>5<@4%eau}mE#bQ0 zrKF|YyIgy1xbfE3S2_kM$2kv2e#}^!G{dho4=ZsF^JQg<1 z-Hn#^Irht_Uon?M?>RUz^}X)5UFlA{EbP|9=VseseP<5zN?dN#>OZcV-@~}MjX!{Z z*#E6Z!hbl~&G@g@=H=g$-G6k%8wh`Bp&*)<)RxRagb$GcC_jc#q19mn%zdcGS*wD! z+t$fjESoKNZ+#!E%s8dFeC11#wX#)9@i?=;0bfgh`em2DZ%FxnqT|xPTy>x3oZe>f zWcKFpeZuysdSmrrK9RFy^Fw+9yCaJcb7+Q`4o%EO8vVF=C$7~?I81;6wFiGD19TPd zB?T=0pcaV5Gdp^$BGv?oWH=Mk#zk6apk3a9!w)C$U?%1uiycfT0){XYMpE)G#;4ia zG)fCuB$UUjxkSFZYDMbkOQ0QG@@gOo?AWe^Eg37nXgJWUkZXkXprz-TiHk>+=aNxs zeJO2{7Cm)PQrp4|C#9iEUnqYZk1`R3U7WX3k`e)jWkS5}PFmSS(#+_RHn(D$Uh7Cf zSi{IENpo;#LE;(uQkXb%b<$boxDG&_<`kveuw`f1&i7x4x`7Bo){e52(IqZUA2;U> z4NT!F;J_(#;JHRmk%$&)vQ8W4rAc;4%v=g{g74IA#!wgm<&DK(hrq1*`%4hjTMRLA1UU*swFVTdBUj7sqXL z^YCi8+y-;Q^(5JnW)lEgnq+e%(4i;oZQch1W{2PI_BeQG_ci#ph9ekn8q(sKS;(xI zo@o}VP=2rHoO`s+x!`}Bnq0T}&c#h@Yiy-v7IE(e!ZGDEFj|M%ZeuhcL16=|>Sgl` zD+Ita0BCix!y-`?Z9&^mI|}!-w3TfEm6SV)N0>F0?s#k{Tgnd5co0=Ng&G3k{TiFG8&Uk6HydcN+-oYgbN*flr!%ly(i;p@!N=@c03i;(3hkeQQ*?h|n|7d6+>rGex5c#Wu{h?5HAwgx> zV-+DqmLxX-WkBf>acPp_I%FW~HeCRB=6l(qg(Eg~LZ|Db=hYbG}akn`e=U@bGP?Lt`&D-rj46SLr3qt zDZ>Tinyq{pp7@Aw5+1ED8{!*=d*z2rx^sgX>OSL8PZ z-caM!en1YfnMhS+zuTvb*7z$`R@AUc5-9o8Sj9Kw_!QlCAGx6 zWpXrUeO`ZiM&V!bmpmQs?B3^H_^vj9B->M*MA}^C3$IvFX3>|ix{cUnMEPxF92(2U z)h<6UEbIGrNRJUw;kgHp-mA+hrt&LR#1aMii-{ILT^-gDY z$Wp~~-=^Po20l0X7W*l$XqH57TJ_NyulJyv^?@#$QK9BTvKtj6a%;icRT{`7mkQt5 z%TRxkK>;Tr{FQgFx%mmS&rJ~^P!qX^$A0Rx6W=DzUKF?a%c!)n2j0K$scha~t%+Tm zzlv$%v2`t;s;ch2L-U5*-i-S(ExTGB)#v4gMs+`B-jcswws7OnYYUX_CQpY=PMm|M zJ(A~jOmkM#`d!cpQ9U&$t2WZYQy*M{vnGGm$F8?LjIoi)nGN~i+H_XJ;LyO*9XPmW zun6Vk z69zBVb!+;RwTfyxAF|nxcz5|zy;fx~PbvuM!antCi2>h#{!v8vh$4=A{0nOH!2EwZ zoFVkTrj-B1k?H?VqNH3qE{dXr?7Ae4DQ2w|#R(=V@(Rjh^TP%)lgOHfL88O_xMEDi z@U3f6QHvsEXn>K~lsX&k<0pfevj3s2TxzBkaS$7HzxGXV&Cz-4Gjo%pb2;gU#|P3D z(GbJzh&fVUVFb&j@>w9%-yxf$RnUJ-fa?KhRk5yUD-;^lVd%;`$cgxX8Rv$tdbp8_ zBv7%}ncTH^FEn65Px|zHV;!6}T;623%u#crZrr)kUUUVSbZa+KwQe;%sor&8gnTwx zvHB!&nZ4Sig3%nW>Et$lA9CkT)2w+7=shrHs>4xwRb2`4jn-te2{GCS3hjS%QR*o3 zYgkOBN;TONpK-m`kKS*a2e7^*sp=0_D$JZ0Tx-H-asejk9wUTu!VA=nXOwO>2V$}$ z?>e^;np$`zIWK16*?*ItE9ta{@DT3LXDru2T&?*b-DS^(>GXo2g6L70G|HCx_yO6o0eBQ2Bt}&{5sm`Oa^=u#O+@XS94^q-eP{ zp}e+e-Ksb!Cw&v)h6#&kxSrE%Gehx2z_JDCD3n$8>7xhh4j$c81?Ow(-(Izr4z7z{ zuAF84u9`3OWmI!1aN;x$qs*O7wsYPFp;6kc=&GyuA#y=dd#+Y3qg8*zcRYJT=!JIwZSs%mHSe(R0`%^G{0VcgFXes&RW}S!9p~K*gWA6*tlw zE{w!kN~oEAZ|hlkQ|FOF?v_ICrYkSv6F&VjnXD0rU?40Scbb0@t2T&2*xF(Dc{a4e z2KEpSZ9=U*UlJ7t&g?Hx6E3dVXZo(tc-gaTRCCUd5(f-5LUYL&DKU9}dU!EuL_r=< zhd&TQvFhwd4lIJ)p_HxU$OI+f@!Uc6Bp7A`^$bLTRtW@}kxPAHtBEbZIRk%IsC zI3lP}2E!03jDYsja`YYl=c({V8Fsw*FOYc%{a*x{|KiXo>o}o^pzvhJUSF}N!RFzY z5hJ)XYB2qRI2$n!3#Yb4G6q43P26^6GG1t8;ey_$d%b_~yW_Y+psrTbD-*0(Li^-O zDdjR+o6yWOZY}9NeeOQZ>hZbldOa=G1JV{=?x*JlKXo9OT#cQ&NWkEAwZ~8+u95UU z(1*z7BsbXTzkHQ89GS|M@+M%Qic5!cj+=sbIwiY-fSD3;A)(iXY|^x}tEzAx)`PX+ z&ZP?O*#)dgwl(i>@4ch?C~IewZN1>V>YiPOv=YSu1!#_R zu%Z^Y56YdeSyiNi#+T^n<$=xM{1Vv{XSuh!jP!qH;;CqiTuf*qrGKS|L)xf)M)6r? zA1=0?CSCnf;93`x#gyv+_g!mQz2h6@b3RB>yRFnz?>yYQX zaqrN2fGN+mT#B)>B%oi%Ij;(*IGn}IT5YDc1na0TOK2{mWjG}aMGU9YnKBYMph801`#yk~RkhS`=0x6|$l)%q(n()e%b8lxGp zQ^|8nPeW(j#MAp5r4Wa8JvfFQ4fIp2!F{NLe$EX_zZeG0Ot+|bol z4$kr|w6w~)8S9nyTLL?@o`9O4`?$aW9u@BSgN6gq~ z=C>XCu-N-G(jE#@A;00iK0!`NTjyUuzxePcmw&Atu)luAj#<-uN9B7BmyUf;^ksj+ zCc`kGl!^m$hX(GJUxT^**}KVY`eR}tcJ2nH!CNXJmZ1DgqdVFy?1gp}$*27?VG*MT zW{!O#G&qNn+KCnF*w_JUW?K#1nfgS^^3^MKI`3tDap}{SPqdwf0b`t6jul zCaWO*D!Vz0G=@ z_I`hS=J};GR&G8hkzBM$gWiWhk)1r*PCrw5HUI{OrV_+Jo=O04&{jn3uhXWbYAetW zB49R#TEsDh7(sqP&k(%^HZz}03y@PqJ!xYyn$jo=H@WIE*k;03>iL);eaY7_og)p} zn<+C%n#|JuExn^V7ullI)TVz+y(!2RJc(nV{+>kv^**(Tghzo+vXJa0_ZwajI}{5n zWbqz0$0U`P=8R1uhZO4uhLkZjN<;{4Yx!2zY>Y!6Duf8uov|!{uAO6*K}13I2&@4{ zVX2a{!F;-1)R)&PsS66AO;TE4%uUx`LNfs+ijk4aULrLNza+v%6`g<8->h#E8t6K_ z4P1LGRgMZ-GPKE``>dG9ae_}?W zrHIi|9%Lfi^mBIKl7^!@T-O&{T8)3$nDukPVZmbUade!s zS_gizEDAY;=)e-6H!+pV^!)G=5d`A0fR!Nmh|(dpQUP(+hrgCqjH)VnWCA^hFH)p} z=n}mH(-gNfoNQt>Gs=I0N;;Kale%&bz59B?YNAf`n3Jm4A~ev;Yp4yYvkPmuJ=5pw zO4?qo^OdBy9{qnIxhW=L6s`z z^OQ#sEUwWXy{c3pr(FN_CCv4qlE~YV!5gBB{ksF9P|0;mZa2IyDBx9Z9TW@^i6r7z ztJ{|tGEROBi8u3C6S%Qo;u4`vyKJwJ@AC#bkNCU8&(D9E%5mj)kO3<5YBnwxA8Jjd z93M=d9p8X3{NCSQf$_M)ytS^M|8QebyLkJs{wtUx{;R>v_&;a`T9}xFIEY|LnVJ(M zVff@>zyT=GQxF4A=Ceus?B~Q319%H+cRz6Ouy?cW!8rLUp`!C zZ5M7wb9;Y*&<6@4D4#0Pf8h?|g{6hOx+o1?4Hlm0dWlS7GO5+;*2 z@Jxi+cb~-+G9H<+56MWPcvbW@3*@RC2@%P8Ps(PkVi{yO_SuVa&n%hDBMIJ{i85)U zE!E;lZgG36vN5BnG1JjHE4gbUf|1ZKIlp1prBkBTSC-Q-~w77@m_HAWvfJZ030 z#l1d*HtsD!zlnM&YKhYMD|iMvWvx%HcT=j5f*sm#*S2lli@3j|CH>E_XI)U8q=- zcDC+b45bdz==et8oR!4Fa6P9qlg=`S1|ELQz!^T=*LIknqcnVp%LTzv5FDE41vv0wZW5s+)^@vWr#&6q*Swgh|SCze58Cp_mx~;YXJ@ zjh2?BWlx;~EP?$D!pKW*S9wzeq9D(PS*%;o_8}zYRowHNFMtcgnq;i7cRJMZ4_|L= zo*vn}RlgOnhWXjNKa9O@pZ`$jgScnFrui!xb^n`sn*VA>sqFrhSrNWlXu5wLaTO`P zLF4r#DUy|he?*ewldIz+4xJY2T#nbTYS#w%MJX5&@&>>!zv_E4-N!1L)q*p(Smcji zPfgEw?@nD=-@fGD@&QHZiws-9RL(gRVz4m~HfuRzs5n5i615Q>r$zWiRhst)1#~Kw zph#1;jUwbQ2cOF_fF1^3!sLHza7|)pE|%;~4p+n8RAFGDYHTdHj?}HdjwapqFdX$T zL&IzYAZ(>KM+y2bUfOdS*`0H2&S+@Q0<7~h2aaRtseJ}fG<>dQa^%dSx!3;>YwzHm ziQ27sR#Fuw6}_=-+qP}nHY$8$+qP}nuGmS%wtMfdzjJ1$ufF!2J~My+!|%D*z1Fh; z{ZT*>;LsXpf*zv=hN```HjgIpOOkg(0gh;hPnwW&<{jeMc8DCIn5>^xQc(S5?l7CklX+*lW@$f zmJ3dKI)TIc|E0$b3N zIo@-;Bpk^v zg8AZeL5XApioBPXPlA$DgRA(@)%{c25NyYx(fR!))B=B)C9$peCS-pRu#aD39HqIz z%E7#(Aac4ql4P7RKCORBoCdCh*ad)G-~G%Sc>G+@6&Nu6Ajr;U z!x`#`zq1G5aApoi1aKDYS$}guN1WuS%S3afTiSo8ckYvgq(tc@h8563Q0DZbNHt7D zJ&#mp?5C!Bq(*W5MuU#H>qx1qx=TK5m~6t{g~5}1unY}ePc-b(5GCRcOPN z#Xxw{X(J+P<|H~(nC%2tq9@nZ%(Pyn57p$@Y@%L`K&6hG^PBE8F4DG$Xr9XDRCKbj zr~`j2P^Z8rYg1X1Wk!J_I^egZ#jLdv%S+}}I_(1ErY+can}|h-R%cbX`qKPI z2C!~julPgih>THH2S1?&aGA5t^Iq|#Q5 z^ZV?t=kKqJi84tO)%?|lr-Jl!gi}%qZhU_xtsJeg?+}UM%}MZTdxz++g#ld=I!8L; z^>GD-A!P&Q>v&EHsi~}rv&9RV89M&7e{MyWUHs8sr-z@ipqh?tX1xnu}vJ^>@t7J zi1Ue)p30ASK?~i9BxnHoE8S@tsLrA>EY*c916{`sZ{AA0(B5*=c%rR;i;O$g@o5BY z^n%VqEY>cj@L+_eU{46pF3 z%iYxmyxjE$AfDw1#GciM$S`?_SPVuEnY?L@^~~7=QfoVgGv1xqtnO&x&gumEqDvZ- zYKzAm$kjP}h*l?H;LU#`E<_I^9VRQ&-}8~>8EKEsb^#{-Y+MgwjaVfyMPh&WYObwS zG@Y077=^Yqe@bvVvpX~5*4!hO?o|KYL&u>sX0%>`BC^rs8Jeh)J$WJEFz}$0by_0h zC=IYdxx_DO0>El;-g-{70;u4ts%#>jahjx468%5NI!v{>H9tM9XW)e7yG3ci9!u1g|Y?9_Ha#58j0oGq5`_<{&=Le#D3R z0srMeuP0s)wL1nJY!mRNb!FhOc(l0>|Egt|TegaJ9}@WaroP+9Q`vc{WO?*jsIuU~fXnreT`_uJ4f7$;9Q5(;lf7h8x7yo@1;9f>_`|2;`sA9Dbyl1RQx zORCF4EjfRbm0@H2#{6 z+`<#OjsnkEpZs0n12ph+rKQJ8zDL{EJ=D>9OuX1LMZV9$(LaBt=dNRrc2&l-v;!sV z+@5h&uWp*aeb{D^NPVLedVaL8{(_KE>k>XUvt%W+OA>%Vq{WxG1pFEu8O!Qp%rqIv z-p9x>t2cv6mxUxHX5WnLOw$z8$iTv4^^9@12ZAv`OKn`=(u zx@VUieWK6a<>I%9U>@H7J20HNV|})Oef!q%kG~lIxRpr7*2%!sM8MkG&d9*o&XM@v zw=1Lmy*z8ZmB9f{VgW%iPaYraNKnQfO&kEFB2o_6A;^DI4B}s7*IFLZQF|oq)$<>e z5x)lU1{j_v&XZOpLe!X<&17PbW=DD z5AoHAI?@*`{`^fM7{R6JxT*21g$z!HB!an`fo&YjWANpL^3LYnHPDNx`z zv_v%W^r$xJTezUyZ%#bAJbO)Q zKod;}uVGZ-7cr>IJ&x{08`mV>Ec!KZ5cYqdLoi$s))Z+qmKJ?BO2Sd<2!-_t`ZtpB zS78+_px?gf{P?G=f$g7=l(w+7GBN(Udq>8=o>;`z+0jGE#Q8sac$6*d*7y;8q+o{^ z3z;D#X6|z-#3F#rU;23!KJ|LlKMPINV|x|o}k?LJDE7TmbPg#2 zmadvNd*1n!&msInNz@6I-^!gJ>O)iHp?!M zJWV3)r42CbI!Zdp5}G+t4iSTG!K+7li7u_q^SG5tKDTp?)ytB(u07vpaY=t)>%}!3 zPq=>y&p7yKsgH28JG^mb=T#Cx>4f;x;8-X-Q+A?IN!nXnO?kg%2!DR&mPTLZfcqD4`CV znJ_a6c#>ekU4Qbm%Kbu5^5uW0PKXN@_zlWqK^<@!QpsxQ{5@i592Y4#`IRJ^BHA)gsQ(u-d#L8W|Znz z)kYy1P%DimzKaL?z25r0+?pl6k`bvp!fUrxU^Tu16L$wwLqyPWln8(2^k)sVq`H)O z;sW<2IfL~=Yv9AbRJ0D_p;q=UWRL!Fw;S0%AuHl;WbI=7pA*RHaNa5=7+*PWbq$+d z`bM=;aNwgO=x_Rjq3tw=(F6kwiEy#IVksB&)f<~!S6xEAid0gJJyc0aiRp&HVNlY< z{*ASbjg7Z3(iBydlkb1zEWEFLN10xi7-m-QLEAnvURzHdIWu2(+*hkgBew9X-nNF51*H1rj9)Wp zaNv8%jbA(MKEt5MfzhMLO-#@Qt_10gT}OtS1(x>Kd~XGPGk(NEnHih7bG&b1e9MCE zN!bO%z)grkGjXQ~WpL*RHSDCWc?!j-DP=F+iw##pF^`%n#jS}6LW1JS!|%As{4*ql zNvrX@wy?;-!6|9n?j4;Jv*_rV5&rrD>$SoYSaoI#hQl zEmWkUf36TW21uv^ceYN%gX7r=yg$2lC&Z4R96b$p2~rDrk(`c#zD4rn#tsi+JNfBR&U=KUj*742kzNQ!jN&r-gs3f>C)O9*8S1wkMQ`)1cX% zHYZ<2KI&VLTd8)TusKc_ciNO^3gL$4x)%^JT?7{F9$?(s@`{{S2gcEbSVj(;oT;T+ zGDwh0v(U*}^;PYqEc`Ux@8meq2}^?uicMtRRev6`Meo}HkK-DF|G{1*6iIV>*JYkd1oH|X(b<|FcFOdRxW;~ur^gy ziQa!kb1KBG)pM{=e&*`?*jP0gIo*^?jpdq7lesog+_r|{?N;&^ z6%&kxB0J5m09Zh$zdHt2tIHWWbZX4Uep<5a44DTZpC}H517uUzp@AXD)-Jn0J9Jsw z=XURXrP$Z1JM#93H84%y7<6J0PW=@prs5ZQ9PP<16FLmy^V}mS@(+v`QL{t~Q*1Jn zI+N3Xp<(-|Y_`dpnbbTHCtcl;^p=~&D#0<$!yX3L(~#nK1{6_T+wEuVd6N-(g?<~s z-8nP|5VE-|bx90xsORP(!l+>Xz7~6z5JE<^V3z|PuJr%lN}HC%E*?8|*9e=i6ynB> zh-kKhtbt+9j0ir@IMwvhKR?D4 zG_bAmE;O+IeDrPF0Ze^>>v<;E)8LD_@o)r3i6-CzX{H+5=#oM=(2P48x9E>#z(7<7mBcH9fw)!Ni zI({!mgaI6l2BYgz$BTfpoWdTP?`TiJsthd4blh@yQ<0>aIbhUOUt>m)GMd3UJw`t; zUjH`j#=`Z-sw7)G-tvj)?a;G)H)NxKsNW~p((3KN4GU9|UcA6?Ge01jih}O=eHMLK zF&C(X)kXGbZ(_*E2+43jtjdidwo(NN-K zZB+2PuY;_Cu&uh4V3?^7Pu@-hX3Y5yx}Lab)U7P)4zi9eU&8`47aRzh)IYUHv401*4!G|QAcgR6HwBR(xYAsKc~;0c*C7oA){%uPw>r1c_hbQ za}%Qi+{oTdh-xj!78$o?jgxtQ0xGj0Q)707z++USMMh)!0KFlrkbAJcTRCB%%0}3h zwTWEmV<;1_Sw4!}G+`Cwa|33yB$jWxt6Pl_18Rv+1YS7~qD2=2;zWwTJE#FHr#O*c zkVoFx(|cbi=1j*;HtKNEf@48lOZ`< zu(LUY1_5=&8r~+Zb%;=WE*(Kuy56`s0;79h_}ePHX3$?ED9*W>TiR`I%`;rh zn^F1W++X*r7${V({8JOH=|7gAld49xOA%N)f4Z?M3=)>lNu!_^WX@o~CY36SB9qR} zRg~t-Pd6rINDQsR9TsDMjeEd~0s5SaBf$AcxTln1btI=*t4; zL`VQSqPt72iO-?vmeY(VwOQMt^lm6d&B?1r45ojB?M{4JSx=tDSL@&HaAyZO1OOrUx%xh@%t7!FsFR z5Cv+9hYn?(`S8Ji>rs`+>%$nJ?PV_}&i0@s_+yzmBa!WyrCa7JO})zf&1c?&?t%=a zDFm`6A3w|j$hr>Z{BSjoiAt2l%m|)LjbPho3a8F4^vB2G9>=1%DSXNOJOLt<)$*NA zNkpTpcms-uCk=pC4s8ojZI2{D;ktpyT+MD65j9)jc1x6hov&*7I-8NUhR0>94tfWk z(YRAYl!OD`@B=i;$wg$K{VeeL<%~kVq2GdMdYl09Y4#9SY_VvWe}y*&|^| zBrNHJLH*c(cbZ}6wKYF|OA<@GyXBu%Bo3_(2&JOfB-OW8W*LE|p4lEO6X+TRWMkE8 zeLl)^CTJ#qXrB&+6aPNjvzx#z8`oCa;s`%3e7Bn2PEVA5+2mE4pe-HhDz^rKjR{eE zypN`&*p3|aHDJZ{H>r77?U)dIBG*_?GB>xID)^Z_#Lj?C_8=a2IIjzPHY4sCovB)G zDGtzm*mA-bba>8jtW0-7?x*$MyjM`$d*J;CzCpq10+~1UXnXV z(u&mB7ly<*&0O7~rV)BzL3kM@tIX?YDPrefMjAhrH@Nkw&RkY@_@%R$^AtWD6_uPA zISOtFlgv3^^q6~qBu?*%dwka^g==ETs!X;+Vz%e{hNpKDyPEaFWKvXS6|-bQ3o+S* z;Ms$J_IV#%GML(|XPGrvzGSzt)r-1Vb8!d| z_N#F(R{(`Eg)`AFwpD1H1Eut#4UlhC$M;asaL&;wX~D9F$td;X;70@@e@rtxK{(vw zF8+{Y2B$!c2X@wB*Prb>KkE4>D37-_^Elamc>U3QJgD_76fhPws5VJ!O9@yYe+~@D zDHvtGZm`%JDU{XnZh*_Ls2pY@c~T}lkJJT7QOll*;Od#xtKKqL)%tZ#R}2~m9=bqK z7q5UzJMiW=_`|l{x1lVSq2N;q>2G?|RVgdVJ8IV64i2 zJytcJ$C7Y`)SDd{>z*`k0^X8^tfOgIM@U#pdRBHn25-4mESFNy*;E+;S~Cb4s|nN2 z&`+E}Jr|>X-dgUw-Tv#5AKn|&0ml+hI3jrg)4EdXk;&pim^!|GyjxKX2k>%=BDUM0 zJ#HCvG_>2_4m%2cW5%9%{U1PGdwg<$!;Rt5q6g- zcHK5Oa<7RBc_kS}$luv-Uy^tlueq@Mo&JpF=u{r7Vz3c&ir5ODz$Bk6Enq*{_Y7UF zi>|z2^uJBJo!j|I0HyI;A+k|?NhqJBrRb?<=-zAQ_H~EW zuiiBGhPaHWDg^H4Z?b@!&}LUA*tSrWbb_Ns7<5s~t1P+bL#}*V7eCa}Q69zn!av|0 zsyvXVye+2+_|$R;DfZr096#F{uw>pVjPf&*5tw3lc84Nh3ySt7N>nj_oVivA+x^qY zp6~u#lEUke3f?vKy1c89-5+$LEbimP(6aw>btokJ;Al%%SgWm2k;ry+%Z$Blgv&Sp zUtdRz!B9_PF$H1s$Ce zR#~qRtJnUsl|iu~9%5Mj1h-M9OXm+>hIXsB|EtgZ02a+E*`Hy#4j9uDxv@FBLItNX zvV%jDKyw+YUy#HZohXIJu=Ltn5L915&D}AJxc9gBxbxe9-df6k*|F+49fDdl<@RXA z@JQmJR=)_pTVjl^?~!{*hr~k=`H4%xw{FKjGV)kxJ`5esH$h-=tZ?#u=f8J~!fzq; zYKbVyj;|Agap>pAZlTjsL4j!#&;>i(x{T|nAXPUJQ_oS#JxHAP{}oEjHw!tEob?SG z&QyJD`gyg<>|K9BF+g(jANdEL+R0!}c6;G2z^Q zc9lZ(|?zzqLgjy=H&stw2LV1vgksYcQ%wt;nZp{1VNAJ8N!#PbcaT0<|>NJ*&5mUPL-=6c#?k zn{vm(Pg}{6J0Z14pk=Cis0y+LI;p8adTOA}9-<}e>fANwZ)xsfWN@ojUZ%uiFed8E zVD1YznIvS$bjA4={34A{YM>Chx^>DB2qQ>^VOJ37jyMQ z2NV;3k8iNaE0WTE6luN2l7pYCWqW&`+0PUK(6}bqBjO8;3P)H|m6*8NZh{vQa8rvc z0(|i{56!8SY5WlG3s^OId^3>+l1xI~O79R;9K;1gq%}Eh&y32(9>YIOT~fo|qLCE> z1wHUX8tAWC_LOIoJh02M`(+s<_05aEb)enq5JvQsec#_; z{)QC=5(QuGFIX#n{1dE{{{*X~fvbV4iKCN+o$bFdRasYDR|V+Qn@m804D%xewvqUw zcMOpbppAbZAt52h$H&9SC=n5B{pn5s+~==KE! zoKY%ML+`^eY$(?SY4y8m!LEXf(o`HH&|b6_=!^8}K+OeoU$nN|o4ux=BvX$hPpbZ8B6E})%9>ao@+ck8D^XCyg?{0?I3aLyWw_H5)9?4 z89YCGpeNGPvK=Er*I;Vw87}F!m~((>cl&5FK;_AgqC%Iqk2#q<%C5lL%1mp2u+A7u zwvOR|^}Tzib{*TIsEr|$KXR>nFFrx)T&096H=?pteG-PUJ~x8eg_>yYr1;e_%dC9LKqp|)KdOdYS}`4Ml^T(3F4Z_}qYfYuKwhwK zii74|s11L^^qec%%|xF+HUc?+8<5K}f5x_g#x-{*x9<9yBZDgW@^1l3p)XxSI>5!$ zRqw#S$z2x#OP(bIqv7C6>@^2421zo%_%bCf(5+02)|oq>q*0!qWauQb$JQ+h4=&qf z=bXFHtQ~mDmXu^ll_#}+w8_)uR!?ij*Z{)QkXvwWTu#qOGubXOTZV#v>xw!W8b`;9 z2%yU@*ln}P0GXl6mGc2f4c;vtdW@cR=4;Ac0dKz)v`hL(>a$Rz9irfKibLaL!P4#A-!p$rCfQ<-Ci zH0@Y|e@&)I9PED-UF(K{aKIfK9{iMRywDf%7Aj48kP=$FJ2KE2HBfH&Sdsaw z1OBGl1{B(>*%EzmP6Qv-R6n8b6LNS-Usy&a(`vr{ug7qh4Si$6mhfxv;)O_F=IN(f zVs~!l5^h&{hkY#|Crf=-Cp^MnM15knUz91)?y!zv(l?&Phy$=!p2^-v>dVXucC&Y+ zqAz5K-lD79Oy>Q6V$Z2_ly3~@cVV+k1G?h(?53_oKn?6ZH+c`ldAwsTeWmCz7KBTA zU8uhtm0m&9`GkBT`eW78F-QMARW{M*(6_1kNLFun5nr%2(TJqf5MFmk5F61w5WQ?m z_u}$>`BON<4m{y1yfU>xT~frntqQTet|iJOpT>`%-<$A%&=&{pQ*s=SY=7$$V~=2f z_+Q#I{Ev-d4FB~nA6Yx&e@{+zsjWL9n*hFi#z-@Yv7o!;OY=m`jnW%`APQTQ@q^KH zX)g!RFG#Kotd_kLf-}Ifpz_SZYlrly0LPkGLj0VD=Mx@*b;9={P*XkfsA_K7Sl!bN&S)IA50rYcW&BOn(OzBjEMMXYJ`*if3 zg2pN?Cr2=LU870qsjK$KThHFxM(sCKWnN?1f-uu}L)lIQxQ@h`vjSPlAhI&@e*=%r}SL0XJndoNp zB^n9bd(+SQ1q2X#fsd&|#wo6(Q}%>0q5O|h|BWUzXVF8&rDHzyEF+M;pD=or~cjm>!H@0;p@ zaCWnW<$&E)!_`U45a*+xss*dV+0c9F%Jy{4{1q(_84_o6-Q8%u+NSMrVIxWF3II);9E7 zIA(041-(Q%f!XXFM4sc1M5!ivF;0qy?&x*$YaiR^8J$W<_Y0QDDk+Mv#mw; zvLby2dX?H(FA-xz$W?DcC?ys9j`o2@ z(Qdr_Tc|7W8Mpo!B>Wk%V@Qr&@`&N4L9HZ!BGPa&+jqEvJy7gCoC;@uy4B^f;EHpU zZH|m+@%PrBZ8FeZeg`Ua|LNJ`8!W;&G}&VpyKi#;{iSq%(# z>!n^J6?;znCS0VDAsk(lv@HSNz6fDEeKh%Du*El2!jT&NsPDy9J{QoxIq26^XLWlV zdQ?D;b-;3$!SL-7LN;W7ypo8sZQKkgJuq+)b_c5|VV!(VB?gZ1B?g-%K?_V#v;&i~gDHZ40O0l+7?YV)j( z6yllAdN_2ENySannt3=WtGP73j5qd*1D9!9kr%_9UJ;#5ei&ab-qQVKJ$bHAsVpPc z(QDRK=IV>b>ji{=-$*SCeVwIYm7mK76@H*32*(E7O|NhgK?_k*q$|ew(j&xY+&ZpW zb=T1iz4h;(=#f0zh_F&NWrG>+v&aG|Pu%s1YrZ*N5Ztkva#5nrL4=lkI53mckm8CVN*i7@bu49=3dv)~k^q~T$c7zv zE39w_Km;+A*rYQItlvQ_B#hT-?eWBPnP*;a>(9BF0Q|wINgz1@b<&?mW2#6cG$|5{_l-(_2WEvQ+-F>jSEnPR*}cz#o~EGN z*}W~kyIRI=(5lvmYfS)~H?|og?ermx8*C?xUG&y$;myISgU%?fum@TnG>eqcxz7Hu;4Pvl4?3rUInTfyXljT(mIjoAq2O9te7U+s_b4GTz_t#@)>{#p@u8SI;N2 zxZI{WoUSI{Uq3#`{7krI^3fI~xO;`Z$G{%Mj~Wg4S|dSCVx+Rw>h~Ip19Y~NtYGW3 zeM1Pkag~1z@Q*zVxpuWv2}WFb9!R8sW_WRbaXT-yonF%ErL)G<}(E*eDMruK8VAknXOq%aWe0OBsvF1<*#i?a;;= zrLu&!*^>9Z4x*6>HU@*}r#o0T)Km{O$`ypAp#|1isSYN^7(6>lz;@k6W*VIG0Vqp< zVOEuyq9W7PBq)*}U&L&G7oDYHHHP(L3N(?GN7b5Za5mOZxB1i#S;^FI)t^0U{}!s} zL#sN1bB}q8elc~+!H2fONy+Q;Frk??CYcc6X&fr9U^e-3;6_bkrQDL*ehe6W$f6;5 z3Xp_mi)E}$?MF&JNj$|VRdS8)oBhRqU}|e2pNM;S}nt2GLLvm^HsaEyMW_tR*!f7D+0)-XF9X(LM*1r4FVh0JTh zkJSRG>wjG-?~Z#m4)}}p^M9;=H*x+qS^qcbe-AkE13sH+pavmP0Ctcn<$?mBq4)ui z0V=hlu=M0HQe%te>50rL&m!IZrSwEhKjnCzGq6V*GNh>kPz->Z37b6Ihv!AxzkFYy z^%3ZpnGK|ckoMsKJb<~Na4||!mPG@}zPr;6i${$yqxJwiWG-{AZteP2dZ(SL@lJVeMHuqo&?z>>mxtXE;4 z;H69c`DV!jE9pdVtuIqF(wex&lTgDHi91E}#;n5phg`RirQhls!GXw{xcsCeI;UK$ zoV6huM*J^J1NM%CyZ3buUFa!uIo3PVu@>!;O2g>9%PzC9MF^IE;BG)fXWhOtbb~|7 z-j|SND1XpZs}4c`-AD%ROH9D(1_e5dmEB~gT-uY|WiF?}@h_D(>g-eDA_!t<>jpjM z?}b;5^so>69$cNQH?;nqPd~_R?M;R@7m_!^Q`j1k*&%Iy5u78BankPAn~OSUj)37T z3$S9s=g_u?h=nmV3l$2fq0vqcCZG@@>Ri1J446dARD z+PJr1rx{w<_r<9v-+?wC@`|nw`6u}=5U6Gxi&C zx@nRIeRFk0HVY;B6m*#5b6Ga;<-D*h#}E~@IngvjI}OArKd07Ys+m+Om#V!S$eh<9 z0#(JZ$0!_sV1=STYuhD(la}j<-Gok9B(Cx-B!kt%c?lSMup*jXp76Qndy}$o)EAJb z<|7akNa{?9Xz>;I zeeo23DjRSko?~d>O)=R}!>6xU5bM>mF$MGYkT)A#Ps?A{%zHR=hNC=5ia}?*F@yr^ zUNU4WEgSguJ_c1K>-4wwE3=ho6D^{d-Qps|>0OV=SqLFkz?d zdK0)oWRv0Lxy?=XPZotdVa;qRj}3UJ6Pr!n*^R@RwnL{A%^9waAVsV97Rm2xd5T%a z$u?jgvxphl<>sWTttv^Vo5>^dH-V!Gm&u`S0k%I2M$=3G)JCF|%aPS+_T*t%XWe;! zlDBffS{-$(z#{)T9j50iyp*{d6mTD<=Nb$%xRj2P;8Yu=d&V*`NTpPhb&=--C_eet z2b=LFN@9^1A1g3v=b~lj=L{BaifC#uGo_1_i|OawJ46phveFtv4sY~=cu3Dq(uaY1 zl_Qp{CLiv$iB$~9#SG?`Gw zO2lEAdRmhX88xbB*Mu!!^TH}pm&SC7QqMeHzO%993GB0fl=nvhhvohz(^)Rj6Dptj>uE9ZQsFfsA#`kcJC=D*I+ZKzxk7)qh=^ZEC=d}1=(yV6jCq; z(yEDZCyL@yR6VPU!cJ3FiKO~}K_a!JZ{{Ao3hYqrzAEJElo=sfR&N{&w^bo8eXgJ> zSj}ER7-+9mGa2*XM*Q$II00n<9^S1|cn|9Pta-SV(ub|pljWyAy*}^+q&^7~lk1Mj z0a{ooe<4U6vXXmr-L~*k&e0o<%5^YRP6wnqk2e?AcyL2P8BHIDAr+&;?>m64^6PA4lA>u(~M}-G+7+VaZ zDcD%Eml`g(b34cnx&YS>hph^RukU+KO;$RkWr4wWf%>&Rh#C3l^M~UgQBB_ZzE4@d z@`6!36<8-F7m9!TbIP@Ukn*$uveKeXF;-FrdF_nrGe%Bk-8_!{E7YYC7)Oo5&cWKb z!gA)c3o=aFc?5k$Vg3Hz$4kYizWCG+I>{?=Juo{t>|c ziZuRhb^f=~Xk)iPkKnsu3P-gGvw7?Xa;EENnke!;48F0PA6ZBoN1VBQrywJpTFcT+ z0{R@jPwly%IQALjoqUjEQ=XvIX!Q2Q`^f9^vhL$!B*pIAr6CbK@*#I}ouT2w1Kh<(*V{?1=gLpAWB)HLA?$#XvX}+WVF1KxD+n#G@z9Gl$ z5whs_vt2UKVnoqJQKP#EJddi`XLC;Qb&u6q<6aY2icLO_t_uh8s@xVNVKhUXZilse zQBSe6+Y@JhT0iH;!~8F0%rNs+fo=G8J6y|Hd}VVvOv!Dmj_nj`@S+?Y#wqugGHTfv zq*kaCv88$g44-^oq2<2FRk2JCj+K})3-Ge@B#aj>xh+_$B7cWH5Y@_sb?`H$pwmdl zQNzOPV>UE0YV*g*p6ozsVDgu}Vx$>9@)Y;o=`JnI!^-lBBm#lj6{T$gwBl++|;ztx(p>+A1_Up#XN%j|S_Xk=p zH#3G91Ktn`Mv8+-zaZ>DE9Gu_&>16PE5UJNP-Kt@hGT|q_%5j}6gAes1#5DIL0i-z z34nORKwcGlyX_L|lI4Jl^2CkI%x-hL3CH?r{Ywy=%~ykK&9&vbe z-$lqq!;eh^;719n3(>xj*-{WB$?5%Bo7{0wcQ zxFPFng8r=gkP~+Q!f|z@ti0`NJobctcj{>P4yndXFp;I@1jDosY0zGcoWel9kGNP| zVTU4|lthzYH#^{soG}vjK)KH$GnWv%7(BrVC!{aWo;;tNf;~(rgoQU>U80Uh>0_+dReH?bgUfkCOF#NFwVI9Iz z$j9t$FfU~6oB|zE$OA-C5Q+YO#AbwIhirWS+9T|Hj6D7u``jJ8U`+qXawtIPBo5Wn zZT5}Lbelj?LcAli8NmypEuOs|rYN`@XHe^SyN@3xkW0Lg{vLA#18VNHU`fs@_V;W? zuNy#&JY61PlZV(mC*LrW-H)1b6l^srev`Z^Ew>i>&`XFanTJ zKM5=hp=BZ+5Q&Ln1uZClfT)&2;W(vlsGGuYvQASZPv@&8_siQopWr))t|l9z+M@7#NTyo!WCc)o5GU8j7S{Ehaou==wd*Do}Gylr0#e`!A!(|p5*JOAUm)`kBRv# z3?(YmhkQR~Hl74s&1uMcvU*qDsa4nM#U<^z=EZwofgIgs=<3T!3yo#hN3tKFbT(u) zZJ>FB#mxm})!j$s4pU1afCVG$hN{{9uEi9~Kv7ch_xy01vBOmAdwD>x3RT22l4p{fYcq<4g5g z5>@gK3e~#+5^u8E&NFoDr?Iw4HI30Uc9Be?4davzm{#R~q_N-8rpcYonS*h;66(n4 z9fJ4LQ{!b)RvDhj3%tG_j}PJJy$hmCF!P&ydlavaKrq}dc$4ubt6ZVFkLV$dXfVa^;!}Dz^i2uMjw1aJ>U2ZV%@Gk4%B#prUJLtpOEfINhUgSt&ig!jVDhzzJ6rJQ!2 zb=6{maYj!t9C8AakdMQOW$`?VTyeFFXv{%ToP8pHNG*(aI#tEjwK(NsuSFPtv_2EY zA-KOW+S8yOy4M+}D<^dFI3AlC=gtvv@>+Q=c$3@5)xw(v8!Q0-v0E3B6Ac3-62J?C%TV}qC z!=2ldf)!MVaf34U@*=~h4!kGQ^yQCr6}aJiWddzXm@uaiO5+vQBK7urTpkm0Ha3K4 z{p2!mbr)YACDiH`vlhT1H;Pn`Tv&j*HHasF-WvH<%uT9L?}{hT9)lDJvIa*e%JuoX zw03{91D(*hY46uaxiBg41ek?K@VdCB^17X1;mXF0{)Qay9<2HYhpM0<5~~6`5t?O{ z2~iBqlm~M+Ty@GAvcP>0j6(QlWE?YA56pIF(9N$x{XH?36O=o^`6cNG@uu>G&#u$F2{e>GbT**zdPc5F?tgL8{~>lZYFH`=zP3p9#;fy*$%%@U z_@xs3sL(V31QBv*@KR#1{kM4QfqqjqYu3)^z;m@zm`tLh(rxY$yrOCAc?3xC>xr$l zr)loH$&ECxr<$H$A8@@w=VPrQ92i-t3D=DncZ~1P?7|T_qw2kekU4~X^GLscl1(ai z(t>7;D+wut1SI>NpsX1C)sLoTOd^S{>xgn}NqxPq#^I$9o1Tow0!-F#?tA0ymv znM0BuGH?uwWQne$#7uPB!Bo0>imAAScVp@pY^j=B;Tj`}?+T?IdR?ql!^Bhkr&4cX zIh!!l78KJ3JNTU2JY9&hiUaA?K=;Vw8p#tP4T|AyLwPF6mC@v`*fg;fWP~~;q zd}1UucOMF!kMm;#0K0pC&tpzfrqgzr@%{oHD&ig9(C@G`LY-noZgyBbP1Av#HXsty z;8bh22uf6Dm4qyR3d^h3^(+&vHq_tdUXDL=QE_${P3ogQ2-3{akuW=(9JP)6A;KTR zO;JF_FA5FO0h&dhd|4pc-#P{H7Mf~VNyk)7oSb~wuMd4*5$8*PxmStqi0Hsk0$1&K zqwd0gaNgi=f7c(%$EaAjHXSaDC{8^ z@~YE0cPCUwf9InUv?sw<4GfgF&yHTmW{-@AyZ7<-Em3JCR=7y$VhJ@rRM84HF!7Ad zJ9_6c)cZ+eH+`B9|8=72hUAutN7)M;Ji;k;L{7Ac#0s>3?H&%MXEVQ8`VZZ>tl}+S z&=XOnuU`n_1or4(82oYjMDV0S405wXz`49=O5YV;ykZ_>1d$16#G;h$t(Ss=>>=P9p_Lj*qOipJkmc*hw_K% zEQ|0?@SgvFFkL9>p|sXol$~d0yh0JFCymLB422QqhH-h>7^KL}u%tpDFglU7%O=>C z|6fcOq}$7lNRoGXH%L|-(aV?s2!+lkz+6$KlUZqVRO3#{3C68tkJ20Pq@EO%P{%N> zM@+@O!g~f8{jsXc+#PpT%{EvnvBH%b%g!vn5)!U|PiP!uA&_=FUS!^dfu~enwKNB$ zA%h@}w|6OftVbsgf=cv<=f0a<$FffDb`C}RjBy$Mi|2*_0}erdc+MF5pX!Gw{teHG zm>XLe$>>{~+nCDPTA3TV|7V;kQbAi5nI4%t8OLSO9zknK7?~G=9)IQs;$9zupa3xe zMko}2;E6#K5N9LFp^fyF*jaMZgSQ>cu$j67K_t0OdSPulWj@7yQMJYE1H3$F5vaZ% zH9cey_{~*-DyUh~?20`99ZSHMn-dLBI4_7Ij_4|ZVm2VsP@virB91Pbf(Tx;59x#_ z1Zg7G#>6oh&Hl2c!Tx_zIT=i+56FN14=;R%j;qnRq1uQ;&qCq>}#aFiC!lQKzzuXta0 z*hpa~b4w#h@s_x+2?YJB+IM^2K@hHp8_1u53lz|V74K*$yYvK^`ya?j)`bfo)H?Ei zv;id zp$<*fO&Gs|zbX*@UO?ZyK_SH5^VsZh9=9i6?|x6|>Z{?Xse3_T+?siPhyQY(xMEGA z?@FWICcSE!uDofQ-diS*XmMpnQ|jJ-09&`*{d=?nnhB~_A<(yP!v8qh0sG%zFZ$=> zzlJy{Yb#9UDDnJe|959eHn7P4Iqw-jMyWd}fG$)M+;- z3^*g*?2Tly*-eVuR2kzWEuuHv)gDhf?(rloy6)tdOl5lO?rvX2gQdbGhc3voZesCJ z)~ntJZ3~h>P~uWAnFsE&OCMumHqqLOGy$t^d8FLK0v>tEp#t-e{7tKR+NQ~%)zrk* zdGZr1>cVB3@sLCb4**kT{tXQJEM@3KmO zb@xpZr^Sl0(C!s7QAEY57LlKu!%xOa*4497b;;ezkFZIUvNI9@&||iL<+1fwX~LBh zH9mIl3x!i=J7I3xU6bb~4lPAZcqmq8S8Xh>&o+s>n4n|F~8|n=|uA7c-1D8Nesxi=8=;ih@+Tpp>Q0r^;gFt1eHs3ac z$==Ql!scnX#^7nP!_ZZKL+>2}PTdQnir7XB&h;jPhINLN&C%^^;nz2BSYZYRx#(X0 zXmyib+gb0skr^?}Lu609T#~7vJ1V3+i0U@`jdC0E=uu6y5D1@LAlRJkwEsJRS6<9N=&uSVT z$i{e-VSREwJx^^JY}MrHcNVY4(3FEXnQMWwr^wb#$6Jr#Q!7lG&yl3Sn(YJ2w@o5Z zcf1QvcZbx)zwax$1SG4CzMq!&T?plf9G<1#4j1hkH3gGQyM@6fMMWk0Pfq)g{wlFg zf2Km4nknXoJ4W<>w{|JBV;*8CoL*$+43r-X23H~JR&&y*RojG_?Pb)$fSX@M6%cq) z;^!}-m!SRA`&+9=sS^Psf(7A*yxalo{5L@p0Yz8gbjNQLal4iU1FZYk)E)z_=m=YT z{?Gh(!>rqBTmZT-`o!-N$u}Gu4+4>QZ1jdUhhOUqj!I*HL+if>)x{6Q$5(CBwIv0M z4O~U-Md*I)X%)u{4C z{l+%=jV;Ep^xxJfyoKnk3-{$2WoRsOni?#M%ggGzlQ#5DB`=W;0zTz-QVX3Knm>(d zf3`f-kK#vN6fI;{3?g`LaELw!=&QQx=il^`Z0DjpMUG6DYDAs1G)8i#yY!RFep2^o zX?fd!deOx-?j*!%6k4$tD&;XdDmjM(IxG{YWsd6r*oIsCoxbR$w_sy6l;N=4N5tbK zE)=b|lX>wC<|-rflnV^>V0-KIzB)qTh(jg{Tuj&YY2g^)NqgbyfY`-Y*ja&@$FmKd zM6NTXCo~OaQ~C#8P!wCGaYWmkik1b38Lb9?x4qU$1*w||Xi|I89y+}H6#pZ)9y&LB z3$VSMSWFvdKCqAG6-?H0y={hOPiS!3fOJK=etR5KED!!|`!4jrybF%`(o479dGGzF z&GZ3i=ey67ALgAMrFVm`R=Y~1B2}NxbnCyWodQ_F@VU3o_;um!aZdJB`L_sxtkHXa z$b}JY2E5F>__?tu8+MT4B=|><#Ojc=$Wr=fm`H-Dyecpue*Wbjm-yIphFZ1|+7l^L;!woyv(vrT(MxIw)3BvX2j8$n-6&vTCt04C2NwVKD%xDl4!+ z?O1U2xUkro13;awEzT13I>Yr0^e(sCA0Q9uww$Ve$a;ro!ppO@3A7C!MP;Tw-MAgY z?>=Gd538}UX*aO6o}>l#I4&fA0M-OsriB@S%oa#h33#&Cnm>#!FT&R4tQeh z*@i56^R2TPga!oS1wJW{W#mQIlD^y&GHE!?h;oku5L2)Pw{?Ruq!2bjg z`F{hDu$!H&<9`AZGbYi0Cx-y|IvH4AlsrR0Q*f4dGVHGWnM;9u8l<5;zhKS*P?T|$ zh(|ulZ@C8YBDeR~Lk6b#&iFO{G=+V*cXxr)4fcq0&bI7aEm|VPwKRIHN{TEy1qd8e zMqgDMSOr-7sDy@w?-B}Fk9TdKc1R!cxYqxWztb^c*}V(ZAn)aW=Y=xlJ`lw@iQTc% z5n{V6jIdNfZ$2&FX{^kaVvlh55@#DVeZ__$NX0)0VhE+O`3Trl&;KYy&-m<{$nm`R z@%@e(gc+U<^_IBn3PXmMO>L5GW9!7Dqp`a)v(>M`PoFgebPPN8%^O*O8_SI|tr`z0 zt%b8zvH>#Czy1DyF$gg?u(kw6et-a*qT-VRQd3-9Nj`oVc`L2onV$tcy(B-jNEUw( zwXy5(pz(2vt|!eO3}Ar&Nn=v~HyHdiQSSc{KM9JNkO8Jg4w8Mb|Mta)Y^+aAgP_G0 zsu>COLyWEjR0PRhd5{4^L4#qAU}r(=`5RBdO%x14Y`^e-nx{vXr;R6X*Cy{btvst- zqn+vAAA%l}#>D7h4IZw&M}Ibm`(x$4zS}?y&U4l-DRc<=2g&a#TgcH8u~8sAr1*-3 zVAvpl!m8b#u!R*(ks=JR;SHX(}%`4f@xd#T%_pfB;j zEC4w`#=ogiNd?nUe-~66ZG@b?5Pa!pzD7f1()lmRyhayKXE3xdHFy z_s+j_bmT8c1LA)$BK}8={#_5I{}m%;8z*zC|Ku;}zxWHVC5@DSQ|_b16{1pbJOK8G zzl!8aGz=(1imkB)MoE886is&>{^k22l5wX(->yj^#T#fAzpe;7YfMou-@fLY9I_KS^)Vp!AC`+0~YzRZpE@+CU4c88s;)2lLoV zi`76WRq_R5rxQ<9)Ia%<*>2Aa9cJhWi~Zys?mwndr#}{*8*;lxfX-+v?zf1IGrrB^ z#TU9xLTU3 zsLR*Av#`U9K+_a3G}<$``nqi7J2FH42;@kkkHxuMd4Pr8i~DFf_pju^Dk9d0ZnoxYI^8hbFAQM<2ubW>0IB50N!pq5*ZJ=?!H|z(5G}5TzeEmvn=NQQLj`#i;IW=^I{*RG2{}|aK@i#i` zUhaA<@vju%lN_DJGTrL};!ae;a1R%Z<-1%TC(H{Gq&wbrHoVbkSKq zp+=QZj$*QR+9bI%uxVuGDfa^?C>Ia)C&-Q?CujwiRfTnk^kXvihQXG}B$<^VL|dX) zu;_B1k1x+6x`$x%s)=}55rL@M`IyT)lxdoLpT^l0gI-6Bf0w)sr+na>G%%9uHAGe-Aqx1`inff7fsT=a z&i5-eDAq644{X>J>^22``4^Q7T{y_!bo^fU8*Ajx%l`uUrz4T{zx4(FtCzTv<-bbh zl4P`D5^it(Um|+b94V41$46!+e*oFeZqec)eOYJEG-@+dF zsEe3E>GKKvgR!I86kvPQ=U6t#V6|`VA!=xyB$wH}$DE}&lQpEz=|EA97xm1f(;I8# zY0fbw8<*=KLyL?>)Hr+T7h1u=f7jd94Iryl8+WKQ@{}0N*kdM#{j=g3Offuu6I*+0 zsyJFpjE#HYrV&!48HQM)7>fL45xtU%TtO- zyiVOKPP7u@sE*6x{slxMxW&!f46kgkKLqT za!@Pp;)fT1ulbtt2sVC9(iUE&L`d#z_^GK-=C_Xf0tF$-loOr#bCFY zTgd?(?vNYJGDM1Kztk+g&{pws>^+_#J0A?6T}3G&v2C!4nmu7VHTlUs4j@+{xo66e zL(qzLASd!s>fdvWq|HQNgu7H(P76H+&zb@|1%GaOBE|(a5k}#z--606^_QsAzP5A4 zhva4l!Xd0eJ*Fc*f3p)}2Ve{8Wso0#G`}y+iYKmmxJHEcZ>TMH z7n54wBNNN(O&D8h7xCI>|2QOc)4=&xtBxl;?ex1przHXS|5VjT+1i-?r{w>e(f?Q9 zI#J2`k8h3abGbTGiJan`*ItsN*yxuaeQmu*O*cLcJP16pe_O0zNqw?vL@V^63>=Cc z3YhvN-)qqp6v;4Ii*vl}IAWi2pKP)A{rLPu;=?Ux$QvFQ**E4I%nOuy?)2 zokLf`s9`xy4LqVJHYUILfPn%7FFbcw@$O^Bge&jCw@zwYe4=cFO$|F)Vp@B$UYb1z zT$!V6)j5}6f1>}sTQTG4d|GwIxkHMDJ#ModU8AMoZs^ik`FR4n()6*9hhY<*oGoek zI)cRd?uyzcHgHpMp>ok(c7+K+lPLzYGwBI9a5|V~V_;gkwvx78#F?VGpG(uYk?z}I zB^Mbd=s3n;4j=l=E7A#zI$}~q7im+XF@2{p3DVG@Rk2UVdSFfsF-V>q;u))f&! z75)ekf1qm8pTMHEQ?!y~E;Wk%0vlU{v}?R@atwBJQlRPRnbK0Uh<)Ehgnfu05# zKYO2teiA|`7O3AIsi~Vzq|I~xMV5zu|7Rdfh_2ZX9WK7RmH#zT3hYgI2xxIF=7djQ zH&`AvUWI>y&mGVydZ1BM6Yo(h`lAILd;QXIaw`_$G-T_f;0h4^3b963F&*!QitnZf)pi>)G zc=a{JsTqz}?%9r0+-)y=Jyl&GX5CGQe;INcpduvw^rYL>-=uqeA{cX^#l#Tk3HXPI zK}?B>=?Bc>VbNg=&{Iqz>R<~}wE#ehd@B9?P@V13j=(yoGx)h&UA(m0{vjezpYNY2 zKxo2D;w7V3x+vFaAtq35cW+Bz?F4zk0@6NTMZcOr+b8&=&;d!`Nfx9;#_SM?e=>Kn z{D%1=`hJ^nF)JsF6(WjP(!`2mIBT~iVV}ttI}yz!OPt=oRAO@lczf@B#>$DhvPsB6 zIhJUNV`4J)LUX_PBkvQgqlY#f9?rZFvJXoli!&3s8!6@@ZiIdMij1=dWdplLk^KC zJDP7TupmA~W|TgyWERGF5>d2gW-lmmHko4?$2v_IbSsZgc(-oNO}|ho1?C}Q7A&xq zIZnvdoIwIun9LcC{LqVt#IV+oE-ykQyc>y0BbeL)R)IcHz(;iT@~;?>f2N-BgPD;n z)(s?-vJmObzz15>4Fi>!sJAeRD=RIA{eDn>jx%*SLQgGivP4mY$vammvo^4tk(pKW zz0j=f-f1wxqUgY0wkHk^a*naH0F{2Q{ti1s+*^W`qU7cPm1Iml13S{*j9O`9DBH~0 z`)9e+$V&k1JmYk?zN>tgf79FQuK1giER7=`ORb`{u#=+anT9|DD2}&3b7nz7?FrjL z{6dpeVlK#dL&P5B2wVT)sy@cmHeztu&>;RE4rnnv|>GY`=&1FNktWHNAgvPG~i$-rY(VpqonsH~-UY00c@4^(dBuzH;< z3mOzg(wHp3%Vx(pSc4FR%uJXp)QnwBOcD>{+r1kmdAVuv1!agMIatkIB`}>#td!!u zaE)d~s0Lp3(BT0BfAfqm`8o4al_6C9gX%6!hUwSxIzd(mV1z;FnacO$8}Q#D@UefI z;y@$VRNLoArdzFB?V$p=*Oo~NT)zX^aD^5ROZ>cWK{UZL2Pbi~TLjGK~=ux2yoqLg&>Qkbr|Hh z{*poE=uMJB>)7XtL~_CRMH~=rcEmNHf^coh=VpT_4SITe#wvk(J(649y=T7?=meL& zz+Ti2Xmi=ZUW8wI&2U_%^BU>d@OnnG=b{1&M(LBe1xiCBq{)vc3c^#B3olpNwN$Df zLtb-L@w(Vwe~Xq7x`xFG6)N;Wm9O35$C)S~cSB9hz)1zX%f3}%3z4A%wRL~H@3?} zo(FE5;U>4hcTdwFDWcNjSjdjctl=8bXRK_7Q5aUr2Jf(xi7AfYoCztG;0qE`Dg1n) zp^PQUCh`uZH?!RQ1pa09Q63gp&Tl%<9igYofBexcxDxaFTYzLbgPpl~`}63)@Xe7E z$8o_{cP(SXuE;U#^Rc~uX{uwzgCw9odMW_+pH3Cp|8q_CUroyuv?a0r_$Y=vE*m0P z4L`i{@mEwQW$#y2fvW_0vlNzDhTft?;u&dT57} ze@pD9lbfD@G}R+WH)s2;+`ui9%JqCtSukQ2yBV9BVacU9b)7e$Fmsy-&r#PN=S@FJ z45!I+0!XmMa1yLsVFNm8<9k13S-;f9w6~ ztrLa{8L5sSc?YzswPs7?I3BkEXy)vXA~QBSez3{5S}AYH=C(@kHlVjSBCarHfp6(< zLN!TM+G(cdMU*)@*AV@UVUCsAcAW;NubdLal~@T?C)Eis6Nm6tGlFZxGvi>%mtsLV z+7tR&#Aptvjuc!YnN*0#LJgaye`ao=Wom=`AEFdJ_QVkdaDr>&jllj%wjF#iij`!p}&Zwj-e?fhx3L9zi z?&rH3yUP*j;`q~f^`zkuqaAJz4Hl_p-r&s44*wjNSI($(h;+vyTl3^nFh3b@G=kvV zocbKFt4?~{icDsLP&im1A!KgiRBtNr+dALKp|*BWAF^WmV|Mhq-5o0KV4p5zGp188 ztJg)tw?GRxw1|+o@76bme`5F0o{;Bo_IP{MWs5f2a2&uS zf311fe^t_|+BQWe^R-wB zZH>oFr}dMkKA1kPf$W0a1^Th!8uGXzF4(h6##HT?IgV9)cArjM=ld2(y&I9RH7#l4 zzszR-ERyJdtZVppe>E+C#hDe|ZJhMogxw5{?VSGamR+&xl6`#0K~ooTW*fe!Ap`L z)0-M?4*N$6V=?!-QRVXmQezDGsi>L`fpU;VV4I_m36N0#e{eDSu@Mcq$e-iCA0wyZ zdet9bCy?t^94q8^`^2gd*c;m!8%WP8cbhlYnIx~M6}+D6)G#FCfAPF&i5jEE zWy9II=tGXfyEw{jYR*+I#FfeAe#^6C_ZH~I`3QHC%$S=Mdn8)yj&&lsmkOZr$V000 zW_R?L)BbX7f0&=n9F8yc3)P(nX)$C8rVrF7niHW6T=0Fz*C(+&RS>Fq$L*zorh^wq z2>?r<0_r@V3*lO_8sq5A6udlmu0Kt$%1L%INoYuLu^d;_f9m~I|8e&*ZynWqCo0s(@T2@> z^8D{~&Tzp2`(JUZqkjxjv1_4P3D35ZE^`N~LvSST7B zb@0W<8f*mcUFOy;41$fd092e#u01gto--Pe4?fZz%9fF6tE3G;<`#1v>{9>Ok3LM$UGz=;@E$X5JIC(0w%$6fIJZ7;e5*RYZ-0O13DeM;y?G%QXjX#5 zk(E4yVGh{~=jbG!q7_}K^ypLw@bOX}Uvl!&PVx~P?4$sMcLmv=HDe2dWW}y*#V)!I zc*%}G6<$FYWTD&%((@wTWu)N2tln|>APWDae;FA=q31=pW2EOryMv*3G|)k~%S%b_ z*FtfW{-uL@Cn{1bF^$>VWdzMPE&Gnwq`h20CcIt z!rfS;VPLF$(7CUy}0S1hUOVYDxxk-Bjt(FZBeGamWacnu%s{xe~3(z zBS^d=qK_AuQsRUm{hf@8e_jW^?XC`Q%_Sv!wTnWK;x7KgqFkSftj#j#<{(LXFNjWB z!gW-wTbyf(Wc+KUd7}HeAn%;g!^3K+&rfsNH1kk-+na(^yelo-6Pgt4sr|}?VRhye zteyRmG_Dkv@peKiqlog~1foo}e@ArMB_6wRsh4_JR+|fp<+(Bs_tZyVZEs1xkoII2 z!DP(n)*EaZ>2WK9izb|Ggr}n;WYol2A(@KVHaiGTF<2eoE1OL`d3%sQqY)I{Um1YU#P-(*gk&9~v&M;J6LK5Htt&5tfHBokmq!^jGpg~mft z0{s-9BmE$*H9?My?y-GLyAzVAEARBaHKMZ7WHu_8q!qH+!fAC&Ne+hG0Z6mq$caz#@{KOATeF7F(MdTno;lpc^3Nd4EG=*ToCD&xa2^x0Je;<0B%D2INP~H60JTIft z?k*}1g#D>ceUEvJ5#Tg&ev((OBY~-RrUQ+b+s4)equdO^ZN;1)z}T9=D#CT+E#dRr zwOlT^H1ma;wxz&{(Vf#2-+okJ@&oG)q)d14@MlSjA7HKqf6Pi<$PkjO>ZLW$;`W;j zL3eFknzt#l;zlqe^*AR2=Hd_EbWJFV&%WD;wjH`(L}v=s4fX!|l;}`$bp7%0a&%e+ zdPdsNjfpCDanLegSoOpVo-G%#lOU`%B>PGEGU<;z?_bGQo~xXK-Qa_qGmhEOP0 zwrMNuiobNO@|X4^pq(OhBcq>3a8+6=_|j8MID6wvRK2Gq5;3CRQWas{R0^Bxh1^0e z9G^=dyqk)f1|3Hx5e`+ld9-ut27@ba+VS<^Uxg>Zf65g(&}NV+N$wngbTa=ei;;P^ zbN*c$b?VC&=Kh<#E5D7>P4{m;7@DEXs&bC`J`)EYc}xNa7J+9ZKirLXC31QLQ|QTc_9MXQi%NC~Yfj{N^F`s?Mwi%= z?d(S}7tWOlR<5UN_hr4syoG$dnTR{bCNQhO(m3+~)Js_g(V~n*W@- z!M=O2SA+it*aPq$CLnPGClyDy%47$Ke=8wHS&k0jR_8hH$!+uU8^8>_1gr$K9pfGEhTZps*Hh_FYhpKNuU<%2@S$~k#ezAKD)1`JylD9=XJP9{TJXl zlDj3QSbjWka?z(d(si}2<@+ss;HB>}uc+Batx6k6aMrX>6=Ataxvg0cA2~iPf8~!b z@=)6FF;ig<1Z}>Dzf;mPu7#vcf4%MhI9vLcq>|Hja5VmZ3FV+zaY+z}MyFY~(yhhf)9 z^vQ)0MQ8Mve#_x;(S^l@N3RP=<+)&-;U$E@*XEBU0^>CUF&2=eUp^R&e+jMqFN^GL z_Yavh5sXDRxM2HPR~$Wb^6!q!o@GQ^KOklcXy0X8Oi_aya1DM?yjp%U76eDlXso?(bBQ(1jPGdDfAEvT~He~^jN{Oe5VU?@dzb|VM zM0olBW2Rc|9cp}XpX@JWX`$-31OK49{*Sd!{|Aopf6gRS{?~;i8ujIBDr7(ZnbpC@ z#lWV7#Z?K)Lb;fJVm%N@iv>vsD9vPT8JE%r?5=y$WnK9$zUU0Yf3COedLCCnis|JP z$IJ2cRMW%B@yxcDvuxf^P&>G&AXodoUUZ%YJESJ2m+}6BAlTZp^~HThxD*Eaq5k?H zn)OISkvnzi%K`f0i#w0IATSshQV08eYS?W_Brqglzo@mSqd-OAfYM&U7cmp9lLdx@ z>PxoF^9gwqN;7xf;P&ts7*Y|NV_p8(DRI=($|Wg z0etqJHF7sijLchAEtHBdUbJ=RtOnFCzZG8&ZXYUsDXZ45PD8Itb?hJpf?Ap0CR`zv zp03>2Bw$6s5|wc@ECD<}X&Cc+Dnw>8x`S<>C*B^cF($cxf0txV-*D%xX>XPPSUYqB zPW}{SaB?T!(00DP?~{RcN)}XqDJ6H!6G_IF)_s1g48mTF9vv9?s0Ne-*k}jZu@mF z`yy*5T#|k$e=DncBMa&sJGC&jMU5pL*ZUYX-5m@l#ARX0FpDJ_QBZVP%-*m(?6Xs* zNUg>!PZ#vw)`??~x*9bLJEQz$Ng#!?a)^*2D?(C_d*H(vEAmwF$f^>Q%bw3-_A@Gf zL1O!Ifjl!~2LbLV*0dK`Ko{P$C~$a}vbiC*N74jte@f%-Rk1ZCEYB`IC>`1^BiAjq?}f9{kuMVfXh+}OpN$cwAeMt_`W;xnwCs#l*e*@~a}-71V~=5ou19zJIFyv%#L^8W(gky7LR%VE<+(*3kfO_X*NI zsy+a6n}!F!frvfoPAtxm$3B+)H5}}N<|u?a_@+WA*Kk%0-IFdmPx9{B zwpm2#NHGGtTZ5Nx>6Pca^OF1C>@KxaM*_56f3PvQ)BC0IslPnj(q&7lu}^ zF=``#i6^g%Kl>Y=;%udd!!kH^VaT@Cjw&pp4wgu12hbh~d{`cCXOe7CJF= ze=LTtc!s@E0YmMc#rz$M6rCUXmGD-dvzLI45UZ?;*n|U zb8%uIYD@q1S#}x#2Br(|57F?Wf0oYG-@w_sPckV5c;%ts)>8pDyle$CH@V>k#? zLXJbzFs{PiP|X}WvGs4EjOkj#DLrRq)Oks}_EctT$KWR0!GnPk&-zyNTg)8ie=z=( zFR^5^$3*!?*ZPiCBQeor5dBAA6l4Br50+ZC!}W>ikeLV`KK%-dV^au)q!&-cEbe|o~njJJ)V%BR!%YweRHPH*K^L21!IMDr9WIg z7uwe(7Z4JoN}t^Cg(^!UwbZ=zvQ&2Ewl~?Be4VB(Y&bM`xuCghd`TSHtW1$aZ9i`b zBAb{AdC+TQTy1VN8_0|o@&8F${70>4qRNU2mMB68oWGwwEQ{W?zoEM%A60E8n@%?jYRtA+FF!g~ z$R4Z$2*_*%1E%{(r?1fFf8FIikXJAJ4)<3ATazdvs#q=dq`a!D^Fn2z?bG#Bm+vRo zHlfdqJ*qq$Jq}#R8Cw7boU1d_j$uQ}0)14V#$>}T?jiHKZ4U^Jb%>>!oP&H{96;6y zTVXDE^j?vfYxV|13T_ND4pV_6if|RUbUzGJMYFo7pl}azk=d%mf6g2Z1Fn(pF2z`L z6V7k&)6dcuZfAC1-v8+4jlcCMu!70ZAI^H}rwox>&nY&~TIt zbfKM;^#i*{EZi>|O8U@2bW1I|23avXndu@SqfebhDePJg*W^)~T)!GRGMi?3Xy1yY zvWzK9CX-*cGyA2uQtseiG^V>f8Il~mGFE}*;LYn!F$eMs*o@B>T(7x5<#sV^A;JGm~Aa$T+!N}gr?SMLuy`^+1zf-hnaG05e zTX39Rf71qiMO*L@7D>7gUt*an!;0t&Ze1BSDV(4kwOg3thwF{FtfR$Vv%JvcD!xBB zxvXL;5H67bXO@c^h>^-!?;rMd-yp zh=FI&s>jH+QjRFh_V>)L9$j4)=>sOf(#5~_f5Nk9hF#DzK%>|?pyiXsnt5JCnHIvD z!E9mdh$IbK#K!RgqGqWNP12p=Yz|%I&G`a=teI&ZGd0@jnI5|nmOW-*$13#<_)n)c1cG=hG*mq5jQCMP_U}0 zfBlqE^i3tH-vj-N;-~QK$KN-m3e~#BHmvxl0?;&5J%gU7-J7$bl5u z!&WbA$u_K;yp?B?hfB#qpCq()vH+Lw!V92ykm$a2rs7)b=P#@1RL zWSFV+Fmo<+q?uv0yFZQU&-p;T%d>d>e|_)xm=E5=+a0Xft7Z6@TkY03H=N+qPd$#b zXN7x}DmT3r;_kjfb_(Kr8kjrj{W~CJU9#Tt+zRB+pNwNv8Nk(kJb8-Q`0T#Ie}*^S zlk{h<*7Yg(c=0`^_~coiBkB2`O;$^IsQepQ2MNQ;gO@1zgV+NaTsW&db+?Fof6s*E zNjXGPSwJbqr_v-OeE^}bq8uVr2`cXb-e3vV;RVir{bq*e75*=NhTiHUQvmz+jpHBF ztpxvupZ~i2Dd22w^gGi+{>|)W(pv1A_W?9Tn;#io{ z#nTI|TgeJw?s=cMBTWuFZls=Xy_v^bo;p3Rd?&fbxQ|W~S+jj(Lu?S^DL-P9f$xWK zUy)vGgGEk$;=U@rGG2C^=JFPIZExz9-K*-{UYE(gmPzbUY0qqF7b)w^9#c!hWsz3dqL^IIL$ zTiHLqefb`5OYfdI0*`SJy#B@!Z%g-bq~MYI8RYpjy5A<%c{L#Z6*C z`Jq|t=;yBcdZg$g@`V?6SEmYVP*nz1YOudO)h%c*<4g{H_B0rr@uW*?pBNp-T-J0kk;kwZo@U5dCd=oil!!L<8fO#Z zqZ3`Az8b{QDARe5f8A{NeNu(2HjCEbA~#BeVA}_x1^=$f1mHo4?nj#Tv95_+Ry}eF z{FEr+GyWqKWu`xah(CBb#M|t5P;P!rDq>nFbeJFHe9!bOy^fN5$_V7RGtq(=uE#JK zsQy?5=-|5^kR6|5u!>e>&wWC43{wWm$0Krdzv0mxW3yK9f7f!jvZmn-^+Dg=8*%^` z++}qO0K=KiJrA=@NEwlPZE=%4k$W?>XiR?#!}wr5znbOi_w|A-wc5Iz2A2lo${q^K z{M&GES9?`9OqNC%hZh!UNQbBXBB#fAJ0;fSs2aM8HZ^keRLI?7bWOaCK`V4Ji&}9z z&f&=yyMEXpfAH)*f0+?Gk*vB!_aJ;*1y<5~A%!xp$f_k=uGIFjoynR&8*6bFSuE_@ zJhZ`jLN#zn2ifHmoG|I2AG%fvXI^g~FFD+fV`qsUc8QkWFO5uVZi9!BoqIQG3Rh7g zhJXVJ%X{*l^+%MOE)z;@BbM*-&l0l4DN-iXYGA?#f6bzxaLd#&XOko4+XYAgiQAjD zU1ct-FRLmC2Wl9J<(>vJ`5~Z;kI=t@6pf)9&hI-^QGTMZxGU3NfNp;K-R9VD(Ue(3 zt#)T`{Jw)dGftMo9+J8BN@Y8QyXAvr_1ODfAsI&@;{vn1!{#w(WJV(d%5Mxrgw7Fx zACnv*e{qD6*;85ytJ`ql=+uz?`RlB^L{;`szX4bScciudB@0ulwYE+#hNrgbimNsw%viL zFJr@kso}(yugB#80S)QOJ9{nF8tP73Q5G>_x$5Q0F@lDVuM5=ZgD6U}?}~_aEEl5w zf4Y$#4{kIGtasSm&(2&Xp+ZiysZF_n5gn`;N|jQB21-nM4KzYIKQOxR3%LG6B!YRA z^N~dJv^GBieQdHw^MsrN$}}c>xJXMTk-f6!BnVHYv>fg-wfxYtV-U;@Vb9z^;~jAS z5p}I$%!~YwX38Y8R;18v;PBrw9Zr1tfB5m@(EQ(8hNq*kF2-E5MCO?CXQ<-of|xk^ z_vzvxf4KSfsV4 zI@})05Zp!@+;MsRRdff!f6AJL zku>``nQN5S%fh}(JB86GTg~;m)CeLqh1w{e&kIE#F9>BHn>w_Sf(PO?BDE>m2a|#i zAix8dU#U9!J2uMDXZkaz0q7_|(h*S0&e4FUDIe)|>dTq_j&)AG@9-RuDTln*Mdxx8 z&ql8_I`pZ(!%f9}{;$%m1D?w6e~({eWhNr4VQ-Si$liNJ_PX}ot5Qahtn67;8{ki#WStH?%QZ0FoVvf~uktVlNBEskjBSq~bHteV0e}7(cD4RgOibf7bdR7azowzfN`n zPehES){@ke<;0HD6CAgX7v=)K@)CbYlhSx_b9=oaV|%2}F4d7DPe*xP_K_9)lW&)` zH7>8^;+`v5ep`xdEAI5A=&4)Ak0+DOj-(o==4d`$ZzccSkx@HZ6=Yj35_RqNd8cD9 zWo0}gBQOb%tx^kfe^+~Db3IedmhvXN+)>kc<3b{P>T=3GD!hT#ZLM3a&hOvQ73gFT z_R4Y6ZJmAD5?PdxTv1kQ(qA|=Sn4V3!S}S9#)_?5 z(qElg&NI{Ke|a9KWUDuJPphj@knyV$C8*vu`f8SqL3XZHj={#m=UN25iDMTfa@?6J zFDMZ7+w;1Q5?=QdOA&h5X*54TWJnD%vb>pX5o|Te(S0S?2Dk zTgJCfb!*;x=WL7VF#fwMUtcdH%Px^_)Gq0rn^xFre{z?5n#7I^hh&yvd!lv9Y3vnW z8JVZIT3$O^SXW-LyytncE3z6Ktykw@zR+*fhkiwNcK(BN(kR(zpgN{ZO7V$${&Ih7 zr&ruvcWgxR)_E^<2{+=+c@ty(IIHAJIOiRs(*Jn=)4cGwY@hkJjZH`2;MK=E3!fJ3 z++#}me=#;Oe2E&xA4~CkxH0RlH#8tQ&`_0&o6vpl1MXSe9j`M{O5a!SeB`X0{z_c5_ru{A5mulEa^MuHQxxoTVf5wO140l!VGL^zSZaKQdEc)oK2VZ1!Q{NZpqqpM1 zVsql2(@|y8l-sLRJV-8opwJ8=vJz@c`0(l2RWpnw^xtc*f`&;Bv#Hv^}*Y+1^AvKzR-%biPGpV5U|`sr1eicbm1o z{+&^yvDMM(5*L0E`FE9XYMmCDW8OM2yFPDS3kp`PYa4VjjK(*NF(pG!luErCMpsF= z!WbPJ{?6nEgLXgHYgrJ{=UAL#_ftCee;yjg?>=mhXJGvmM(kt){Fq~0kxlvRQheh; zxLHjX=`+u_-__~_;ymBJBF$3hOeOm+#i^AbguQ4kc3qfgQ8w*)elOOyW8LfO?an_F zd8GmgrUwJI!s%W-ASCAOl^h{X0J)5un4a0hWoovq68Aq%Y}`4e(;Q(YCs+Ddf200V zOJ)3<)K5~3W|t?nr{9Y;NEogAQWnHZyqUMLU^@AgpMB_;?(@^;!J(ra`Mh|eo1JIS zezphP2xNVss&OUsKFbzax$DXD_A08vwFh!umISS#xB~8(D{hrvso!MPeWflI8+3_i zuomo#S-g=vR-!o6X51%1T|zo)f0P-~RoNr$*PYhz?QMnDJKxZ1BDdt`rWKNvTBRBm zS4@*d@R#-qDc6P~ae7@o=vh5IYAZqtT(9 zWzjFS!ioN#vT5V<;bi_s`oq3{r_POZv6sGBpRT|Cg5@T%$bLG`NQ26-e(q)}rVONk5m$7M#e8ofXLWScD!nzkk@AHdMT+Cbpos5xyRqH{*Uf zU$?_3H@CZvylA^ivW>4_e+%r>_*HnzP>I@=aFH^lVD-`YS)=y5J0iZXzqHB}M3;-S z#c^b;U-)|5jI5)#Wjc^N->LLX>XhCI$04WU;##>HB60O!#ST|#LL(buwROjRqp_R` zjpkpfUai~Go~|Jg{nn|sv-sKWStOZ3YE#BF1mroYX|47(qf7N4c!L@p{&ix}+ zawu2Iy6}Rmp+c;0oWOmy(2G5y;Isx@?OQ*7j)uj=P8jCSjl^}=1?JZU4p50IawU}n zJ?&{zm$2#`Ea7O&lq92JDCQcpBn|K~c-Z1rIVDVB$~~s8rRg8P^U0=$I{%Z6l&3;^ zZVJ`C^oLZ*VwX%^f6NCl>jw=QDGPhfaq;`wN9bZ*=v}BU{4PY&?E_xnpWq zGGo13me+DgW^YKnlDAc(eOUF1Wi(G%E+uYIt^fJZ6JZIuSALJOzrRMC{Y9!yn{#ibQjy6Kid1uBg}(dJfWFxxiX5vwPGw$Kv@&g)G+V z;y7f%%pK(GqUziF)a*8zTgk}=c5S^0pSLAP{G^xT#Lr8`B@%npiTvKX^kwOKfL;_4 z2$ack(Da@Gf7uxwad&rD8#51gu-o5n)>YP|k|n+#c^g!u%z?D2>m8(jjkWpKi%VNNTnN$ z^(J{`;l?i`)xHLw?M1$T$g&)|dtnhzbzeCo-(m=@6OY`0e8yjW$|AIlo!4K@o`o6Me9()6@n^)h6Q5v5gU;ZTo~ zaVBVzJvVs$oU-whrdYL(YNbB*HmP?R*Jrh+(X7v%y528VN$U!#SGL9yB6^&kI)~d+ zM}3#*lD!sGv_bK52-Nsvcaxk?7*0!4GpJhBY#@U^ePMKXcV<|r|_zRjY&V(0Vm{6!nTD>XePSFx9uh;CeLyKz=aGLTvRiI2(l)7At=GB#!z zf8W7{`dr=Uf?apByH3n%7?+FehPgiz*WfFeDLk*i;+&4T<>k^Qc9IuMZ*c>oo054- zh#&=vcFGSUa{a}_6jh&$DYpn^?-2%kf`NoJ1-UH3PabpjOYr+#3FUM! zuDw+As-kf7^3$o~gJth82>L!BBb%!Tf0^O0k8g8!?4UbA*i%p%%+~W+CEt#s#1gNq z__qrcW8_@kV*O4)L8QwQPLUX|s#3nqhCqBW844^i5o%_xD1+z9zA_b?)dQfsz(;Yk zMSW3exlcU8`-wW@chOS(LZfAK>YAf}J}{sYP`q^K;)qZAyXY2z+iY~I>Hdi!e{KRq z#|rU|scIJXrhUr|#5TN+F_Y6A<$G)MjL1;gvzts}7g`f>7F>xp2!B|+pO3*!eNC?d z1`#S_T4H^QWZ2-xnrjwSx)a~+^!qVk@(-L(r6Z?jshD5tg%bqs{V5>fuTs%*A<~q?OhfM@{K{b}wYp)L=ws^b0Eg z&KMAFqzUZ%Ika(X&IzN>_uO5*oxKgT6_lkMEf^qBD9%AS0nvpukVi67e_*%$kN6~= z9Nod*f8E5MrYf$2E>2wZGLB4bhDc5zADd}?o|5Z$XwWf|Zx}P9Dk1p17M|SmgTh;& zGvL&9vAe6GSKJE+JGPObZE)a9u4vOJe~(;Mbu zFZo=t`cWeL$3jvWJqF=se~OaZ%oA4)43^n*Rqu6>2dz45uCrQOEm1QC^%}6TJs*|L z7;*5~Fm|hX?01dvX`TLLonv_1P5bvZw$a#VY};0o#?rcwVA;2i3x}ict%5mIyYZCL`drVh8vXHN zgn5hF;f8U^#}dziVj(e6PynhmS(Y1vZr#~{`&O<0c|L1wzJp`dw*spbS6BdO1!JEg z@*poaKZb4lp3)ludVqha-l4)^z+^C8VjevjyD*R&#q|SMxwOzwwTMb3IiC(n(Z~Sj z$dRDQVn7$+mUlY$I;dHr8wf9^j6yfP&lX~se9hFUf8fkioy0hq{)*Oag{4T_A=qKx z;oCu;!7;Ab6`Q8a@gpqK64nB!YMn)%2oQJ}Zpu(%mD4|Lk{;l&FqT)lonS-qY}inF zKgO#hk%U3v)Qe*_nQ4PBoWmtL7amkAx4KL8;XmzNEl-V-#GXZrl5{#9`jf8TQPXF@ z8|xE{{Fb0!@F&x7ZYubGWQ~9|`A#2nw6PrWhIa;0V*)#Su4Z20x34`=o$#|2L-ChX z+&?k?%#jrvfz0=isygS-0l}3eQhH7I2WUC{GI)K)`HgjT7Ya5W9kexWZqM|UOmsE- z8Zxwc{ifaMc3(X}S4ceV_#g@(G2qgOjUt9-!bLzP@3f%bhg~%s;ZzuA>Ut(WYHbaH z_x2-OVWjlqtubvmoRkUzSr^PLlLWS1dG7c(8)uy$7ezUAQj(T-0(mUDf*SeDbsn$)^8B1)@8k3e$|&L_u2h5<#~$nmD4Qb`^-!dQDi_D8yVx8 zWAbMy;0>V^c}14z;Y}=bq`ud*V#?7g>E$Xb)%+MCYXG_eoSovnxQ@F-dl61LQuF2K z@1+a47~Q74)ju(6fKLnLt=Yq2)ZQXdPjpl(6LdaiOG5Mk_YTwTTFtSQA3XU zMU0{+1_s!l7NgP;;#u}JM_d?&56iXd_Ss!huZ%NkCGuNsb1Z;JlNqUjyc?G+eH1Lo zZV*Eh3J|LWVPM0DbeZ^#=z)n4zDF9_{%~-fQjX1e*v_rQKnj@8IqNFH1tTk3 zU$r}(pIyQZhokl`Qtn4uxxBlgBVkf84Ku(9(FQL{SLcE_Fwos{4CZu{y#xoA@w6*- zotow{1I-dluM-X)c~>yUMD`h zx5*SoB_F4eT@i&&>ui(_>$bBJZG<{3acE+jyVRe& zT3Or|MvUwuyG;C^u63Yza<>gPNiGy=%GR3T1>O5=6HQd2=I25*gVcu5Frm@Kr~;9r zA4ezKutp}I>~Wct*07S$;LYi~qTR`lx6Z{kBKX-nSnHYAsme9w^tyk7i6@^0sTjXH z@&1%awqn@A^GE(`#FV|S7w?rX!)unH_O4$0pa>14Si>Nlc;^x{0{aAnuf$QO9`vmh zBCxYFHA6_^z-rB@T~^f!|I#^P>B8KZN+SG~2A+RC;izJeaGo~rTHbkRUpDKjOh*bM z2_1D?(Vsa#<{xK-(ZT`#^?u*_e?4-MOC(G@>rJ7F(}knJ9F}%aBWuxMWVgN5PLY^~ z(%$HXuz!t=RHY=JJ<=F^zq9ao%g3SpAp=bPL;GjBGcC1rL4VI!3qv$TGVr2{kh{?* z!tO343eN1VaP@9NPQyhFq6?+Ymus)poz2el*=r*>le-+ZKqo=@A6uOla7*I&L-UIi!YA~`^wE0zqEV!)1x?tq+Fpla(IAt zIZ96m5MR!uU9J?E(eF$-mxkRF2Zz6RD(jla}@l6OL=bULoUfo&byn` z3XZiXoe~+Z>DhXe2l~erOaz4Yn%J@1j%cAUpa{oTB}GzCTc5KlSPR3k zj?TVN>$gt>FO1uMBsqyELveLeS_)&QWD#`O+VTX z;YX&43_>r{XDV3OnaY{i!z&V}a+jxjqHz)l{dii4*?L%! z$>0+`wr4k|bqo$ADqX@243gE7qjH006|$apRuyXRzGXZ!Iq7zORO~aAKZs2IsQ?vU zBFCyZa{*U0hK=2C+?R-3>o^Qu$;6lK!tVtps$q;WbUA1-H1tU>rcI)IO->p-8}bK2 zLygk~p>&hmO7aPGe{tZ+!x>t`=*W!r4#5pAt1fa;wxJZGut6pecsF2mVG4Gzrn<7x zmFeH*&bRb{OX}Q)pW+G^6U1 zc9noYmvTa{RL;fT`Nt#bRG#7|tdwow*TNbg*JXZ2%Dr(9BPk>8wX6k4*r~!EytKmw z_hycJScbgMuX@oYV5s%>1mW5EWPbIG8cteMHoZ@^N9xlSV>W5{la);d{x5>l=;dW% zjcg7Ky&9kj~gt*>^fx4Tjc*fbZ`2&4xZQx2wF#C=#^EqOGOY!N&`f&gv=}EzrTedU&b1FH&^<~2xU%R^-~h2#Zn!pgbQex3ZtY-`iN7cZ znBL;qFdFm{bURe_YVZ*?P*-dvi-2xf^*;I*U6pUf_) zk7~JVWojQ}sgRYlN0kdXFWCL2&Dw}9MIbg?q#`#C?3@#=uV>{hN%h1= zvQihR7#ry1Y|pl+M4LULD6o7Jo)I*AxZf~w*^KcWTep{GWwF6L3vp@>fk@}s!0kGZ zgMcele?^++to2d<_aEMiw7Zj6+S5*rbQuJ1?Bx@0l^` zR%OO*HOn@^btp#mJP&t7`Hc&iW}bJ}EX9-rZ|wsQ12)eSy&tpiBZ{#{hlYPlxhC)K zibnyLtIQDv@0=Jb(eW{+na@Sl67 z%KWmenA^{At?!F&mSDbL*qLZ!z+WF|I>5wiZfa%tRCbmE38_D|wy}&JinCDuF^P$x zOYOXBa1h2lh`PKzL&{_AOEIArQusASNDc#RBTA{9ZfS|!%A(=>x_@PD@}=&eQB~-x zXwUhg5>y9Ofyn4a!R7%XLja@!b}Q}uk4kTJf|s!S`i6H2uh)edNB1y4b-=2ddV3HC zo0_fDTdMsslfJx9^oRjX+;i@oK)OTeudx{d@>_K^_W5oJTKrbG5lURh=PiI7mG(d{*|Mf^i!m>+y~_fFio)Xqv8 zF~g+Rhj!K)cm7T3)Qq`v;NZ^PMYpr>Nakw#LrqTyR@zs-#~13$q{+1GM`CZ)chbjK zaj2o?mh*Fn3I+{DpV&<2Us7wJM9IZHl8H?H`G}B_WF+(^Kk2^eRy@54<-D<7w_8qjS9OJ4>Xv6k)KI_Y%0ZmmuTs>)1KNtCe$-Vskjw_Jih zn`v5TP+QcLw;?Sl#Ah|C%BX}p>6OZ-l{Y&``k!Kbg89v1-gkc5Jvf+~F}RotL{f;Z zo;2=$p0Se=@-8#;R(c-9KPv=2H`Dw#80KauI2SU6s#04e>hYF7Aj(eSQft;$6c`t>S#T47Uu(D-PKel}zeH6`Clye_Ej5PKD}PMq zYWsRe)oDudm`c`{*v#sRzl_{mZ2T+KN|APHie}%cS+LLJTm7Mx;Kc|$Fgi%1mvfVk zw-SC{Yuu8@wwxr`S-_+>uK98>O2~$Ve(4}}R&VGXligEkK=)o&nq8oAw~83-o?_M! zzLl|kWkiV#UL%}<@mM=LCzpZsrR39{aN~^Kexidr+wCh+W0~j><-Xc_(QBpJ{`CZ9 z#Bs_I6&42aP_;!Fw&&WfesBTLDd2vFsZ8PtE3Dg-{9x$x2od@n51tYp2sR?*AkPbXK!7R# zD#9#Y-$NE!?27mc!tvlI47iFYsY8xvR1{9}7mp$;q$hP^U?$8wERm)4Q7I$+*%*W) zD^|DHg>hect1@i(R(n`{-5P?WKE!6ZXdn`vj0%N0Pxln6_zuF{P%BsiIQL}}CSQab zIqqkeG+JUP!_yR;nqhV|@N40({AcHACX{K;lyl&lLAscefZ#?ls94I&qXYXBOXOt8 zwdr-Z@HxqFV2)r406+kspvK%wzU%P}0=7a$Dtzs%IfB;W5ec3D+(_QbuwuLjmDMNGSh`W#kKKLm;ss z7iM(IZGf%-PTL*~r6W4pfZU#qzszm$^mE++1^`n7G^EU45Fxvk=$9T?YtDdlXsIrnQjpftQy}~nKeKzU-eC~|x z@A-U|wfO>3b2)9qrhh8dU&~nS873>>8Z;E;=SuQGP$8So7VmZuS0lOu>s3jC8;OeZ zt>O`~hDWCCt{X=Jk&8>uP#(bUpn1lAxkJqD8#e(i zz`g&`wd2jTfu7+r^hNp2@bUVNp|#|zF>MjNMoAqAA;-=5+^_NxcUPvCi*(Y;GPn|pL4iiKVGM^#5}ITXf%;xXlB41 zuL&ob5XwtV55bMmUGhGuLjYtTv2A|BisJmhli%i;GLxl@L}(P)y4?} zJ1G46dItcftq>ah_TgK{g+dv}=2HKjA-*!bjwKrSJBvj8$k?DN8z4%GSf$hO;tQA- zq9z41(^}Ca9b9OBKVU|JMT3VWMGO{b2u?uV$$q%$CXRn>5855&IY*^oHZ0@@wmF%4 zkxVO3)=zdlfmzUky(PoI>wJGCu~jv|_sZ$u$sfo$91Gjl(WTwQhuGd4{WuI?>B>QK zX2(nl!s)OM-=k#8#gT3bj0tfcftdO@Z{PcP`O)%=X9-C}O-#DLxr*X5IHZbS<-kvA z?ot$zXN%eqtO~;uD^IQ6$w}cf;D=bq?gak{@NnqU@rW~+xVL2zcT4+}aD5+SkDNn#AaiY^pXlNN zcU^L>uV}XVgD9{#iH&eet~-RI6L%8K8_3s?RLZ9bo%$)Ywfq;a#|la2kg%m}2gcVX zK=3n!+NWI0iYMgUXH`2eRwrsHgp3X|`TnOCV!~-jzom*$8{G6~6&V(3I_!SVXBsg= zCqLAot&y7JC{d>mF#t9MNhluUnKQi3 z8z8-l_e=i^4UTM2s>o~o79$`Smn6E?*BzCeDlYyqD%RW_;#WKYaTBWR2vPS9HFd=5 zDqk8oQ(Z9OT`V>L?)MhOk=i>7`Zw}ql3nO}4)U!E(IG=N*x;3EAf<3p4$0BF!A7Jb zn2{mkkrX1lz}MX=c{Qzeaegm+Pc`MVws z72CEe1kJfWH4%yr{Mx+JEs^IYE-()3R1QXL;Vp67`nZt~_*nu1{m1k&k39p})802y zLV}3a5k~T9oCCI4E`Z2q#P3Iitacspo(S3=N7^iSe7;TZW> z%x^*%@nRBjnqRkHkXmlog@3|I7^(T<40LkwMt;yW)ra~%w^5@12)^YdL ziA=aaedY}E+mc@r3-2o8pMpHZQzjSwf^UObW30@+)~aEyp+Y zpTN+BuR!;BSk&*|aYwP0HCG&$C^OSkFk#vG3D)fh9x#h{txwLPDlV4xbUTZox&1$7 zVzA2-NIqJyoWm-Tn0lyV+ys5iK(&u|eVIyv%c4Wmkx(}DEyat=gTY7^b^@a;*A=YwD} zBI!7lIoXXP@U;pdF@(=Rp{WaBi9=P+&ZQC;)v#Ln$MccixDwRg@*&V>`L|>s#B1s$ zt#qB$<|xjOw!f7KuT)+K2m*8nyQyFXl1067R{e)hg)J_=F#bdU*Y(GQS-|T34N{dp z$pFn%kYMwlpj&#QcWWRO)jp$M|hc`QgDSsC3YX*PkifhVkr0?%Iu9xUq#UZV+A(! z4#$y3Xd9AgnXhxUCDegK9(Zu8>c|jD0yeuyYJ(akRr{y`drYVmc_#%b(S#Hc>H8*R z0|3+RAXT*#MC%CAgv3C*vzTfCq{CJjdcVw(i zt~n}&KsiM$OB<`OAqrPpmw9x$9O)oemg?3(NI$3UKyRIt0)WDcjic~;uK^5y4(nR<#9sNdwskIa1g$Ptwkthkyo%`vJFnBL#)*E~=-aN}BVF?bP*;e?gp z4hLD|LDMDt#kwv%L@k%*3$!^yz?A*lR@XA&Fg(+u3qF-_jHqyooHGB=EXwY)#jihc zwCGTEf=J8lafrp`EmEJ4LH-v-g1t}48H1mLBLSz_$N@nJ)3NEtB6L)~xwMUcT3a=M zld>hjg^8GBQ8bV7rCK0qF0MWYvc_h6e&BA{4MQO(@Nl6eLSl<2(5;UDw>ol%TlMtoi37hvArmKj81y~;|O zU&?jgxRkS`NX-Ow7x zkwaW$u4ahSlLeXN*(;FWxZ?>gl@$h@{F5L4Gwd~3?r&ra5-QR@QoeHdILuy}uKhhQ zikcCro3$O$`hGhez%wQ1*@4%PDon0e6M;UIH~KLPuD&{rwuvx90b~t+8eD0Nty+IZ z5J?}l$G3A*9$;=##*m>;=j$2RRh-FMI^%;^1{}LUW>%JN{KZ#?h>f}yBxZV(UO|YI zB0liIXo#ip^|_wsIDP0|cB2kH3#2_5C4!}k%+TvD7SaPpjp%zn@oStldvx}NK986h zJZRG4$QD{C9-2GA1T)RqXFO<`;5JD&s}u-BoB)i#bl|Dw32y)n9V98TtDl1V)^}Gp z6l-IWL5=DvZZv4vTp)q#!bx9f9RsRrLukVQ zVlsFoFrYPxOi#c1_ZFCCk3Vo1(v<<+{AU{vBov)9k?}T>P$WFYJw&$$@gY(Ho%tv% zJBU@eML-IY8%pOTTLM~`*p`o(P#|)gn6n>6B{q(_ z-_mhNEwe^Qd7B*BrSgKZGSSNv0b~Fq^~MHkSfK7U?5%!SPiGE`{4!w96B)tvwkhnz z2sz|Tvk$XaL=@YPClJo2iiE*ko`NY@0#Trv2R{}Xkpq=jy}sEc2DK(kgm9liowO+6 z?8jjMI3Xwhje9)hj_EVtbP>S4XZX1x0EkOmW=Qc|NbdE(+K(jIqeuU31Chw-a<48n zAJ|szfDZQ0QdZT#hm%0%(}BttO1b_*&XU5Y zOpXD(RKj4v8On*N4Ge$7p(T5QKwAp6$BJm%{?k3UrST;Fb#t9q@$J;s9BDISBcXoqe zE#aJN5iI=uVhfXL#56wATM{9w$PNC$T7zs?Z@i#a%*FK)C!@>xZ>vaJD`u}z-GTP? z{Z8xH4^Pi7 zT6qzn_w(ggnEs;Ja}NTQGq{J2+Z;ddeqtV+{-!Oe&?^MR2ymPbw6p840V--*x8IE~ znDzz5Cb@q5NQ24R`MpDaBh~6SGE6a~hZyD46hy*5g>6IJ#nr41ye~3JBs9`6B!i&i z)+9yBIDyRwD#uMdG}OoATt#!`K&rAZ^g&)o31M*d+usVYDUPvl0Dpu0Yl;6oW&hU_ zoArIjj-dWpjX09kPbdI@68XR9`yHo=di^qyuQrJIMifaQqYVH^8UHu&*PNR_a>+uW zK?qzeoG&ZLrVi2mg)ESevw%~9N+GnMng`jG8{|I`>Gh4oKv0S|M2ZR%8AA$lJQ-99 zZvq)X^Z5l?JQ%3>{}BJo+x#9+rt!BM5=ksRkD#I%k^Y64@sc0?HU3cBx!=hkJ`;Af)}@YgJJ#+40{_FO9KGMS+IJ{ogBI7A%<|JP1L-V@AHKz^SalibAi`vd{bQi zAhJyQ1(jXvqija4o{D8I0juF&GN|#4L#3P5AVgSdeOa*B2=6Nb5J-qS~t`xOU!G zLTtj_cNz7{OuC&7nAp)5TpJ2~{qqD#v{kS4a!uo{-D~moL@!9aeTzucC(j1Csz6t{ zB>B-wF58x-XrrEKZmhYU%?^V#&!V=vFdro<9&*ujf{dc(?An9!-3XPewWPS%3ZjN)LU7c_ zl`)`m_CY};TN+-f^bs5P8uq4^Cc#273#?m;?*u}b#Z1X+wxAiK?N9G1d?nMA#=W`nXd zOHhYYJP7r4}7ZT~*Q{#QkB>m{-f- z7X>;sGQqt(G;~Ypr{pMyT|V?qx?Qg)!M?m1?h)nOi?8QkBRIIcG15=|QN%k^h@@Om zOgQqawgg5ky+pM#Sf8TMt|ie(8e7-Pc!$9l=0z@}qlkCW;Dq9@RrzHn+tN{b)aQ%H zfiOHH&!nfh=F6rvK{>-uJQFIKDxXNUKe76TJE8kh?k+wd-K!+NI5g9{1W^Y@ZSJ_C z`!emKJdsmIMG0-A;Q|K@N+@Z@m2iRk?Lhjk7S3N0}EI zp?^pqqRpKvN|>!3?7pnUQ{S9AS6Fe#Y>68xWRK~jhUmorI$#+I50xBsMz(PQ2Au!3 zw0|En=XMZ_yCTQ2kv%@c2~;ljU|?!c^)CMP)8&T{ZOP9W`bZOkH0X?ktZcARekyS% zK^u!{dscet8gWUC%l-x$AtC-o!cvCS+@y(vOk<$x1Gdt-Rc5P%=vu3&_Swj{#4kI~ zJo40ubCpF_mb4Nj>=k%Cb_PNmT)^$V!Z+QBr!hG|bC3EOo=f6HVgTxL>FVAeG=_a5 z<)YAHABO6#H`lB=V&&*}eMXyYiWolmJ`#HOS81z+QN(nb4ub|;@NMN5R_7w_0iop^ zHBSc8m%EiZB+rw3W6s5t_)m5GjLwaG$3F@;5pl0+VDgTHH_q)Z>lMfkErEJvY{D0+ zs-+QkjN;wfX|KifR1#=a)G}D{=^WgtI*i#2!7DR@ga@WQ30HU2{LOggbISX2N&)&q zBD7+S-in9m^QFnvZStdN+w1Ds8+K}a;))BQtFkPvk1_+lteWQt-EMFezotu>MZAQF z{CILmhmOf$%8nVR(O1(-bOn~%8;mi~CzXbLf#YkT3tvqF#v4L4?LY%KS`xSf*b#07 zRlG~TVj7%a52WBpUKeJ^ccPc9p#g zvsL9tjkSd6v+VM!&+_{dvCy88%?q(->w#Ii3N# zgws~hy%MjB||bcwg$&1bB)7l2K=F$ zUiHs2>>*ge^BlV^P0U>}VK#$PTUA4~H<&Vv!A8{Hdy%|1gY&?>aM)*K!e=8Cj~#$S zglsLV_#70D^l9F8U)8msX;@WkMY)(z0{bnA#NmBIqR?!F#UJNn>}hsk;G9%j>Y9kb zM8`h8IvV!2>&z-!$RM0J4)5;=s*u&%B&u;l6q1!?OhPeRa9J!*CBbXUSsMR4LDI(b zL81h2fYHo1hGSrhzwRt0*_fCY7(p;8!Sc?^PMYOBDP_W4qXpi;&gCLn~~WZe^*w96L|yKf^MqT?Rfav21L&m6pci z@~bRBF3LR`^^q#gLUOWFAbVKN^it2WFZVOYjE9SHuoo+kCE^Sv%lgxh)(B2k9JO&s zY$ENZ(tQukg*kw1jxkx)OP!p}{!ybXyX)BFy9fO1jds!cuvoQ+|=`w^DpFz6vnz7*9ob)ei&HQxoD`6x)LIbs3v1)7k&_Z zW4xc@`qxYtsZtD7$w`3n4wF2aZZEp6N|VGNZx4Ka`uqtsFxCWY=+?~mLway5mX2dG zB}3i>twGT5z9IpT~;p7YsT#Q!0|o3 z!JxbDh!7CVE{y|$m_tK|_kYrV@*Lt=^Pv>s7>k*!n^`JYhj;=9^rXudTqrAN<-KOc zK6}d6vcIopMDe(BWNf>$nlS#3$fs+nqzS2@v(lM3zNj!?x)IPA9n&eZr`1}X&`M{a zLn(_edSq$%TxFtI#+Gco`ITYtOO}W~YY|KS_=wmJODV0^{0Tytmq`6wl!^8z7Z$Y> zSHzBs-0)baa^`m+rc_4-&WJ5+VMh8cy@8WLs55fN-KcP_!SM8W8krQ!oyM+tNK?gR zAYw5&Ul-2Atqo>NW89SXIhjM~@9_i@7Ip@K2O%+sq!JbiD!smG?71X!8#QK#5F_+R zb38S60h!tKds^ecFW7UGv+0LKZga0O6`0}@ji~B2D;#yeR$lWZo?6q8OJ1lTt4Zq{ zlOPdN&ZMDh3!M$q=Q4LhQ`R!O4AL1n9r9*@^Z<_b^x1(i#Mc~K!oFnPa2iAyaILW` zjjax{@5}o2H{qAsWT?|40=CC5)|L1ArD%x7dFhi=i?~baF7T%*#g~pR=bu|IB)sT9 z=%O$dm-V%QPKAbuhvgihGwOP3NW~@CE?rJReieJ{9;vZatyX#%ZH`IC_>4w$SD%D& z!Drdnk0NZs?AnSb&-`^fYxrkpd2Jab1P|z5J677^$}V}+?MG?m>q_mJtjyY;4jvF? zm)g}`sr+dR-ZDB1Dk+#^=Q z=u-8iZ54|@qdC>G@f6e3k`a9WQlGUodd*?Dnlg7+V4YF*L&ZV!CG6scB7d-Sd{Vfs zFePz+Z3T*QKM`r;nN|(`X8d0Mh)*giT6Lx3CwG+u#pdf`y9~XGfE%B%0S=B=R;%oo zRdQVwa2k=kC($B*;5LTO&`X|f#M-;ys6f5B1q!2@hADrt;~Za#^qHGecQkbjQ^J8R z>wN!~t7LT=wSnpwG>M}qX&Xsq>FI+;{hpb=W36N#a|;znP!DQqBI0l8jg z4zN{}4da+cmU_@Nq{}YE?k;DvvA|t5>Oq8bfVtDkz!&yx=wmbmv+lmf=?z-sPOrz| znP%|)&7t^PWAof^qN)>}-lDbW@~)yA+b5U4SF`&NjWP@4D2jRF#h~a4a zFPj;?g6&ZPTfj=g_MH5W*W(m2k6LhvL``IjeBVqgIa_pu^85JvIIkO1spDY| zJoRbjjsQs^RRa}yJpQ6D6^!j#7kDx)MZ>Y2YCRk_u5#S5{;Z3L3HsUF^j0@tIjZtJ zT5X^hd;?TOY6O_nNG$uI5NBV%ECJDU?+k$?JCdHR6~?2^WD8j(m&daG7)ar*SPd^U zUjlZr+C}~I0&kqv+AmSV)yXAoj^|0gFj7ed;^YHv(F|-laH4n7_c@8L_iV0kJ&9x{ zr})@MU3Ao^C9&edRKB-wae;o5JsdzGKsSX|m>4_bY6ST+=zqULqmFqFdj+znWK*3d zQR)229K@IvAN*>(qlpn4*b)s>8yGl`?Z1o_;Et4d=UXP+Z&5XS6v9lkPfjp2ep9hL z0cUWh1MC7f>NJ)!+U#}yrQ{l?gzh^ROZ!xiRlvQldIt*pQG-Z{(P!7LV}GG&f59kp z)dzpD(c@)yK;-|${=%O1k&;p9B%eB{2HNUcNHujF`~&>&PWb;fGm?L9bIl&%WL%IS z#iTWRuX6z;sQ4iK3qeUC?fI+T@Dq7J7=xr49gryYznf+AS9?;ePasjH#)x$X<`V!w z#{N&_N@nY%6ePDmA*PTXlCh>RFOZ=&ZxoYtL4u^^|0Mo1+n3)w+k2ooDL`_NM2e9S z8EpzlC^>Yq;REUW-!}bs`ajwPhd~zn7kPvieWwEzQSt9)FOHHjBY}{jo;i$dL;!%5 z_@DKsNSl&BfI22(!+li%62Hemum98o2a@{8ZOhrcK*i6&q_pai5&>745&vK*Tqds| zLxhOB)*>zS`GK#l`1l#Kg==Vir)af*xzG15&uX%QJ~LAacjw_JLosu?SH^Wk5losW zdehBxgywXUu;QZmaCzl;{$0&lIDLdFYnQmfo-tF$w^YpU_9KVX?k z)NJq(u-nPp1&Zba%?Eg9VPOW>CNdQ=!a`4HNT<-*=!Vs{@Mx&@*$(+^=j^S+a5LSn z;sF)h#=5A(?VN8SBw^S&93i`OBQ?qOoR!194rNDP_cIUCusy;djC#&svX{V%%9}1-8#@$kI>A6 z!)Cescr>A{Y4DVyP)2+#uq6T$aN>r!2MUd14mXc#*4}|doSp^^c&(gt{&v8;|?p8t?Z+}P{k8OeV zJgfz-PX5_?%`*jd`qa5p>KGIBu@^L@2#scv<}S28#qYm*J2q4M`+Puj7Sqj6T12`ojg zrOQK5mBd!hC6a7Hv-0Mcbqfi0xFP^up8zL-Lex%*4{MwT0KzJ z!-V?Uy+3(RM`iI zX4DJ)HFdoI%*X$psefhb<_CR}xF*naPZEaU1pT)Rus8GH$Y0&MiIN-w2{hepkIfn@ zK?adc_D^D$mj25a$dYW4Q?^G*8Jfcb$@c!r%>ViPpE;Uqq{-P~KnR=^E9MEvrOx}e z65Sq6#`0JF{mE}jG6OOuk$%bVt-ZY|Azk22xdhxECdjORcaOe3nV|&68x*3+1q4x ze}{Ele-=OM)Zb%o!J4EygRhMbCs`~eU`2l~GLQPFWVS?%Apq<>wfwFEWT z=wDrF_AnyV`a2-nWz>Ywe@Fh`1CnAxTK5;Z;%#+R0U;XyUPxPmNn`(t>Zm6urR<<& z-v8M}U_mo$Hf3@Z4)m=KAg1W6lQ08W8Lf@JBV3uAqZt`)Z5eK{Dw@b@Pr z3iHoH?5rY||Ji25@T9lXEDSjHd(oi_bPN_fXO>^vR_8bW;E2J4W z$TN^8ijPTp(!8q;izfvfp)=;n;dC<^34>?;X6&hqf(lE#b-FIu3M`YRAC{qKoRA;u z%U8z{vr{zw!a{^Jv&4b9msDhNusvH6V_;%rW&Yh&#ahwV7@kP%DIRnSPc1!G&SnEM z76f4~jGG_PZ!pgwy$ksS9%&-r^!Nz9>>L9l!{^^jv|u`Z(t*Vp3F@21#)hfC2fI6UgT@>Xfq+Ld77$$y?$+^iZDl#xmFwMw8t1=>@e3G_83U4wr+eXY1e}Qe#}Okt0J>x z(*3$Rl>9h5x#c_j@cq*P6znUE_z^h}T$#YHKgOL$*Vdujp>V5sS&*rBMok%Du1ml7 zA$X6H?m#czp3z;!ndViZVU19OtQ3F{fLO|P7W`6rY6WTK{yw>b{Bvx|ym ztdw_g%*}CW8MuSrp80!+XvcPcwH`^ zkx}+wO5)5F%zT}8D({cBW{X$TZL!*3w*+$U&U^g8TK9r)6VFw}=7ToOX{~hqo`;C*lkaW>XNxj2 z1iI4)6A!jKjVN5mu&1WtskWM5J{^7rrc&0<8X5)GEOd^&mE_N2p$lL`5+a)m5;|V@ zr9o)lHhA@W6nr>T0OMGV+c-(@C7yVox5jNm8pkV8iBe1#tx@-}8r-M*?rBRf7bvPKW7vXSBNdD>nA!1>esW zIpa<6Q(#qFWFm}Iki!U zhyi1Wi28HGrBK2GSig8GMPUAWX680MuCu#j`)F(sk17x46uEu; zxU}@B^U!n0hVZV<7NIZ4M()0G)Av#D`LO1Uwzx*;KNd8eJMv&|?EEr*>5J;3mTpTm z-$!17w?{iJyZGN>{jZmVxH~&P)O^$M^mL8Osgv*dcFPa1uiIi5IPLS^x|n#6^#K_t zI#PaJEZaKZ*Wneca;;Y0H+`t{)Ytjf+xy(r4>v?R#a7Ko^4`gN_$Vl0al*5bTlf1; z_O5-Sld!6Ylk{$L+578WnNu3>WtnRqzc6CM`LL&vD)$!5kM~rJJ0~~po8z4-$3yn5 z8Nb^_Yw0vbxPPwwdXoR;Z0($r{$Dijgj(0V&#u=?>ALB&;b}#DS;xf?54#VOZ+7ae zylCLHVp{gVU;7S+w|&Wb^~>bG>O(z-XG#;)x1 z>c9|xjp2>Qlh_6?NA1L={e>n`x4J(p``9u~`Cuil)Hfzp)#{&lx+ayJUEE`3W{oDN z4vw05YR|trIo&B7X`_2er*BNjyBsBN^jq`l)jqePhq59%Wpw&?&3Sa8C~2tocuCNR zw$A%?PX>pYODFzax2553SCFqu>Ac-z+s>Z|>wEXy!F`(SfqlW|GRD^x74y7{yTWzG zMy=_-@TY70?gMj-YT^x#`1Lz}wzEpIvDs?%I3l8{>DSqEBXiZVQWP5s71d3e2JanH zI@O-)Z=kg8bgpv!Y?%ndjb@G(L3ceryk(a^`?~#n@}-be+ig;EIWqc&4JwQ0Set1z zx*W_^f0+4Cx$fCKjrrv={NM@Nt$gcAkJnc8OunyoC1BNP^GSY-?%1q9AuG4r_Ow-d znMQiJ)9H&wysy4X!*sqrd*nFVvQ+)O`-*Cp(BVC;*X3+VMko}gI@n?9%j9a1yn=YX zq|fTq?Sr7ixAS2-wl(c5teMYZZ4IU)$dZKb>&a0g^S*&=_xZ=LVljbPTbvpw0R-3k zak>G~{A%8nQ-F|y?0|{j7y5G)$-SS2Nd^i31&9>`LfUa}s=Ck*v;8+XWgmsp7IQ}N zPyWOiBzX(HO3ataTY5E67D?n@Rw|i|gM?(RDX(}hfr-InaAD;C05^!IM^hLE0=O~r@x1mi}ux_JNF_i z$Kl)CQb*KmTC35l^l6xNdsz9g|IE}bt-i5tuVdGh2g`DeZ}`g?=d>l8J$_I*$>(lH zMz{@*{a73u4FAXD(&g~qoxV~$Y@7Y^CfR_KR;6={{@~tj(5|!iJE}5EcD!GM-*lBY zB^9Ol%AqgMDOaA33^00@@+xdQKk5Fi;9TCTqc5fwq#5hYd89P#!NBTJzl(}VZ}NJ1 zov%L!WcbOXX&Q|QnrQWOg?F^|+aAlI-2G+`;+`BDw768>u~og_UwLm;;{?6t&g%sR zo{gwA)@8C=ao#QeiwA$1rXGCJaJbb}J;l0NV|vax#_Sp~ct1ZTQ14LEv6NQB-WPMf zO^=^damn@2Z&qh3%YPo@@LP;s;I~7SCVhTI%Okql(`%Fpx1O&2YeCIWdhOfG2D~86 zi8{3pe45#=Z)DtCn(ar2N45NEpKG1@AU(D7O@_bGKTiv4CIs-KrS0^LO8?~fHmthS zf4+UAzHtaN|O9=@LblQY*ki;UiV=<-bT&UF3#B5xo#z}Ymm zVvc9!J&R5Mnaw`&`epm}GVSg$t)ahq+|J`|i(h+uZu`{s55w;HbhtjBa=SD?(c`%G z^%UE@eEF>W{eRde4euy+7~G$zKX<8hVS9qkMCq99aNX7?&0|)+kKWcRecW`7@fq#e zeM@#m?rokogRk~Q?(Ik~tCdT&RCZWC`FGKWtZ?NOie`le*|s-L2V)zlSSNBjiG>9+ zSD1fGipAHlL1x6eh2L4>lHT1Az@Mu)VRAh`@uFRR*=05t`vz6Wn z@n0=Hw#iRx+OE0b{*Iqb%6cl#)r8cX4gK`^%}c{3)um4MW*dB_U%7H1yC&N-^RE)s zb-%UkSl+$b+WuYrQQfD7j*CLvy1m;^4XSvVw=QDe(ad-MSoZ9))7lihynQw=Vl@nS zM|Gs^F7m(pXVfx7pKp6sCmN5{+BO-_c&|OJJ-@;0Tjg<{rdyoW-b}Y}k6GuG|8Qrg zTv#kWV2)IZ$8S1TFofUmO zCx-V%_JzZSk}97s78lEX^n3JXm`t6b9T_q*Dlp$5)akm(&M~&Pr?`F%cpv*siA-uD z^GX;V-;CtGhYq&bl&@X{0f@QJMq)MoxP$u<5aum!6KVnR845Fc@%dx8Z;6-ODmlyB z0ZnX_@F%|9L#`W{HjdS5{P_|f3nh0E_~zUU5|5{klanHjWG)$xD@(ZUq@m9@t)D*t zcR4HRjt+eSh;Q*v_g(;`NfI*RZ>|pE@>-ko=_Ej|Nv_U)ol!4=EK`s9k__T*lGJFS z9a1J5O^;gm>O4SvB{kw=K$ z2mU`!gthyT2LEY4x18K%q=T59lk=+E@D7Mpp@t(Jk&R#yE$-$j@LN7|JIOwV7$MQI zg+|Ddm|Nr&WyNu4y>t^km_C5eOfpdY+j{LzEH zz2WGQhOQ{BQEY*R#!Gg|(g6;e%*E)pm50uP4=MpntSU*+w#ZJFf-($>G!z*qvQXrp z$U{+pq6mczMG1;B6cs3{P}HEPL(zbu2}KKvHk9E|bfAoYq6?=${QU+ESa%agf;dvcw57GF%jG25yVe%}{v~KK9d?McLFDhBiZm8(iaF@u>!r(5AX~v(7qoiS%C(UzSN(q} z0ZOAxg|9)8Yrj*78^fJZNZSB<-ls49gL4qR<{3!-08~-RLlhbkm+#n8V3z)12QKXIter;Tb zv}mk92!5^K%9X?FAf%XJn5#S}W}`}+gl2DcEU2}eRE@>;IYfmtg)qFYfIZ@np+JqOI8Z}I5qAfp@stZl z5Km?hFQB0Icw{_M7)9Q15aBnDt_duLA@&mT`-vhrguApd#e#aa#fS-OLnAE0V(CMZ z@-(SRXuU)Qa}ct`ZPi>g>|cp=fvwDiYcwfNaH()D}!T2Cj$6Y08gotMSsw}s1u-7DTjHwKoW%iT@p36pt zBJ>w2FYRgI6HVZ*&J1+eRBZhomMZVQaWsS@RS@?`Aja+M;Q`F>87%y2Ju)@@-d73Z zz7!BSeW6%0xb;R$7aU{TFc?w2wBR5x%88N2`>SEhFG9#j5~G$4n62xiF)y7v7W+Tv zDB+*8k*cWK_^N%~4&WXL)|ujAOIwB%IQnkvQ?59;NCWknq1S zc}S`+7Hpy6UAM<#-j(RZb>Ln5*e@$!hDb|eOPv_Kil;MEM! z<;jSP{D2vl!L zLduj3gmGs>!_Ae@atM@}A_)j%v!7&$>Qk_41Y%F1^CaFgJ+HvA3Q>kks!8(xq%iY(RgOXU}NrYp;M4jAtcWJIIm4#armjMHc|m(kB8C3^NUUwE(G! z7}MWBO=B>=^i&?VA4Owv(>mDZf_Hox*y$*pbJK3%e7R4+Sr#K5N)amFMv}4e`+z*o zNkrpC`r%jm;O7px!}Ycn75{ z8Hr!+0id^+7|6v*6|})cfA2J%(dczQ1rjr+;5tW8oWo!tke*mv z&jTPuFHflT;RCd8rN4t>dQizqiq}kRP{d{9h5P7+@2&!d*&&eT5Z{LlNRFsG=})3y zOIY-&Nc$WnG{e^-G)9D{zmH=26>F`6c>E-9Ls}vl`h%(Iq87;b*_c41ss6~(k{HYM ztGLXu+_6!CxSOY_8Tyr6W@hjrQ_xITEix3r=~tT^7*eHYQJjU2y!R-5=E}oh%Es-4 zp9Ow^VOt44)EWoh*aTIYI~8#BE*J#U(~uVS!yHWsmFQQUnC2MREhg5)ZD~kV%zZgz zQPL}GsXp<o@Z@zh_DzPR%P z;hXf63aZWlA2Q|YVUv|yIf>q+-#lR^)b`av#?A}4`Vw^XDOPS6AOJdP%=*-alK?nS`V z`l2F-HrP{>*SAL$wWG;S1w?@?1W zN!ccdopb{%NTr{TV+&>(*xggY7Me&^(#`=SQ07^(MMFUjAKXMsYjDR70QgK{lcC^` z&-sG30r~WyxF!bxZO#;6CT__=2BMy#AILG&dgJL7eg-bJft`(Id9(;hKZkQ=sM+N! z)Q23~?Uq84V~&16$8?Rp0J@4JSAuIqrP9?HUkqyO;;{wM>HZ0>rlhe3@&FSWOo{G_ zJ+`BfVv7$DGjxSebyQdwB}v~y7MHGJLQ}C3S08uh&{U@%p?EM2#g<%Err7iY z0cNCgOc07)wUd^&(2ob0^BgOY!i)0=`Z)nJG0mx%4UHO9?1wmrPm72`?>0IwBXQpC6E|mhYd6w4yXILsjgZM-OnzNUl6JQ)qUG zsJggu9~vX}mYy;IRAmVPFc)g#sgM|pnq{0Od~Xx}R-~zC(}^ws9}yA#e8H1JG$WTz zRPBtkBt5{Q1{n<7A;fL9gB1fKbEY3VFq6}e0%1?UeiE!=FjrN=H|YlwOb>a4sTQ4Z z&tbaD1(H>QO_4Z$(Ngj$i(FaQzJtvX_$M0@sk?_^(}wkb5dNC< diff --git a/src/applications/bamboo.ts b/src/applications/bamboo.ts index 9d64dde..d2914f7 100644 --- a/src/applications/bamboo.ts +++ b/src/applications/bamboo.ts @@ -35,7 +35,7 @@ export class Bamboo extends Base { dockerfile_inline: ` FROM dcdx/${this.name}:${this.options.version} COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/bamboo/lib/mysql-connector-j-8.3.0.jar -COPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bamboo/shared/plugins/quickreload-5.0.2.jar +COPY ./quickreload-5.0.4.jar /var/atlassian/application-data/bamboo/shared/plugins/quickreload-5.0.4.jar RUN echo "/opt/quickreload" > /var/atlassian/application-data/bamboo/quickreload.properties; \ mkdir -p /opt/quickreload; \ chown -R bamboo:bamboo /opt/quickreload; diff --git a/src/applications/bitbucket.ts b/src/applications/bitbucket.ts index 46ea5f8..cadcccf 100644 --- a/src/applications/bitbucket.ts +++ b/src/applications/bitbucket.ts @@ -32,7 +32,7 @@ export class Bitbucket extends Base { context: toAbsolutePath('../../assets'), dockerfile_inline: ` FROM dcdx/${this.name}:${this.options.version} -COPY ./quickreload-5.0.2.jar /var/atlassian/application-data/bitbucket/plugins/installed-plugins/quickreload-5.0.2.jar +COPY ./quickreload-5.0.4.jar /var/atlassian/application-data/bitbucket/plugins/installed-plugins/quickreload-5.0.4.jar COPY ./mysql-connector-j-8.3.0.jar /var/atlassian/application-data/bitbucket/lib/mysql-connector-j-8.3.0.jar RUN echo "/opt/quickreload" > /var/atlassian/application-data/bitbucket/quickreload.properties; \ mkdir -p /opt/quickreload; \ diff --git a/src/applications/confluence.ts b/src/applications/confluence.ts index 4718073..36520b8 100644 --- a/src/applications/confluence.ts +++ b/src/applications/confluence.ts @@ -33,7 +33,7 @@ export class Confluence extends Base { dockerfile_inline: ` FROM dcdx/${this.name}:${this.options.version} COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/confluence/confluence/WEB-INF/lib/mysql-connector-j-8.3.0.jar -COPY ./quickreload-5.0.2.jar /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/quickreload-5.0.2.jar +COPY ./quickreload-5.0.4.jar /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/quickreload-5.0.4.jar RUN echo "/opt/quickreload" > /var/atlassian/application-data/confluence/quickreload.properties; \ mkdir -p /opt/quickreload; \ chown -R confluence:confluence /opt/quickreload; diff --git a/src/applications/jira.ts b/src/applications/jira.ts index bd95f80..14d7b07 100644 --- a/src/applications/jira.ts +++ b/src/applications/jira.ts @@ -33,7 +33,7 @@ export class Jira extends Base { FROM dcdx/${this.name}:${this.options.version} COPY ./jira-data-generator-5.0.0.jar /var/atlassian/application-data/jira/plugins/installed-plugins/jira-data-generator-5.0.0.jar COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/jira/lib/mysql-connector-j-8.3.0.jar -COPY ./quickreload-5.0.2.jar /var/atlassian/application-data/jira/plugins/installed-plugins/quickreload-5.0.2.jar +COPY ./quickreload-5.0.4.jar /var/atlassian/application-data/jira/plugins/installed-plugins/quickreload-5.0.4.jar RUN echo "/opt/quickreload" > /var/atlassian/application-data/jira/quickreload.properties; \ mkdir -p /opt/quickreload; \ chown -R jira:jira /opt/quickreload; From e8abb5f8852ac207d5a2a9c96cfce5cc06d6458e Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Tue, 23 Apr 2024 12:22:08 +0200 Subject: [PATCH 15/18] fix: external files of reset command have been moved --- src/commands/reset.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/reset.ts b/src/commands/reset.ts index 501a3e0..7b74420 100644 --- a/src/commands/reset.ts +++ b/src/commands/reset.ts @@ -19,10 +19,10 @@ if (isDefaultCommand) { program .name('dcdx reset') .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) - .command('bamboo', 'Remove all data (incl. database) for Atlassian Bamboo (standalone)', { executableFile: './reset-bamboo.js'}) - .command('bitbucket', 'Remove all data (incl. database) for Atlassian Bitbucket (standalone)', { executableFile: './reset-bitbucket.js'}) - .command('confluence', 'Remove all data (incl. database) for Atlassian Confluence (standalone)', { executableFile: './reset-confluence.js'}) - .command('jira', 'Remove all data (incl. database) for Atlassian Jira (standalone)', { executableFile: './reset-jira.js'}) + .command('bamboo', 'Remove all data (incl. database) for Atlassian Bamboo (standalone)', { executableFile: './reset/bamboo.js'}) + .command('bitbucket', 'Remove all data (incl. database) for Atlassian Bitbucket (standalone)', { executableFile: './reset/bitbucket.js'}) + .command('confluence', 'Remove all data (incl. database) for Atlassian Confluence (standalone)', { executableFile: './reset/confluence.js'}) + .command('jira', 'Remove all data (incl. database) for Atlassian Jira (standalone)', { executableFile: './reset/jira.js'}) .showHelpAfterError(true); program.parse(process.argv); \ No newline at end of file From 9224b2877b0ce543b97e7482ddaa2d0a74d1d533 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Tue, 23 Apr 2024 12:22:49 +0200 Subject: [PATCH 16/18] chore: refactoring commands to remove code duplication --- src/commands/reset/bamboo.ts | 24 +++-------------- src/commands/reset/bitbucket.ts | 24 +++-------------- src/commands/reset/command.ts | 27 +++++++++++++++++++ src/commands/reset/confluence.ts | 24 +++-------------- src/commands/reset/jira.ts | 24 +++-------------- src/commands/run/bamboo.ts | 43 +++--------------------------- src/commands/run/bitbucket.ts | 41 +++-------------------------- src/commands/run/command.ts | 45 ++++++++++++++++++++++++++++++++ src/commands/run/confluence.ts | 43 +++--------------------------- src/commands/run/jira.ts | 43 +++--------------------------- src/commands/stop/bamboo.ts | 33 +++-------------------- src/commands/stop/bitbucket.ts | 33 +++-------------------- src/commands/stop/command.ts | 35 +++++++++++++++++++++++++ src/commands/stop/confluence.ts | 33 +++-------------------- src/commands/stop/jira.ts | 33 +++-------------------- 15 files changed, 151 insertions(+), 354 deletions(-) create mode 100644 src/commands/reset/command.ts create mode 100644 src/commands/run/command.ts create mode 100644 src/commands/stop/command.ts diff --git a/src/commands/reset/bamboo.ts b/src/commands/reset/bamboo.ts index f50f7cd..0b9515c 100644 --- a/src/commands/reset/bamboo.ts +++ b/src/commands/reset/bamboo.ts @@ -1,29 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; -import { bamboo as versions } from '../../../assets/versions.json'; -import { Bamboo } from '../../applications/bamboo'; -import { AMPS } from '../../helpers/amps'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { ResetCommand } from './command'; -const version = AMPS.getApplicationVersion() || 'latest'; - -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default(version)) - .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .parse(process.argv) - .opts(); - - const instance = new Bamboo({ - version: options.version, - database: options.database - }); - - await instance.reset(); -})(); +ResetCommand(SupportedApplications.BAMBOO); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/reset/bitbucket.ts b/src/commands/reset/bitbucket.ts index bd7d5c5..1373b7d 100644 --- a/src/commands/reset/bitbucket.ts +++ b/src/commands/reset/bitbucket.ts @@ -1,29 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; -import { bitbucket as versions } from '../../../assets/versions.json'; -import { Bitbucket } from '../../applications/bitbucket'; -import { AMPS } from '../../helpers/amps'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { ResetCommand } from './command'; -const version = AMPS.getApplicationVersion() || 'latest'; - -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default(version)) - .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .parse(process.argv) - .opts(); - - const instance = new Bitbucket({ - version: options.version, - database: options.database - }); - - await instance.reset(); -})(); +ResetCommand(SupportedApplications.BITBUCKET); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/reset/command.ts b/src/commands/reset/command.ts new file mode 100644 index 0000000..9cafd48 --- /dev/null +++ b/src/commands/reset/command.ts @@ -0,0 +1,27 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; + +import versions from '../../../assets/versions.json'; +import { AMPS } from '../../helpers/amps'; +import { getApplicationByName } from '../../helpers/getApplication'; +import { SupportedApplications } from '../../types/SupportedApplications'; + +const version = AMPS.getApplicationVersion() || 'latest'; + +export const ResetCommand = async (name: SupportedApplications) => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions[name]).default(version)) + .parse(process.argv) + .opts(); + + const Application = getApplicationByName(name); + const instance = new Application({ + version: options.version, + database: options.database + }); + + await instance.reset(); +}; \ No newline at end of file diff --git a/src/commands/reset/confluence.ts b/src/commands/reset/confluence.ts index 917055a..04fbb97 100644 --- a/src/commands/reset/confluence.ts +++ b/src/commands/reset/confluence.ts @@ -1,29 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; -import { confluence as versions } from '../../../assets/versions.json'; -import { Confluence } from '../../applications/confluence'; -import { AMPS } from '../../helpers/amps'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { ResetCommand } from './command'; -const version = AMPS.getApplicationVersion() || 'latest'; - -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default(version)) - .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .parse(process.argv) - .opts(); - - const instance = new Confluence({ - version: options.version, - database: options.database - }); - - await instance.reset(); -})(); +ResetCommand(SupportedApplications.CONFLUENCE); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/reset/jira.ts b/src/commands/reset/jira.ts index f9955f7..039fc5e 100644 --- a/src/commands/reset/jira.ts +++ b/src/commands/reset/jira.ts @@ -1,29 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; import { gracefulExit } from 'exit-hook'; -import { jira as versions } from '../../../assets/versions.json'; -import { Jira } from '../../applications/jira'; -import { AMPS } from '../../helpers/amps'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { ResetCommand } from './command'; -const version = AMPS.getApplicationVersion() || 'latest'; - -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default(version)) - .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .parse(process.argv) - .opts(); - - const instance = new Jira({ - version: options.version, - database: options.database, - }); - - await instance.reset(); -})(); +ResetCommand(SupportedApplications.JIRA); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/run/bamboo.ts b/src/commands/run/bamboo.ts index e90befb..c466e18 100644 --- a/src/commands/run/bamboo.ts +++ b/src/commands/run/bamboo.ts @@ -1,46 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { gracefulExit } from 'exit-hook'; -import { bamboo as versions } from '../../../assets/versions.json'; -import { Bamboo } from '../../applications/bamboo'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { RunCommand } from './command'; -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) - .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) - .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) - .parse(process.argv) - .opts(); - - const instance = new Bamboo({ - version: options.version, - database: options.database, - port: Number(options.port), - contextPath: options.contextPath, - quickReload: options.qr, - clean: options.clean, - prune: options.prune, - debug: options.debug - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - await instance.start(); -})(); +RunCommand(SupportedApplications.BAMBOO); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/run/bitbucket.ts b/src/commands/run/bitbucket.ts index 9413a90..b1e22e0 100644 --- a/src/commands/run/bitbucket.ts +++ b/src/commands/run/bitbucket.ts @@ -1,44 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { gracefulExit } from 'exit-hook'; -import { bitbucket as versions } from '../../../assets/versions.json'; -import { Bitbucket } from '../../applications/bitbucket'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { RunCommand } from './command'; -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) - .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) - .parse(process.argv) - .opts(); - - const instance = new Bitbucket({ - version: options.version, - database: options.database, - port: Number(options.port), - quickReload: options.qr, - clean: options.clean, - prune: options.prune, - debug: options.debug - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - await instance.start(); -})(); +RunCommand(SupportedApplications.BITBUCKET); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/run/command.ts b/src/commands/run/command.ts new file mode 100644 index 0000000..3e07442 --- /dev/null +++ b/src/commands/run/command.ts @@ -0,0 +1,45 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { asyncExitHook } from 'exit-hook'; + +import versions from '../../../assets/versions.json'; +import { getApplicationByName } from '../../helpers/getApplication'; +import { SupportedApplications } from '../../types/SupportedApplications'; + +export const RunCommand = async (name: SupportedApplications) => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions[name]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) + .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) + .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) + .parse(process.argv) + .opts(); + + const Application = getApplicationByName(name); + const instance = new Application({ + version: options.version, + database: options.database, + port: Number(options.port), + contextPath: options.contextPath, + quickReload: options.qr, + clean: options.clean, + prune: options.prune, + debug: options.debug + }); + + asyncExitHook(async () => { + console.log(`Stopping ${instance.name}... ⏳`); + await instance.stop(); + console.log(`Stopped ${instance.name} 💪`); + }, { + wait: 30 * 1000 + }); + + await instance.start(); +}; \ No newline at end of file diff --git a/src/commands/run/confluence.ts b/src/commands/run/confluence.ts index 4442225..fc87025 100644 --- a/src/commands/run/confluence.ts +++ b/src/commands/run/confluence.ts @@ -1,46 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { gracefulExit } from 'exit-hook'; -import { confluence as versions } from '../../../assets/versions.json'; -import { Confluence } from '../../applications/confluence'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { RunCommand } from './command'; -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) - .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) - .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) - .parse(process.argv) - .opts(); - - const instance = new Confluence({ - version: options.version, - database: options.database, - port: Number(options.port), - contextPath: options.contextPath, - quickReload: options.qr, - clean: options.clean, - prune: options.prune, - debug: options.debug - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - await instance.start(); -})(); +RunCommand(SupportedApplications.CONFLUENCE); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/run/jira.ts b/src/commands/run/jira.ts index 6358937..9bcfc90 100644 --- a/src/commands/run/jira.ts +++ b/src/commands/run/jira.ts @@ -1,46 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { gracefulExit } from 'exit-hook'; -import { jira as versions } from '../../../assets/versions.json'; -import { Jira } from '../../applications/jira'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { RunCommand } from './command'; -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) - .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) - .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) - .parse(process.argv) - .opts(); - - const instance = new Jira({ - version: options.version, - database: options.database, - port: Number(options.port), - contextPath: options.contextPath, - quickReload: options.qr, - clean: options.clean, - prune: options.prune, - debug: options.debug - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - await instance.start(); -})(); +RunCommand(SupportedApplications.JIRA); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/stop/bamboo.ts b/src/commands/stop/bamboo.ts index b293d8e..6d16b63 100644 --- a/src/commands/stop/bamboo.ts +++ b/src/commands/stop/bamboo.ts @@ -1,36 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { gracefulExit } from 'exit-hook'; -import { bamboo as versions } from '../../../assets/versions.json'; -import { Bamboo as Application } from '../../applications/bamboo'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { StopCommand } from './command'; -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .parse(process.argv) - .opts(); - - const instance = new Application({ - version: options.version, - database: options.database, - prune: options.prune - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - gracefulExit(); -})(); +StopCommand(SupportedApplications.BAMBOO); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/stop/bitbucket.ts b/src/commands/stop/bitbucket.ts index 40c1194..01f9ce5 100644 --- a/src/commands/stop/bitbucket.ts +++ b/src/commands/stop/bitbucket.ts @@ -1,36 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { gracefulExit } from 'exit-hook'; -import { bitbucket as versions } from '../../../assets/versions.json'; -import { Bitbucket as Application } from '../../applications/bitbucket'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { StopCommand } from './command'; -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .parse(process.argv) - .opts(); - - const instance = new Application({ - version: options.version, - database: options.database, - prune: options.prune - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - gracefulExit(); -})(); +StopCommand(SupportedApplications.BITBUCKET); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/stop/command.ts b/src/commands/stop/command.ts new file mode 100644 index 0000000..2e3d006 --- /dev/null +++ b/src/commands/stop/command.ts @@ -0,0 +1,35 @@ +#!/usr/bin/env node + +import { Option, program } from 'commander'; +import { asyncExitHook, gracefulExit } from 'exit-hook'; + +import versions from '../../../assets/versions.json'; +import { getApplicationByName } from '../../helpers/getApplication'; +import { SupportedApplications } from '../../types/SupportedApplications'; + +export const StopCommand = async (name: SupportedApplications) => { + const options = program + .showHelpAfterError(true) + .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions[name]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .parse(process.argv) + .opts(); + + const Application = getApplicationByName(name); + const instance = new Application({ + version: options.version, + database: options.database, + prune: options.prune + }); + + asyncExitHook(async () => { + console.log(`Stopping ${instance.name}... ⏳`); + await instance.stop(); + console.log(`Stopped ${instance.name} 💪`); + }, { + wait: 30 * 1000 + }); + + gracefulExit(); +}; \ No newline at end of file diff --git a/src/commands/stop/confluence.ts b/src/commands/stop/confluence.ts index d729064..8d457b4 100644 --- a/src/commands/stop/confluence.ts +++ b/src/commands/stop/confluence.ts @@ -1,36 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { gracefulExit } from 'exit-hook'; -import { confluence as versions } from '../../../assets/versions.json'; -import { Confluence as Application } from '../../applications/confluence'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { StopCommand } from './command'; -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .parse(process.argv) - .opts(); - - const instance = new Application({ - version: options.version, - database: options.database, - prune: options.prune - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - gracefulExit(); -})(); +StopCommand(SupportedApplications.CONFLUENCE); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); diff --git a/src/commands/stop/jira.ts b/src/commands/stop/jira.ts index e8bb181..53a25c5 100644 --- a/src/commands/stop/jira.ts +++ b/src/commands/stop/jira.ts @@ -1,36 +1,11 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { gracefulExit } from 'exit-hook'; -import { jira as versions } from '../../../assets/versions.json'; -import { Jira as Application } from '../../applications/jira'; +import { SupportedApplications } from '../../types/SupportedApplications'; +import { StopCommand } from './command'; -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .parse(process.argv) - .opts(); - - const instance = new Application({ - version: options.version, - database: options.database, - prune: options.prune - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - gracefulExit(); -})(); +StopCommand(SupportedApplications.JIRA); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); From e78864c0c41bb7f7be74227e9e960eb2b319372d Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Tue, 23 Apr 2024 12:23:03 +0200 Subject: [PATCH 17/18] chore: update package.json to conform to NPM spec --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f547e5..024a296 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "README", "package.json" ], - "bin": "./lib/index.js", + "bin": { + "dcdx": "./lib/index.js" + }, "engines": { "node": ">=18" }, From 3f3cbc87c8384f729f76e9bac3579ba0c4ca89c3 Mon Sep 17 00:00:00 2001 From: Remie Bolte Date: Sat, 25 May 2024 11:35:28 +0200 Subject: [PATCH 18/18] feat: complete rewrite + unit tests --- .circleci/config.yml | 4 + .gitignore | 4 +- __mocks__/exit-hook.js | 144 + assets/versions.json | 2 +- generateVersionList.mjs | 2 +- package.json | 17 +- rollup.config.js | 7 +- src/applications/bamboo.ts | 33 +- src/applications/base.ts | 91 +- src/applications/bitbucket.ts | 28 +- src/applications/confluence.ts | 27 +- src/applications/jira.ts | 33 +- src/commands/build.ts | 171 +- src/commands/database.ts | 81 +- src/commands/database/mssql.ts | 50 - src/commands/database/mysql.ts | 52 - src/commands/database/postgres.ts | 52 - src/commands/debug.ts | 122 + src/commands/reset.ts | 137 +- src/commands/reset/bamboo.ts | 13 - src/commands/reset/bitbucket.ts | 13 - src/commands/reset/command.ts | 27 - src/commands/reset/confluence.ts | 13 - src/commands/reset/jira.ts | 13 - src/commands/run.ts | 173 +- src/commands/run/bamboo.ts | 13 - src/commands/run/bitbucket.ts | 13 - src/commands/run/command.ts | 45 - src/commands/run/confluence.ts | 13 - src/commands/run/jira.ts | 13 - src/commands/start.ts | 114 - src/commands/stop.ts | 134 +- src/commands/stop/bamboo.ts | 13 - src/commands/stop/bitbucket.ts | 13 - src/commands/stop/command.ts | 35 - src/commands/stop/confluence.ts | 13 - src/commands/stop/jira.ts | 13 - src/databases/base.ts | 98 +- src/databases/mssql.ts | 28 +- src/databases/mysql.ts | 27 +- src/databases/postgres.ts | 26 +- src/helpers/ActionHandler.ts | 47 + src/helpers/FileWatcher.ts | 63 + src/helpers/amps.ts | 191 +- src/helpers/docker.ts | 40 +- src/helpers/getApplication.ts | 15 +- src/helpers/getDatabaseEngine.ts | 18 + src/helpers/getZodDefaults.ts | 20 + src/helpers/licences.ts | 4 +- .../showHelpWithDefaultCommandOptions.ts | 18 + src/helpers/showRecursiveBuildWarning.ts | 3 +- src/index.ts | 6 +- src/types/AMPS.ts | 23 + src/types/Application.ts | 46 + src/types/ApplicationOptions.ts | 14 - src/types/Database.ts | 68 + src/types/DatabaseEngine.ts | 15 - src/types/DatabaseOptions.ts | 11 - src/types/SupportedApplications.ts | 7 - src/types/SupportedDatabaseDrivers.ts | 7 - src/types/SupportedDatabaseEngines.ts | 2 - test/amps.test.js | 995 ++++++ test/build.test.js | 1277 ++++++++ test/database.test.js | 641 ++++ test/debug.test.js | 2904 +++++++++++++++++ test/fixtures/pomFiles.js | 711 ++++ test/reset.test.js | 384 +++ test/run.test.js | 1945 +++++++++++ test/stop.test.js | 384 +++ yarn.lock | 1229 ++++++- 70 files changed, 11860 insertions(+), 1148 deletions(-) create mode 100644 __mocks__/exit-hook.js delete mode 100644 src/commands/database/mssql.ts delete mode 100644 src/commands/database/mysql.ts delete mode 100644 src/commands/database/postgres.ts create mode 100644 src/commands/debug.ts delete mode 100644 src/commands/reset/bamboo.ts delete mode 100644 src/commands/reset/bitbucket.ts delete mode 100644 src/commands/reset/command.ts delete mode 100644 src/commands/reset/confluence.ts delete mode 100644 src/commands/reset/jira.ts delete mode 100644 src/commands/run/bamboo.ts delete mode 100644 src/commands/run/bitbucket.ts delete mode 100644 src/commands/run/command.ts delete mode 100644 src/commands/run/confluence.ts delete mode 100644 src/commands/run/jira.ts delete mode 100644 src/commands/start.ts delete mode 100644 src/commands/stop/bamboo.ts delete mode 100644 src/commands/stop/bitbucket.ts delete mode 100644 src/commands/stop/command.ts delete mode 100644 src/commands/stop/confluence.ts delete mode 100644 src/commands/stop/jira.ts create mode 100644 src/helpers/ActionHandler.ts create mode 100644 src/helpers/FileWatcher.ts create mode 100644 src/helpers/getDatabaseEngine.ts create mode 100644 src/helpers/getZodDefaults.ts create mode 100644 src/helpers/showHelpWithDefaultCommandOptions.ts create mode 100644 src/types/AMPS.ts create mode 100644 src/types/Application.ts delete mode 100644 src/types/ApplicationOptions.ts create mode 100644 src/types/Database.ts delete mode 100644 src/types/DatabaseEngine.ts delete mode 100644 src/types/DatabaseOptions.ts delete mode 100644 src/types/SupportedApplications.ts delete mode 100644 src/types/SupportedDatabaseDrivers.ts delete mode 100644 src/types/SupportedDatabaseEngines.ts create mode 100644 test/amps.test.js create mode 100644 test/build.test.js create mode 100644 test/database.test.js create mode 100644 test/debug.test.js create mode 100644 test/fixtures/pomFiles.js create mode 100644 test/reset.test.js create mode 100644 test/run.test.js create mode 100644 test/stop.test.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 64cdb06..4dd1f0d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,6 +20,10 @@ jobs: paths: - node_modules + - run: + name: Test + command: yarn test + - run: name: Publish command: npx semantic-release diff --git a/.gitignore b/.gitignore index 4b1fe44..27acf00 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .yarn/install-state.gz dist/ lib/ -node_modules \ No newline at end of file +coverage/ +node_modules +.DS_Store diff --git a/__mocks__/exit-hook.js b/__mocks__/exit-hook.js new file mode 100644 index 0000000..93d6741 --- /dev/null +++ b/__mocks__/exit-hook.js @@ -0,0 +1,144 @@ + /* eslint-disable */ + +/* + THIS IS A VERBATIM COPY OF THE EXIT HOOK IMPLEMENTATION + The only change in this mock is that the `isCalled` and `isRegistered` variables are reset + For some reason, vi.resetModules() does not clear exit-hook and these values do not get reset between tests + If the `isCalled` variable is not reset, exit-hook will not process gracefullExit again + Because we depend on gracefullExit to capture error messages and convey them to Commander.JS, this breaks tests +*/ + +import process from 'node:process'; + +const asyncCallbacks = new Set(); +const callbacks = new Set(); + +let isCalled = false; +let isRegistered = false; + +async function exit(shouldManuallyExit, isSynchronous, signal) { + if (isCalled) { + return; + } + + isCalled = true; + + if (asyncCallbacks.size > 0 && isSynchronous) { + console.error([ + 'SYNCHRONOUS TERMINATION NOTICE:', + 'When explicitly exiting the process via process.exit or via a parent process,', + 'asynchronous tasks in your exitHooks will not run. Either remove these tasks,', + 'use gracefulExit() instead of process.exit(), or ensure your parent process', + 'sends a SIGINT to the process running this code.', + ].join(' ')); + } + + const exitCode = 128 + signal; + + const done = (force = false) => { + if (force === true || shouldManuallyExit === true) { + isCalled = false; + isRegistered = false; + process.exit(exitCode); // eslint-disable-line unicorn/no-process-exit + } + }; + + for (const callback of callbacks) { + callback(exitCode); + } + + if (isSynchronous) { + done(); + return; + } + + const promises = []; + let forceAfter = 0; + for (const [callback, wait] of asyncCallbacks) { + forceAfter = Math.max(forceAfter, wait); + promises.push(Promise.resolve(callback(exitCode))); + } + + // Force exit if we exceeded our wait value + const asyncTimer = setTimeout(() => { + done(true); + }, forceAfter); + + await Promise.all(promises); + clearTimeout(asyncTimer); + done(); +} + +function addHook(options) { + const {onExit, wait, isSynchronous} = options; + const asyncCallbackConfig = [onExit, wait]; + + if (isSynchronous) { + callbacks.add(onExit); + } else { + asyncCallbacks.add(asyncCallbackConfig); + } + + if (!isRegistered) { + isRegistered = true; + + // Exit cases that support asynchronous handling + process.once('beforeExit', exit.bind(undefined, true, false, -128)); + process.once('SIGINT', exit.bind(undefined, true, false, 2)); + process.once('SIGTERM', exit.bind(undefined, true, false, 15)); + + // Explicit exit events. Calling will force an immediate exit and run all + // synchronous hooks. Explicit exits must not extend the node process + // artificially. Will log errors if asynchronous calls exist. + process.once('exit', exit.bind(undefined, false, true, 0)); + + // PM2 Cluster shutdown message. Caught to support async handlers with pm2, + // needed because explicitly calling process.exit() doesn't trigger the + // beforeExit event, and the exit event cannot support async handlers, + // since the event loop is never called after it. + process.on('message', message => { + if (message === 'shutdown') { + exit(true, true, -128); + } + }); + } + + return () => { + if (isSynchronous) { + callbacks.delete(onExit); + } else { + asyncCallbacks.delete(asyncCallbackConfig); + } + }; +} + +export default function exitHook(onExit) { + if (typeof onExit !== 'function') { + throw new TypeError('onExit must be a function'); + } + + return addHook({ + onExit, + isSynchronous: true, + }); +} + +export function asyncExitHook(onExit, options = {}) { + if (typeof onExit !== 'function') { + throw new TypeError('onExit must be a function'); + } + + if (!(typeof options.wait === 'number' && options.wait > 0)) { + throw new TypeError('wait must be set to a positive numeric value'); + } + + return addHook({ + onExit, + wait: options.wait, + isSynchronous: false, + }); +} + +export function gracefulExit(signal = 0) { + exit(true, false, -128 + signal); +} diff --git a/assets/versions.json b/assets/versions.json index f24bd14..8dada16 100644 --- a/assets/versions.json +++ b/assets/versions.json @@ -1 +1 @@ -{"jira":["latest","9.8.0-ubuntu-jdk11","9.8.0-jdk11","9.8.0","9.5-ubuntu-jdk11","9.5-jdk11","9.5.1-ubuntu-jdk11","9.5.1-jdk11","9.5.1","9.5","9.14.0-ubuntu-jdk11","9.14.0-jdk11","9.14-ubuntu-jdk11","9.14.0","9.14-jdk11","9.14.1-ubuntu-jdk11","9.14","9.14.1","9.12.5-ubuntu-jdk11","9.12.5-jdk11","9.12.5","9.0-ubuntu-jdk11","9.0-jdk11","9.0.0-ubuntu-jdk11","9.0.0-jdk11","9.0.0","9.0","9.8.1-ubuntu-jdk11","9.8.1-jdk11","9.8.1","9.14.1-jdk11","9.10-ubuntu-jdk11","9.10-jdk11","9.10.2-ubuntu-jdk11","9.10.2-jdk11","9.10.2","9.8","9.10","9.12.6-jdk11","9.12.6-ubuntu-jdk11","9.12.6","9.8-ubuntu-jdk11","9.8-jdk11","9.8.2-ubuntu-jdk11","9.8.2-jdk11","9.8.2","9.6-ubuntu-jdk11","9.6-jdk11","9.6.0-ubuntu-jdk11","9.6.0-jdk11","9.6.0","9.6","9.4.2-ubuntu-jdk11","9.4.2-jdk11","9.4.2","9.3.0-ubuntu-jdk11","9.3.0-jdk11","9.3.0","9.14.0-ubi9-jdk17","9.14.0-ubi9","9.12.7-ubi9-jdk17","9.12.7-ubi9","9.5.0-ubi9-jdk17","9.5.0-ubi9","9.12-ubuntu-jdk11","9.12-jdk11","9.12.7-ubuntu-jdk11","9.12.7-jdk11","9.12.7","9.12","9.1.0-ubuntu-jdk11","9.1.0-jdk11","ubuntu-jdk11","9.1.0","jdk11","9-ubuntu-jdk11","9-jdk11","9.4-ubuntu-jdk11","9.4-jdk11","9.4.20-ubuntu-jdk11","9.4.20-jdk11","9.4.20","9.4","9.3.1-ubuntu-jdk11","9.3.1-jdk11","9.3.1","9.15-ubuntu-jdk11","9.15-jdk11","9.15.0-ubuntu-jdk11","9.15.0-jdk11","9.15.0","9.15","9.12.0-ubuntu-jdk11","9.12.0-jdk11","9.12.0","9.11.0-ubuntu-jdk11","9.11.0-jdk11","9.11.0","9","9.8.0-ubi9-jdk17","9.8.0-ubi9","9.7.0-ubi9-jdk17","9.7.0-ubi9","9.5.1-ubi9-jdk17","9.5.1-ubi9","9.1-ubuntu-jdk11","9.1-jdk11","9.14.1-ubi9-jdk17","9.14.1-ubi9","9.1.1-ubuntu-jdk11","9.1.1-jdk11","9.1.1","9.9.0-ubuntu-jdk11","9.1","9.9.0-ubi9-jdk17","9.9.0-ubi9","9.9.0-jdk11","9.9.0","9.7.0-ubuntu-jdk11","9.7.0-jdk11","9.7.0","9.4.6-ubuntu-jdk11","9.4.6-jdk11","9.4.6","9.4.1-ubuntu-jdk11","9.4.1-jdk11","9.4.15-ubuntu-jdk11","9.4.15-jdk11","9.4.15","9.4.1","9.3.2-ubuntu-jdk11","9.3.2-jdk11","9.3.2","9.12.1-ubuntu-jdk11","9.12.1-jdk11","9.12.1","9.11.1-ubuntu-jdk11","9.11.1-jdk11","9.11.1","9.8.1-ubi9-jdk17","9.8.1-ubi9","9.7.1-ubi9-jdk17","9.7.1-ubi9","9.4.10-ubuntu-jdk11","9.4.10-jdk11","9.4.10","9.13.0-ubuntu-jdk11","9.13.0-ubi9-jdk17","9.13.0-ubi9","9.13.0-jdk11","9.13.0","9.11.3-ubi9-jdk17","9.11.3-ubi9","9.10.2-ubi9-jdk17","9.10.2-ubi9","9.9.1-ubuntu-jdk11","9.9.1-ubi9-jdk17","9.9.1-ubi9","9.9.1-jdk11","9.9.1","9.7.1-ubuntu-jdk11","9.7.1-jdk11","9.7.1","9.4.7-ubuntu-jdk11","9.4.7-jdk11","9.4.7","9.4.16-ubuntu-jdk11","9.4.16-jdk11","9.4.16","9.3-ubuntu-jdk11","9.3-jdk11","9.3.3-ubuntu-jdk11","9.3.3-jdk11","9.3.3","9.3","9.2.0-ubuntu-jdk11","9.2.0-jdk11","9.2.0","9.12.4-ubi9-jdk17","9.12.4-ubi9","9.12.1-ubi9-jdk17","9.12.1-ubi9","9.11.2-ubuntu-jdk11","9.11.2-jdk11","9.11.2","ubi9-jdk17","ubi9","9.8.2-ubi9-jdk17","9.8.2-ubi9","9.7.2-ubi9-jdk17","9.7.2-ubi9","9.6.0-ubi9-jdk17","9.6.0-ubi9","9.4.8-ubuntu-jdk11","9.4.8-jdk11","9.4.8","9.4.3-ubuntu-jdk11","9.4.3-jdk11","9.4.3","9.4.11-ubuntu-jdk11","9.4.11-jdk11","9.4.11","9.15.0-ubi9-jdk17","9.15.0-ubi9","9.13-ubuntu-jdk11","9.13-jdk11","9.13.1-ubuntu-jdk11","9.13.1-ubi9-jdk17","9.13.1-ubi9","9.13.1-jdk11","9.13.1","9.13","9.12.2-ubuntu-jdk11","9.12.2-jdk11","9.12.2","9.9-ubuntu-jdk11","9.9-jdk11","9.9.2-ubuntu-jdk11","9.9.2-ubi9-jdk17","9.9.2-ubi9","9.9.2-jdk11","9.9.2","9.9","9.7-ubuntu-jdk11","9.7-jdk11","9.7.2-ubuntu-jdk11","9.7.2-jdk11","9.7.2","9.7","9.4.17-ubuntu-jdk11","9.4.17-jdk11","9.4.17","9.2-ubuntu-jdk11","9.2-jdk11","9.2.1-ubuntu-jdk11","9.2.1-jdk11","9.2.1","9.2","9.12.5-ubi9-jdk17","9.12.5-ubi9","9.11-ubuntu-jdk11","9.11-jdk11","9.11.3-ubuntu-jdk11","9.11.3-jdk11","9.11.3","9.11.1-ubi9-jdk17","9.11.1-ubi9","9.11","9.10.0-ubuntu-jdk11","9.10.0-ubi9-jdk17","9.10.0-ubi9","9.10.0-jdk11","9.10.0","9.4.9-ubuntu-jdk11","9.4.9-jdk11","9.4.9","9.4.4-ubuntu-jdk11","9.4.4-jdk11","9.4.4","9.4.12-ubuntu-jdk11","9.4.12-jdk11","9.4.12","9.12.3-ubuntu-jdk11","9.12.3-jdk11","9.12.3","9.12.2-ubi9-jdk17","9.12.2-ubi9","9.4.18-ubuntu-jdk11","9.4.18-jdk11","9.4.18","9.5.0-ubuntu-jdk11","9.5.0-jdk11","9.5.0","9.4.5-ubuntu-jdk11","9.4.5-jdk11","9.4.5","9.4.19-ubuntu-jdk11","9.4.19-jdk11","9.4.19","9.4.14-ubuntu-jdk11","9.4.14-jdk11","9.4.14","9.4.0-ubuntu-jdk11","9.4.0-jdk11","9.4.0","9.12.6-ubi9-jdk17","9.12.6-ubi9","9.12.4-ubuntu-jdk11","9.12.4-jdk11","9.12.4","9.12.3-ubi9-jdk17","9.12.3-ubi9","9.12.0-ubi9-jdk17","9.12.0-ubi9","9.11.2-ubi9-jdk17","9.11.2-ubi9","9.11.0-ubi9-jdk17","9.11.0-ubi9","9.10.1-ubuntu-jdk11","9.10.1-ubi9-jdk17","9.10.1-ubi9","9.10.1-jdk11","9.10.1","10.0.0-EAP02-ubi9-jdk17","10.0.0-EAP02","10.0.0-EAP02-ubi9","10.0.0-EAP02-ubuntu-jdk11","10.0.0-EAP02-jdk11","9.15.0-RC","9.15.0-RC-jdk11","9.15.0-RC-ubuntu-jdk11","9.15.0-RC-ubi9","9.15.0-RC-ubi9-jdk17","9.15.0-EAP02-ubi9-jdk17","9.15.0-EAP02-ubi9","9.15.0-EAP02-jdk11","9.15.0-EAP02-ubuntu-jdk11","9.15.0-EAP02","8.22.3-ubuntu-jdk11","8.22.3-jdk11","8.22.3","8.22.4-ubuntu-jdk11","8.22.4-jdk11","8.22.4","8.22.5-ubuntu-jdk11","8.22.5","8.22.5-jdk11","8.16-ubuntu-jdk11","8.16-jdk11","8.16.2-ubuntu-jdk11","8.16.2-jdk11","8.16.2","8.16","8-ubuntu-jdk11","8-jdk11","8.22-ubuntu-jdk11","8.22-jdk11","8.22.6-ubuntu-jdk11","8.22.6-jdk11","8.22.6","8.22","8","8.17.0-ubuntu-jdk11","8.17.0-jdk11","8.17.0","8.20.21-ubuntu-jdk11","8.20.21-jdk11","8.20.21","8.20.4-ubuntu-jdk11","8.20.4-jdk11","8.20.4","8.17-ubuntu-jdk11","8.17-jdk11","8.17.1-ubuntu-jdk11","8.17.1-jdk11","8.17.1","8.17","8.20.22-ubuntu-jdk11","8.20.22-jdk11","8.20.22","8.13.5-ubuntu-jdk11","8.13.5-jdk11","8.13.5","8.20.5-ubuntu-jdk11","8.20.5-jdk11","8.20.5","8.13.19-ubuntu-jdk11","8.13.19-jdk11","8.13.19","8.13.6-jdk11","8.20.23-ubuntu-jdk11","8.20.23-jdk11","8.20.23","8.20.11-ubuntu-jdk11","8.20.11-jdk11","8.20.11","8.13.6-ubuntu-jdk11","8.13.6","8.13.2-ubuntu-jdk11","8.13.2-jdk11","8.13.2","8.20.6-ubuntu-jdk11","8.20.6-jdk11","8.20.6","8.18.0-ubuntu-jdk11","8.18.0-jdk11","8.18.0","8.13.0-ubuntu-jdk11","8.13.0-jdk11","8.13.0","8.20.24-ubuntu-jdk11","8.20.24-jdk11","8.20.24","8.20.12-ubuntu-jdk11","8.20.12-jdk11","8.20.12","8.13.7-ubuntu-jdk11","8.13.7-jdk11","8.20.7-ubuntu-jdk11","8.13.7","8.20.7-jdk11","8.20.7","8.18.1-ubuntu-jdk11","8.18.1-jdk11","8.18.1","8.13.20-ubuntu-jdk11","8.13.20-jdk11","8.13.20","8.13.1-ubuntu-jdk11","8.13.1-jdk11","8.13.1","8.13.8-ubuntu-jdk11","8.13.8-jdk11","8.13.8","8.20.25-ubuntu-jdk11","8.20.25-jdk11","8.20.25","8.20.13-ubuntu-jdk11","8.20.13-jdk11","8.20.13","8.13.21-ubuntu-jdk11","8.13.21-jdk11","8.13.21","8.13.10-ubuntu-jdk11","8.13.10-jdk11","8.20.8-ubuntu-jdk11","8.13.10","8.20.8-jdk11","8.20.8","8.18-ubuntu-jdk11","8.18-jdk11","8.18.2-ubuntu-jdk11","8.18.2-jdk11","8.18.2","8.18","8.13.9-ubuntu-jdk11","8.13.9-jdk11","8.13.9","8.20.26-ubuntu-jdk11","8.20.26-jdk11","8.20.26","8.20.14-ubuntu-jdk11","8.20.14-jdk11","8.20.14","8.13.22-ubuntu-jdk11","8.13.22-jdk11","8.13.22","8.20.9-ubuntu-jdk11","8.20.9-jdk11","8.20.9","8.14.0-ubuntu-jdk11","8.14.0-jdk11","8.14.0","8.13.11-ubuntu-jdk11","8.13.11-jdk11","8.13.11","8.20.15-ubuntu-jdk11","8.20.15-jdk11","8.20.15","8.13.24-ubuntu-jdk11","8.13.24-jdk11","8.13.24","8.20.27-ubuntu-jdk11","8.20.27-jdk11","8.20.27","8.19.0-ubuntu-jdk11","8.19.0-jdk11","8.19.0","8.14-ubuntu-jdk11","8.14-jdk11","8.14.1-ubuntu-jdk11","8.14.1-jdk11","8.14.1","8.14","8.13.12-ubuntu-jdk11","8.13.12-jdk11","8.13.12","8.21.0-ubuntu-jdk11","8.21.0-jdk11","8.21.0","8.20.28-ubuntu-jdk11","8.20.28-jdk11","8.20.28","8.20.16-ubuntu-jdk11","8.20.16-jdk11","8.20.16","8.13.25-ubuntu-jdk11","8.13.25-jdk11","8.13.25","8.13.13-ubuntu-jdk11","8.13.13-jdk11","8.13.13","8.21-ubuntu-jdk11","8.21-jdk11","8.21.1-ubuntu-jdk11","8.21.1-jdk11","8.21.1","8.21","8.19-ubuntu-jdk11","8.19-jdk11","8.19.1-ubuntu-jdk11","8.19.1-jdk11","8.19.1","8.19","8.20.17-ubuntu-jdk11","8.20.17-jdk11","8.20.17","8.15.0-ubuntu-jdk11","8.15.0-jdk11","8.15.0","8.13.26-ubuntu-jdk11","8.13.26-jdk11","8.13.26","8.13.14-ubuntu-jdk11","8.13.14-jdk11","8.20.29-ubuntu-jdk11","8.20.29-jdk11","8.20.29","8.13.14","8.13-ubuntu-jdk11","8.13-jdk11","8.13.27-ubuntu-jdk11","8.13.27-jdk11","8.13.27","8.13","8.20.3-ubuntu-jdk11","8.20.3-jdk11","8.20.3","8.20.18-ubuntu-jdk11","8.20.18-jdk11","8.20.18","8.15-ubuntu-jdk11","8.15-jdk11","8.15.1-ubuntu-jdk11","8.15.1-jdk11","8.15.1","8.15","8.13.15-ubuntu-jdk11","8.13.15-jdk11","8.13.15","8.22.0-ubuntu-jdk11","8.22.0-jdk11","8.22.0","8.20.19-ubuntu-jdk11","8.20.19-jdk11","8.20.19","8.20.0-ubuntu-jdk11","8.20.0-jdk11","8.20.0","8.13.16-ubuntu-jdk11","8.13.16-jdk11","8.20-ubuntu-jdk11","8.13.16","8.20-jdk11","8.20.30-ubuntu-jdk11","8.20.30-jdk11","8.20.30","8.20","8.22.1-ubuntu-jdk11","8.22.1-jdk11","8.22.1","8.20.2-ubuntu-jdk11","8.20.2-jdk11","8.20.2","8.20.1-ubuntu-jdk11","8.20.1-jdk11","8.20.1","8.13.17-ubuntu-jdk11","8.13.17-jdk11","8.13.17","8.16.0-ubuntu-jdk11","8.16.0-jdk11","8.16.0","8.13.3-ubuntu-jdk11","8.13.3-jdk11","8.13.3","8.22.2-ubuntu-jdk11","8.22.2-jdk11","8.22.2","8.20.20-ubuntu-jdk11","8.20.20-jdk11","8.20.20","8.20.10-ubuntu-jdk11","8.20.10-jdk11","8.20.10","8.13.4-ubuntu-jdk11","8.13.4-jdk11","8.13.4","8.13.18-ubuntu-jdk11","8.13.18-jdk11","8.13.18","8.16.1-ubuntu-jdk11","8.16.1-jdk11","8.16.1","9.15.0-EAP01-ubuntu-jdk11","9.15.0-EAP01","9.15.0-EAP01-jdk11","10.0.0-EAP01-jdk11","10.0.0-EAP01-ubuntu-jdk11","10.0.0-EAP01","9.14.0-RC-ubuntu-jdk11","9.14.0-RC-jdk11","9.14.0-RC","9.14.0-EAP02","9.14.0-EAP02-ubuntu-jdk11","9.14.0-EAP02-jdk11","9.14.0-EAP01-ubuntu-jdk11","9.14.0-EAP01-jdk11","9.14.0-EAP01","9.13.0-EAP02","9.13.0-EAP02-ubuntu-jdk11","9.13.0-EAP02-jdk11","9.4.13-jdk11","9.4.13","9.4.13-ubuntu-jdk11","9.13.0-EAP01-ubuntu-jdk11","9.13.0-EAP01-jdk11","9.13.0-EAP01","8.11.0-ubuntu-jdk11","8.11.0-jdk11","8.11.0","8.11-ubuntu-jdk11","8.11-jdk11","8.11.1-ubuntu-jdk11","8.11.1-jdk11","8.11.1","8.11","8.12.0-ubuntu-jdk11","8.12.0-jdk11","8.12.0","8.12.1-ubuntu-jdk11","8.12.1-jdk11","8.12.1","8.12.2-ubuntu-jdk11","8.12.2-jdk11","8.12.2","8.12-ubuntu-jdk11","8.12-jdk11","8.12.3-ubuntu-jdk11","8.12.3-jdk11","8.12.3","8.12","9.12.0-EAP01-ubuntu-jdk11","9.12.0-EAP01-jdk11","9.12.0-EAP01","9.11.0-RC-jdk11","9.11.0-RC-ubuntu-jdk11","9.11.0-RC","9.11.0-EAP02-ubuntu-jdk11","9.11.0-EAP02-jdk11","9.11.0-EAP02","9.11.0-EAP01-ubuntu-jdk11","9.11.0-EAP01-jdk11","9.11.0-EAP01","9.10.0-RC-ubuntu-jdk11","9.10.0-RC-jdk11","9.10.0-RC","9.10.0-EAP02-ubuntu-jdk11","9.10.0-EAP02-jdk11","9.10.0-EAP02","9.10.0-EAP01-ubuntu-jdk11","9.10.0-EAP01","9.10.0-EAP01-jdk11","9.9.0-RC02-jdk11","9.9.0-RC02-ubuntu-jdk11","9.9.0-RC02","9.9.0-RC01-jdk11","9.9.0-RC01-ubuntu-jdk11","9.9.0-RC01","9.9.0-EAP02-ubuntu-jdk11","9.9.0-EAP02-jdk11","9.9.0-EAP02","9.9.0-EAP01-jdk11","9.9.0-EAP01-ubuntu-jdk11","9.9.0-EAP01","9.8.0-RC01-jdk11","9.8.0-RC01-ubuntu-jdk11","9.8.0-RC01","9.8.0-EAP02","eap-jdk11","eap","9.8.0-EAP02-ubuntu-jdk11","eap-ubuntu-jdk11","9.8.0-EAP02-jdk11","9.8.0-EAP01","9.8.0-EAP01-jdk11","9.8.0-EAP01-ubuntu-jdk11","9.7.0-RC01-jdk11","9.7.0-RC01","9.7.0-RC01-ubuntu-jdk11","9.7.0-EAP02","9.7.0-EAP02-ubuntu-jdk11","9.7.0-EAP02-jdk11","9.7.0-EAP01-ubuntu-jdk11","9.7.0-EAP01-jdk11","9.7.0-EAP01","9.6.0-RC01","9.6.0-RC01-ubuntu-jdk11","9.6.0-EAP02-ubuntu-jdk11","9.6.0-EAP02","9.6.0-EAP02-jdk11","9.6.0-EAP01","9.6.0-EAP01-jdk11","9.6.0-EAP01-ubuntu-jdk11","9.5.0-RC01","9.5.0-RC01-ubuntu-jdk11","9.5.0-RC01-jdk11","9.5.0-EAP03-ubuntu-jdk11","9.5.0-EAP03-jdk11","9.5.0-EAP03","9.5.0-EAP02-ubuntu-jdk11","9.5.0-EAP02","9.5.0-EAP02-jdk11","9.4.0-RC02-jdk11","9.4.0-RC02","9.4.0-RC02-ubuntu-jdk11","9.4.0-RC01-ubuntu-jdk11","9.4.0-RC01-jdk11","9.4.0-RC01","9.4.0-EAP01","9.4.0-EAP01-ubuntu-jdk11","9.4.0-EAP01-jdk11","9.5.0-EAP01-ubuntu-jdk11","9.5.0-EAP01","9.5.0-EAP01-jdk11","9.3.0-RC01","9.3.0-RC01-ubuntu-jdk11","9.3.0-RC01-jdk11","9.3.0-EAP02-jdk11","9.3.0-EAP02","9.3.0-EAP02-ubuntu-jdk11","9.3.0-EAP01-jdk11","9.3.0-EAP01-ubuntu-jdk11","9.3.0-EAP01","9.2.0-RC01-ubuntu-jdk11","9.2.0-RC01-jdk11","9.2.0-RC01","9.2.0-EAP03-jdk11","9.2.0-EAP03-ubuntu-jdk11","9.2.0-EAP03","9.2.0-EAP02-ubuntu-jdk11","9.2.0-EAP02-jdk11","9.2.0-EAP02","9.2.0-EAP01-jdk11","9.2.0-EAP01-ubuntu-jdk11","9.2.0-EAP01","9.1.0-RC01-ubuntu-jdk11","9.1.0-RC01-jdk11","9.1.0-RC01","9.1.0-EAP02-ubuntu-jdk11","9.1.0-EAP02-jdk11","9.1.0-EAP02","9.1.0-EAP01-jdk11","9.1.0-EAP01-ubuntu-jdk11","9.1.0-EAP01","8.8.0-ubuntu-jdk11","8.8.0-jdk11","8.8.0","8.9-ubuntu-jdk11","8.9-jdk11","8.9.1-ubuntu-jdk11","8.9.1-jdk11","8.9.1","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.9.0","8.9","8.8-ubuntu-jdk11","8.8-jdk11","8.8.1-ubuntu-jdk11","8.8.1-jdk11","8.8.1","8.8","8.10-ubuntu-jdk11","8.10-jdk11","8.10.1-ubuntu-jdk11","8.10.1-jdk11","8.10.1","8.10.0-ubuntu-jdk11","8.10.0-jdk11","8.10.0","8.10","9.0.0-RC02-jdk11","9.0.0-RC02-ubuntu-jdk11","9.0.0-RC02","9.0.0-RC01-jdk11","9.0.0-RC01-ubuntu-jdk11","9.0.0-RC01","9.0.0-EAP04-ubuntu-jdk11","9.0.0-EAP04-jdk11","9.0.0-EAP04","9.0.0-EAP03-ubuntu-jdk11","9.0.0-EAP03-jdk11","9.0.0-EAP03","9.0.0-EAP02-ubuntu-jdk11","9.0.0-EAP02-jdk11","9.0.0-EAP02","9.0.0-EAP01-ubuntu-jdk11","9.0.0-EAP01-jdk11","9.0.0-EAP01","8.22.0-RC01-ubuntu-jdk11","8.22.0-RC01-jdk11","8.22.0-RC01","8.22.0-EAP02-ubuntu-jdk11","8.22.0-EAP02-jdk11","8.22.0-EAP02","8.22.0-EAP01-ubuntu-jdk11","8.22.0-EAP01-jdk11","8.22.0-EAP01","8.21.0-RC01-ubuntu-jdk11","8.21.0-RC01-jdk11","8.21.0-RC01","8.21.0-EAP02-ubuntu-jdk11","8.21.0-EAP02-jdk11","8.21.0-EAP02","8.21.0-EAP01-ubuntu-jdk11","8.21.0-EAP01-jdk11","8.21.0-EAP01","8.20-ubuntu-jdk-11","8.20.7-ubuntu-jdk-11","8.20.0-RC01-ubuntu-jdk11","8.20.0-RC01-jdk11","8.20.0-RC01","8.20.0-EAP01-ubuntu-jdk11","8.20.0-EAP01-jdk11","8.20.0-EAP01","8.19.0-RC01-ubuntu-jdk11","8.19.0-RC01-jdk11","8.19.0-RC01","8.19.0-EAP02-ubuntu-jdk11","8.19.0-EAP02-jdk11","8.19.0-EAP02","8.19.0-EAP01-ubuntu-jdk-11","8.19.0-EAP01-jdk11","8.19.0-EAP01","8.18.0-RC01-ubuntu-jdk11","8.18.0-RC01-jdk11","8.18.0-RC01","8.18.0-EAP02-ubuntu-jdk-11","8.18.0-EAP02-jdk11","8.18.0-EAP02","8.18.0-EAP01-ubuntu-jdk11","8.18.0-EAP01-jdk11","8.18.0-EAP01","8.17.0-RC02-ubuntu-jdk11","8.17.0-RC02-jdk11","8.17.0-RC02","8.17.0-RC01-ubuntu-jdk11","8.17.0-RC01-jdk11","8.17.0-RC01","8.17.0-EAP02-ubuntu-jdk11","8.17.0-EAP02-jdk11","8.17.0-EAP02","8.17.0-EAP01-ubuntu-jdk11","8.17.0-EAP01-jdk11","8.17.0-EAP01","8.8.0-ubuntu-jdk-11","8.8.1-ubuntu-jdk-11","8.8-ubuntu-jdk-11","8.7-jdk11","8.7-ubuntu-jdk11","8.7","8.7.1-jdk11","8.7.1","8.7.1-ubuntu-jdk11","8.7.0-jdk11","8.7.0-ubuntu-jdk11","8.7.0","8.5.18","8.5.18-jdk11","8.5.18-ubuntu-jdk11","8.5.19-jdk11","8.5.19-ubuntu-jdk11","8.5-jdk11","8.5-ubuntu-jdk11","8.5","8.5.19","8.5.2-ubuntu-jdk11","8.5.2","8.5.2-jdk11","8.5.3","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.5.4-ubuntu-jdk11","8.5.4","8.5.4-jdk11","8.5.5-jdk11","8.5.5-ubuntu-jdk11","8.5.5","8.5.6-ubuntu-jdk11","8.5.6","8.5.6-jdk11","8.5.7-ubuntu-jdk11","8.5.7","8.5.12-ubuntu-jdk11","8.5.7-jdk11","8.5.12","8.5.12-jdk11","8.5.8","8.5.8-jdk11","8.5.8-ubuntu-jdk11","8.5.13-jdk11","8.5.13","8.5.13-ubuntu-jdk11","8.5.9-jdk11","8.5.9","8.5.9-ubuntu-jdk11","8.5.14-jdk11","8.5.0-ubuntu-jdk11","8.5.14","8.5.0","8.5.14-ubuntu-jdk11","8.5.0-jdk11","8.6.0-jdk11","8.6.0","8.6.0-ubuntu-jdk11","8.5.15-jdk11","8.5.1","8.5.15","8.5.1-ubuntu-jdk11","8.5.15-ubuntu-jdk11","8.5.1-jdk11","8.6","8.6-ubuntu-jdk11","8.6.1-ubuntu-jdk11","8.6.1","8.5.16","8.5.10-ubuntu-jdk11","8.6.1-jdk11","8.5.16-ubuntu-jdk11","8.5.10","8.6-jdk11","8.5.16-jdk11","8.5.10-jdk11","8.5.17","8.5.11","8.5.17-ubuntu-jdk11","8.5.11-jdk11","8.5.17-jdk11","8.5.11-ubuntu-jdk11","8.14.0-jdk8","8.14.0-ubuntu-jdk8","8.14.0-ubuntu","8.14.1-jdk8","8.14-jdk8","8.14.1-ubuntu","8.14.1-ubuntu-jdk8","8.14-ubuntu","8.14-ubuntu-jdk8","8.15.0-ubuntu","8.15.0-jdk8","8.15.0-ubuntu-jdk8","8.15-ubuntu","8.15.1-ubuntu","8.15-ubuntu-jdk8","8.15.1-ubuntu-jdk8","8.15.1-jdk8","8.15-jdk8","8.16.0-ubuntu","8.16.0-ubuntu-jdk8","8.16.0-jdk8","8.18.1-jdk8","8.18.1-ubuntu","8.7.1-ubuntu-jdk8","8.18.1-ubuntu-jdk8","8.7-jdk8","8.16.1-jdk8","8.16.1-ubuntu-jdk8","8.7.1-ubuntu","8.16.1-ubuntu","8.7.1-jdk8","8.7-ubuntu","8.18.2-ubuntu","8.7-ubuntu-jdk8","8.18-ubuntu","8.18-jdk8","8.16-ubuntu","8.18.2-jdk8","8.18-ubuntu-jdk8","8.16-ubuntu-jdk8","8.10.0-ubuntu-jdk8","8.18.2-ubuntu-jdk8","8.10.0-ubuntu","8.16.2-jdk8","8.13.1-ubuntu","8.10.0-jdk8","8.16-jdk8","8.13.1-ubuntu-jdk8","8.16.2-ubuntu","8.12.0-ubuntu-jdk8","8.12.0-ubuntu","8.16.2-ubuntu-jdk8","8.8.0-ubuntu","8.8.0-jdk8","8.5.8-jdk8","8.5.15-ubuntu","8.10.1-ubuntu","8.5.8-ubuntu-jdk8","8.5.15-ubuntu-jdk8","8.8.0-ubuntu-jdk8","8.12.0-jdk8","8.5.15-jdk8","8.13.1-jdk8","8.5.8-ubuntu","8.10.1-ubuntu-jdk8","8.19-ubuntu-jdk8","8.10-ubuntu-jdk8","8.5.16-jdk8","8.12.1-ubuntu-jdk8","8.8.1-ubuntu-jdk8","8.10.1-jdk8","8.13-jdk8","8.12.1-ubuntu","8.8-ubuntu","8.10-ubuntu","8.13.10-jdk8","8.5.16-ubuntu-jdk8","8.5.9-ubuntu-jdk8","8.12.1-jdk8","8.8.1-ubuntu","8.13.10-ubuntu-jdk8","8.19.0-ubuntu-jdk8","8.5.16-ubuntu","8.5.9-jdk8","8.17.0-ubuntu-jdk8","8.10-jdk8","8.13-ubuntu","8-ubuntu-jdk8","8.5.9-ubuntu","8.8-jdk8","jdk8","8.8.1-jdk8","8.19-ubuntu","8.17.0-ubuntu","8.8-ubuntu-jdk8","8.5.3-ubuntu-jdk8","8.13.5-ubuntu-jdk8","8.19-jdk8","8.5.10-jdk8","8.17.0-jdk8","8.5.17-jdk8","8.5.3-jdk8","8.13.5-ubuntu","8.12.2-ubuntu","8.5.10-ubuntu","8.19.0-ubuntu","8.13-ubuntu-jdk8","8.6.0-ubuntu-jdk8","8.5.17-ubuntu","8.13.5-jdk8","8.12.2-ubuntu-jdk8","8.5.10-ubuntu-jdk8","8.6.0-ubuntu","8.5.17-ubuntu-jdk8","8.5.3-ubuntu","ubuntu-jdk8","8.12.2-jdk8","8.19.0-jdk8","8.17-ubuntu","8.6.0-jdk8","8-jdk8","8.17.1-jdk8","8.13.6-jdk8","ubuntu","8.11.0-ubuntu","8.5.11-jdk8","8-ubuntu","8.5.4-jdk8","8.5.18-ubuntu-jdk8","8.17.1-ubuntu","8.12.3-ubuntu","8.11.0-jdk8","8.13.6-ubuntu-jdk8","8.5.11-ubuntu","8.5.4-ubuntu","8.17.1-ubuntu-jdk8","8.6.1-jdk8","8.5.18-jdk8","8.13.6-ubuntu","8.5.11-ubuntu-jdk8","8.9.0-ubuntu-jdk8","8.5.4-ubuntu-jdk8","8.12.3-jdk8","8.17-jdk8","8.5-ubuntu-jdk8","8.11.0-ubuntu-jdk8","8.9.0-jdk8","8.12-jdk8","8.5-jdk8","8.9.0-ubuntu","8.12-ubuntu-jdk8","8.6.1-ubuntu","8.17-ubuntu-jdk8","8.12.3-ubuntu-jdk8","8.6.1-ubuntu-jdk8","8.5-ubuntu","8.13.7-jdk8","8.13.2-ubuntu-jdk8","8.5.5-jdk8","8.6-ubuntu","8.13.7-ubuntu-jdk8","8.11-jdk8","8.13.2-ubuntu","8.5.5-ubuntu-jdk8","8.6-jdk8","8.12-ubuntu","8.5.12-ubuntu-jdk8","8.11-ubuntu-jdk8","8.5.18-ubuntu","8.13.7-ubuntu","8.5.5-ubuntu","8.6-ubuntu-jdk8","8.5.12-ubuntu","8.13.2-jdk8","8.5.12-jdk8","8.11.1-jdk8","8.9-ubuntu-jdk8","8.11-ubuntu","8.9.1-jdk8","8.11.1-ubuntu","8.9.1-ubuntu-jdk8","8.11.1-ubuntu-jdk8","8.9-jdk8","8.13.3-ubuntu","8.13.8-ubuntu-jdk8","8.5.6-jdk8","8.9-ubuntu","8.5.0-jdk8","8.5.13-jdk8","8.5.0-ubuntu-jdk8","8.13.3-jdk8","8.5.6-ubuntu-jdk8","8.13.8-ubuntu","8.5.13-ubuntu","8.9.1-ubuntu","8.5.0-ubuntu","8.13.3-ubuntu-jdk8","8.5.6-ubuntu","8.13.8-jdk8","8.5.13-ubuntu-jdk8","8.18.0-jdk8","8.13.0-jdk8","8.5.7-jdk8","8.5.2-jdk8","8.13.9-ubuntu-jdk8","8.7.0-ubuntu","8.5.14-ubuntu","8.18.0-ubuntu","8.5.1-ubuntu-jdk8","8.5.7-ubuntu","8.5.2-ubuntu-jdk8","8.13.4-ubuntu-jdk8","8.13.0-ubuntu-jdk8","8.7.0-jdk8","8.18.0-ubuntu-jdk8","8.5.2-ubuntu","8.13.4-jdk8","8.13.0-ubuntu","8.13.9-jdk8","8.5.1-ubuntu","8.5.14-jdk8","8.5.7-ubuntu-jdk8","8.13.4-ubuntu","8.13.9-ubuntu","8.5.1-jdk8","8.5.14-ubuntu-jdk8","8.7.0-ubuntu-jdk8","8.13.10-ubuntu","8.3.5-jdk8","8.3.5","8.3.5-ubuntu-jdk8","8.3-ubuntu","8.3-jdk8","8.3","8.3.5-ubuntu","7.13.13-ubuntu","8.3-ubuntu-jdk8","7.13.13-jdk8","7.13.4-ubuntu-jdk8","7.13.13-ubuntu-jdk8","7.13.4","7.13.13","7.13.4-ubuntu","7.13.4-jdk8","8.2.4","8.2.4-ubuntu-jdk8","8.2.4-ubuntu","8.1.3-jdk8","7.13.14-ubuntu-jdk8","8.2.4-jdk8","7.13.14","8.1.3-ubuntu","7.13.5-jdk8","7.13.14-jdk8","8.1-ubuntu","7.13.5-ubuntu-jdk8","7.13.14-ubuntu","8.4.0","7.13.5-ubuntu","8.4.0-jdk8","8.2.5-ubuntu","7.13.5","8.4.0-ubuntu-jdk8","8.2.5","8.4.0-ubuntu","8.2.5-ubuntu-jdk8","8.2.5-jdk8","7.13.15","7.13.15-jdk8","7.13.6-ubuntu","7.13.15-ubuntu","7.13.6-ubuntu-jdk8","7.13.15-ubuntu-jdk8","8.4.1-ubuntu-jdk8","7.13.6","8.4.1-ubuntu","8.2","7.13.6-jdk8","8.4.1-jdk8","8.2-jdk8","8.4.1","8.2-ubuntu-jdk8","8.2-ubuntu","7.13.16-ubuntu-jdk8","8.2.6-ubuntu","7.13.16-jdk8","8.2.6-jdk8","7.13.8-ubuntu","7.13.16","8.2.6-ubuntu-jdk8","7.13.16-ubuntu","8.4.2","7.13.8-jdk8","8.2.6","8.4.2-jdk8","7.13.8-ubuntu-jdk8","8.4.2-ubuntu","7.13.8","8.4.2-ubuntu-jdk8","7.13.17-ubuntu-jdk8","8.4.1-ubuntu-jdk11","8.2.2-ubuntu-jdk11","7.13.17-ubuntu","8.4.1-jdk11","8.3.1-ubuntu-jdk11","8.2.2-jdk11","7.13.17","7.13.9-ubuntu","8.3.1-jdk11","7.13.17-jdk8","8.4.3-ubuntu","7.13.9","8.4.3-jdk8","7.13.9-ubuntu-jdk8","8.4.3","7.13.9-jdk8","8.2.3-jdk11","8.4.2-ubuntu-jdk11","8.4-jdk8","8.2.3-ubuntu-jdk11","8.3.2-ubuntu-jdk11","7.13-jdk8","8.4.3-ubuntu-jdk8","8.4.2-jdk11","8.3.2-jdk11","7.13-ubuntu-jdk8","8.4-ubuntu-jdk8","8.3.0-ubuntu-jdk8","7-jdk8","8.4-ubuntu","8.0.0-jdk8","8.3.0","7.13.18","8.4","8.0.0-ubuntu","8.3.0-ubuntu","7.13","8.2.4-ubuntu-jdk11","8.0.0-ubuntu-jdk8","8.4-jdk11","8.3.0-jdk8","7.13-ubuntu","8.2.4-jdk11","8.3.3-ubuntu-jdk11","8.0.0","8.4.3-jdk11","7-ubuntu","8.3.3-jdk11","8.4-ubuntu-jdk11","7","7-ubuntu-jdk8","8.4.3-ubuntu-jdk11","7.13.18-jdk8","8.3.1-jdk8","8.0.2","8.2.5-jdk11","7.13.18-ubuntu-jdk8","8.3.1","8.2.0","8.0.2-ubuntu","8.3.4-ubuntu-jdk11","8.2.5-ubuntu-jdk11","8.3.1-ubuntu-jdk8","7.13.18-ubuntu","8.2.0-ubuntu-jdk8","8.0.2-jdk8","8.3.4-jdk11","8.3.1-ubuntu","8.2.0-ubuntu","8.0.2-ubuntu-jdk8","8.2.0-jdk8","8.2-ubuntu-jdk11","8.3-jdk11","8.2.6-jdk11","8.3.2-ubuntu-jdk8","8.0-jdk8","8.3.5-jdk11","8.2.1","8.3.2","8.2-jdk11","8.0.3-jdk8","8.3.5-ubuntu-jdk11","8.2.1-ubuntu-jdk8","8.3.2-jdk8","8.2.6-ubuntu-jdk11","8.0-ubuntu","8.3-ubuntu-jdk11","8.2.1-ubuntu","8.3.2-ubuntu","8.0","8.2.1-jdk8","8.0.3","8.0.3-ubuntu","8.0-ubuntu-jdk8","7.13.2-jdk8","8.0.3-ubuntu-jdk8","8.3.3-jdk8","7.13.2-ubuntu-jdk8","8.2.2-ubuntu","8.3.3-ubuntu","7.13.2","8.2.2-ubuntu-jdk8","8.3.3","7.13.2-ubuntu","8.2.2-jdk8","8.2.0-ubuntu-jdk11","8.3.3-ubuntu-jdk8","8.2.2","8.2.0-jdk11","7.13.3-ubuntu","8.3.0-ubuntu-jdk11","7.13.3-ubuntu-jdk8","8.3.4-ubuntu-jdk8","8.2.3-ubuntu-jdk8","8.4.0-ubuntu-jdk11","8.2.1-ubuntu-jdk11","7.13.3-jdk8","8.3.0-jdk11","8.3.4-jdk8","8.2.3","8.4.0-jdk11","8.2.1-jdk11","7.13.3","8.3.4-ubuntu","8.2.3-jdk8","8.3.4","8.2.3-ubuntu","8.19.0-RC01-ubuntu-jdk8","8.19.0-RC01-jdk8","8.19.0-RC01-ubuntu","7.13.0-ubuntu-jdk8","7.13.0","7.13.0-ubuntu","7.13.0-jdk8","7.13.1-ubuntu-jdk8","7.13.1-jdk8","7.13.1-ubuntu","7.13.1","7.13.11-ubuntu","7.13.11","8.1.0-jdk8","8.1.0","7.13.11-ubuntu-jdk8","8.1.0-ubuntu-jdk8","7.13.11-jdk8","8.1.0-ubuntu","7.13.12-ubuntu","8.1.1-ubuntu","7.13.12","8.1.1-jdk8","7.13.12-jdk8","8.1.1","7.13.12-ubuntu-jdk8","8.1.1-ubuntu-jdk8","8.1.2-ubuntu-jdk8","8.1.2-ubuntu","8.1.2","8.1.2-jdk8","8.1.3-ubuntu-jdk8","8.1","8.1.3","8.1-jdk8","8.1-ubuntu-jdk8","eap-ubuntu","8.19.0-EAP02-ubuntu","eap-ubuntu-jdk8","8.19.0-EAP02-ubuntu-jdk8","eap-jdk8","8.19.0-EAP02-jdk8","8.19.0-EAP01-ubuntu","8.19.0-EAP01-jdk8","8.19.0-EAP01-ubuntu-jdk8","8.19.0-EAP01-ubuntu-jdk11","8.18.0-ssmith-perm-build-1","8.18.0-RC01-ubuntu","8.18.0-RC01-ubuntu-jdk8","8.18.0-RC01-jdk8","8.18.0-EAP02-ubuntu-jdk8","8.18.0-EAP02-jdk8","8.18.0-EAP02-ubuntu","8.18.0-EAP02-ubuntu-jdk11","8.18.0-EAP01-ubuntu","8.18.0-EAP01-jdk8","8.18.0-EAP01-ubuntu-jdk8","8.17.0-RC02-jdk8","8.17.0-RC02-ubuntu","8.17.0-RC02-ubuntu-jdk8","8.17.0-RC01-jdk8","8.17.0-RC01-ubuntu-jdk8","8.17.0-RC01-ubuntu","8.17.0-EAP02-ubuntu","8.17.0-EAP02-jdk8","8.17.0-EAP02-ubuntu-jdk8","8.17.0-EAP01-ubuntu","8.17.0-EAP01-ubuntu-jdk8","8.17.0-EAP01-jdk8","8.16.0-EAP02-ubuntu-jdk8","8.16.0-EAP02-jdk8","8.16.0-EAP02-ubuntu","8.16.0-EAP02","8.16.0-EAP02-jdk11","8.16.0-EAP02-ubuntu-jdk11","8.16.0-RC01-ubuntu-jdk11","8.16.0-RC01-jdk11","8.16.0-EAP03","8.16.0-EAP03-ubuntu-jdk11","8.16.0-EAP03-ubuntu-jdk8","8.16.0-EAP03-jdk11","8.16.0-EAP03-jdk8","8.16.0-EAP03-ubuntu","8.16.0-RC01-ubuntu-jdk8","8.16.0-RC01-ubuntu","8.16.0-RC01","8.16.0-RC01-jdk8","8.16.0-RC02-ubuntu-jdk11","8.16.0-RC02-jdk11","8.16.0-RC02","8.16.0-RC02-ubuntu","8.16.0-RC02-ubuntu-jdk8","8.16.0-RC02-jdk8","8.16.0-EAP01","8.16.0-EAP01-jdk8","8.16.0-EAP01-ubuntu","8.16.0-EAP01-ubuntu-jdk11","8.16.0-EAP01-jdk11","8.15.0-RC01","8.15.0-RC01-jdk8","8.15.0-RC01-ubuntu-jdk11","8.15.0-RC01-ubuntu","8.15.0-RC01-jdk11","8.15.0-EAP03-jdk11","8.15.0-EAP03-ubuntu-jdk11","8.15.0-EAP03-jdk8","8.15.0-EAP03","8.15.0-EAP03-ubuntu","8.15.0-EAP02-ubuntu","8.15.0-EAP02","8.15.0-EAP02-jdk8","8.15.0-EAP02-ubuntu-jdk11","8.15.0-EAP02-jdk11","8.15.0-EAP01-ubuntu-jdk11","8.15.0-EAP01-jdk11","8.15.0-EAP01","8.15.0-EAP01-ubuntu","8.15.0-EAP01-jdk8","8.14.0-RC01-jdk11","8.14.0-RC01-ubuntu-jdk11","8.14.0-RC01","8.14.0-RC01-ubuntu","8.14.0-RC01-jdk8","8.14.0-EAP01-ubuntu","8.14.0-EAP01","8.14.0-EAP01-jdk8","8.14.0-EAP01-ubuntu-jdk11","8.14.0-EAP01-jdk11","8.13.0-RC01","8.13.0-RC01-jdk11","8.13.0-RC01-ubuntu","8.13.0-RC01-ubuntu-jdk11","8.13.0-RC01-jdk8","8.13.0-EAP03-jdk8","8.13.0-EAP03","8.13.0-EAP03-ubuntu","8.13.0-EAP03-ubuntu-jdk11","8.13.0-EAP03-jdk11","8.13.0-EAP02","8.13.0-EAP02-ubuntu","8.13.0-EAP02-jdk8","8.13.0-EAP02-jdk11","8.13.0-EAP02-ubuntu-jdk11","8.12.0-RC02-jdk11","8.12.0-RC02-ubuntu-jdk11","8.12.0-RC02-jdk8","8.12.0-RC02-ubuntu","8.12.0-RC02","8.13.0-EAP01-jdk11","8.13.0-EAP01-ubuntu-jdk11","8.12.0-RC01-jdk11","8.12.0-RC01-ubuntu-jdk11","8.13.0-EAP01-jdk8","8.13.0-EAP01-ubuntu","8.13.0-EAP01","8.12.0-RC01-jdk8","8.12.0-RC01-ubuntu","8.12.0-RC01","8.12.0-EAP02-ubuntu-jdk11","8.12.0-EAP02-jdk11","8.12.0-EAP02","8.12.0-EAP02-ubuntu","8.12.0-EAP02-jdk8","8.12.0-EAP01-jdk11","8.12.0-EAP01-ubuntu-jdk11","8.12.0-EAP01","8.12.0-EAP01-jdk8","8.12.0-EAP01-ubuntu","8.5.6-RC02-jdk8","8.5.6-RC02-ubuntu","8.5.6-RC02","8.5.6-RC02-ubuntu-jdk11","8.5.6-RC02-jdk11","8.11.0-RC01","8.11.0-RC01-jdk8","8.11.0-RC01-ubuntu","8.11.0-RC01-jdk11","8.11.0-RC01-ubuntu-jdk11","8.11.0-EAP01-ubuntu-jdk11","8.11.0-EAP01-jdk11","8.11.0-EAP01-jdk8","8.11.0-EAP01-ubuntu","8.11.0-EAP01","8.10.0-RC01-ubuntu","8.10.0-RC01","8.10.0-RC01-jdk8","8.10.0-RC01-jdk11","8.10.0-RC01-ubuntu-jdk11","7.13.14-RC01","7.13.14-RC01-ubuntu","7.13.14-RC01-jdk8","8.10.0-EAP02-jdk8","8.10.0-EAP02","8.10.0-EAP02-ubuntu","8.10.0-EAP01-jdk8","8.10.0-EAP01","8.10.0-EAP01-ubuntu","8.10.0-EAP01-ubuntu-jdk11","7.13.15-RC01-ubuntu","8.10.0-EAP01-jdk11","7.13.15-RC01","7.13.15-RC01-jdk8","8.10.0-EAP02-ubuntu-jdk11","8.10.0-EAP02-jdk11","8.5.6-RC01-jdk11","8.5.6-RC01-ubuntu-jdk11","8.5.6-RC01","8.5.6-RC01-ubuntu","8.5.6-RC01-jdk8","8.5.5-RC01-jdk11","8.5.5-RC01-ubuntu-jdk11","8.5.5-RC01","8.5.5-RC01-jdk8","8.5.5-RC01-ubuntu","8.9.0-RC01-ubuntu","8.9.0-RC01-jdk8","8.9.0-RC01","8.9.0-RC01-jdk11","8.9.0-RC01-ubuntu-jdk11","8.9.0-EAP02-jdk11","8.9.0-EAP02-ubuntu-jdk11","8.9.0-EAP02-jdk8","8.9.0-EAP02-ubuntu","8.9.0-EAP02","8.9.0-EAP01-ubuntu-jdk11","8.9.0-EAP01-jdk11","8.9.0-EAP01-ubuntu","8.9.0-EAP01","8.9.0-EAP01-jdk8","8.8.0-RC02-ubuntu-jdk11","8.8.0-RC02-jdk11","8.8.0-RC02-jdk8","8.8.0-RC02-ubuntu","8.8.0-RC02"],"confluence":["8.7-ubuntu-jdk17","8.7-jdk17","8.7.2-ubuntu-jdk17","8.6.0-jdk17","8.7.2","8.6.0-ubuntu-jdk17","8.7.2-jdk17","8.6.0","8.7","8.5.5-ubuntu-jdk17","8.5.5-jdk17","8.8.0-ubuntu-jdk17","8.8.0-jdk17","8.5.5","8.7-jdk11","8.7-ubuntu-jdk11","8.8.0","8.7.2-ubuntu-jdk11","8.7.2-jdk11","8.1-jdk11","8.1-ubuntu-jdk11","8.1.4-ubuntu-jdk11","8.1.4-jdk11","8.1","8.1.4","7.19-ubuntu-jdk11","7.19-jdk11","7.19.21-ubuntu-jdk11","7.19.21","7.19.21-jdk11","8.8-ubuntu-jdk17","8.8.1-ubi9","8.8.1-ubi9-jdk17","8.8.0-ubuntu-jdk11","8.8.1-jdk17","8.8.0-jdk11","8.6.1","8.6.1-jdk17","8.6.1-ubuntu-jdk17","8.5.6-ubuntu-jdk17","8.5.6-jdk17","8.5.6","8.5.5-ubuntu-jdk11","8.5.1-ubuntu-jdk17","8.5.5-jdk11","8.5.1-jdk17","8.5.1","8.4.4-jdk17","8.4.4-ubuntu-jdk17","8.4.4","8.0.3-jdk11","8.0.3","8.0.3-ubuntu-jdk11","7.20.2-jdk11","7.20.2-ubuntu-jdk11","7.20.2","8.8-jdk17","8.8.1-ubuntu-jdk17","8.8.1","8.8","8.6.0-ubuntu-jdk11","8.0-jdk11","8.0.4-ubuntu-jdk11","8.3.3-ubuntu-jdk17","8.6.0-jdk11","8.0.4-jdk11","8.3.3","8.0","8.3.3-jdk17","7.19.7-ubuntu-jdk11","7.19.7-jdk11","8.8-ubuntu-jdk11","8.8-jdk11","8.8.1-ubuntu-jdk11","7.19.7","7.19.17-ubuntu-jdk11","8.8.1-jdk11","7.19.17-jdk11","7.19.17","8.7.2-ubi9-jdk17","8.6-ubuntu-jdk17","8.7.2-ubi9","8.6-jdk17","8.6.2-ubuntu-jdk17","8.6.2-ubi9-jdk17","8.6.2-ubi9","8.6","8.6.2-jdk17","8.6.2","8.5.7-ubuntu-jdk17","8.5.7","8.5.7-jdk17","8.5.6-ubuntu-jdk11","8.5.6-jdk11","8.5.2-ubuntu-jdk17","8.5.2-jdk17","8.4.5-ubuntu-jdk17","8.4-jdk17","8.5.2","8.4-ubuntu-jdk17","latest","8.4.5-jdk17","8.4.5","8.4.1-ubuntu-jdk17","8.4","8.4.1-jdk17","8.4.1","8.2.0-jdk11","8.2.0-ubuntu-jdk11","8.3.0","8.3.0-ubuntu-jdk17","8.3.0-jdk17","8.0.4","8.0-ubuntu-jdk11","7-jdk11","7-ubuntu-jdk11","7.20-ubuntu-jdk11","7.20-jdk11","7.20.3-jdk11","7.20.3-ubuntu-jdk11","7.20.3","7.20","8.6.1-ubuntu-jdk11","7","8.6.1-jdk11","8.5.1-jdk11","8.5.1-ubuntu-jdk11","8.4.4-ubuntu-jdk11","8.3-ubuntu-jdk17","8.4.4-jdk11","8.3-jdk17","8.3.4-ubuntu-jdk17","8.3.4","8.3.4-jdk17","8.3.3-ubuntu-jdk11","8.3.3-jdk11","8.3","7.19.3-ubuntu-jdk11","7.19.18-jdk11","7.19.3-jdk11","7.19.18-ubuntu-jdk11","7.19.3","7.19.18","7.19.12-ubuntu-jdk11","7.19.12-jdk11","7.19.12","ubuntu-jdk17","ubi9-jdk17","jdk17","ubi9","8-ubuntu-jdk17","8-jdk17","8.9-ubuntu-jdk17","8.9-jdk17","8.9.0-ubuntu-jdk17","8.9.0-ubi9","8.9.0-ubi9-jdk17","8.9.0","8.9.0-jdk17","8.9","8.5-ubuntu-jdk17","8.5-jdk17","8.5.8-ubuntu-jdk17","8","8.5","8.5.8","8.5.8-jdk17","7.19.8-jdk11","7.19.8-ubuntu-jdk11","7.19.8","7.19.0-ubuntu-jdk11","7.19.0-jdk11","7.19.0","8.5.8-ubi9-jdk17","8.5.7-ubuntu-jdk11","8.5.7-jdk11","8.5.8-ubi9","8.5.3-jdk17","8.5.3-ubuntu-jdk17","8.5.3","8.4.2-ubuntu-jdk17","8.4.2-jdk17","8.3.1-ubuntu-jdk17","8.4.2","8.3.1-jdk17","8.6-ubuntu-jdk11","8.3.1","8.6-jdk11","8.6.2-ubuntu-jdk11","8.2.1-jdk11","8.2.1-ubuntu-jdk11","8.6.2-jdk11","8.2.1","8.2.0-ubuntu-jdk17","8.5.2-ubuntu-jdk11","8.5.2-jdk11","8.2.0-jdk17","8.4-ubuntu-jdk11","8.4.5-ubuntu-jdk11","8.4-jdk11","8.4.5-jdk11","8.4.1-ubuntu-jdk11","8.3-ubuntu-jdk11","8.4.1-jdk11","8.3-jdk11","8.3.4-ubuntu-jdk11","8.3.4-jdk11","8.3.0-ubuntu-jdk11","8.3.0-jdk11","8.1.3-jdk17","8.1.3-ubuntu-jdk17","7.19.4-ubuntu-jdk11","7.19.4-jdk11","7.19.4","7.19.1-ubuntu-jdk11","7.19.19-ubuntu-jdk11","7.19.1-jdk11","7.19.19-jdk11","7.19.19","8.8.0-ubi9-jdk17","7.19.14-ubuntu-jdk11","7.19.14-jdk11","8.8.0-ubi9","8.7.1-ubuntu-jdk17","7.19.14","7.19","8.7.1-ubi9-jdk17","7.19.1","8.7.1-jdk17","8.7.1-ubi9","8.7.1","8.6.1-ubi9-jdk17","8.6.1-ubi9","8.6.0-ubi9-jdk17","8.6.0-ubi9","8.5.7-ubi9-jdk17","8.5.7-ubi9","8.5.6-ubi9-jdk17","8.5.6-ubi9","8.5.0-ubuntu-jdk17","8.5.0-jdk17","8.5.0","8.4.0-ubuntu-jdk17","8.4.0","8.4.0-jdk17","8.3.2-jdk17","8.3.2","8.3.2-ubuntu-jdk17","8.2.2-ubuntu-jdk17","8.2.2-ubuntu-jdk11","8.2.2-jdk17","8.2.2-jdk11","8.2.2","8.2.1-ubuntu-jdk17","8.2.1-jdk17","8.1.0-jdk11","8.1.0-ubuntu-jdk11","8.1.0","8.0.3-ubuntu-jdk17","8.0.3-jdk17","8.0.0-ubuntu-jdk11","8.0.0-jdk11","8.2.0","8.0.0","7.19.9-ubuntu-jdk11","7.19.9-jdk11","7.19.9","jdk11","ubuntu-jdk11","8-jdk11","8-ubuntu-jdk11","8.9-ubuntu-jdk11","8.9-jdk11","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.5-ubuntu-jdk11","8.5-jdk11","8.5.4-ubuntu-jdk17","8.5.8-jdk11","8.5.8-ubuntu-jdk11","8.5.4-jdk17","8.5.4","8.4.3-ubuntu-jdk17","8.4.3-jdk17","8.4.3","8.0.0-ubuntu-jdk17","8.0.0-jdk17","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.4.2-ubuntu-jdk11","8.4.2-jdk11","8.3.1-ubuntu-jdk11","8.3.1-jdk11","8.2-ubuntu-jdk17","8.2-jdk17","8.2.3-jdk17","8.2.3-ubuntu-jdk17","8.1-ubuntu-jdk17","8.1.4-ubuntu-jdk17","8.1.4-jdk17","8.1-jdk17","8.1.0-ubuntu-jdk17","8.1.0-jdk17","8.0-ubuntu-jdk17","8.0.4-ubuntu-jdk17","8.0-jdk17","8.0.4-jdk17","7.19.5-ubuntu-jdk11","7.19.5","7.19.2-ubuntu-jdk11","7.19.5-jdk11","7.19.2-jdk11","7.19.2","7.19.15-ubuntu-jdk11","7.19.15-jdk11","7.19.10-jdk11","7.19.15","7.19.10-ubuntu-jdk11","7.19.10","8.2-ubuntu-jdk11","8.2-jdk11","8.2.3-jdk11","8.2.3","8.2","8.1.1-ubuntu-jdk11","8.1.1-jdk11","8.1.1","8.0.1-ubuntu-jdk11","8.0.1-jdk11","7.20.0-ubuntu-jdk11","8.0.1","7.20.0-jdk11","7.20.0","8.5.4-jdk11","8.5.4-ubuntu-jdk11","8.5.0-ubuntu-jdk11","8.5.0-jdk11","8.7.1-jdk11","8.7.1-ubuntu-jdk11","8.4.3-jdk11","8.4.3-ubuntu-jdk11","8.4.0-ubuntu-jdk11","8.4.0-jdk11","8.1.1-ubuntu-jdk17","8.1.3-ubuntu-jdk11","8.3.2-ubuntu-jdk11","8.3.2-jdk11","8.0.2-ubuntu-jdk17","8.1.3","8.1.3-jdk11","8.0.2-ubuntu-jdk11","8.2.3-ubuntu-jdk11","8.0.2-jdk11","8.0.1-ubuntu-jdk17","8.0.2","7.20.1","7.20.1-jdk11","7.19.6-ubuntu-jdk11","8.1.1-jdk17","7.19.20","7.19.16-ubuntu-jdk11","7.19.6","8.0.2-jdk17","7.19.20-jdk11","7.19.16","8.0.1-jdk17","7.20.1-ubuntu-jdk11","7.19.6-jdk11","7.19.20-ubuntu-jdk11","7.19.16-jdk11","7.19.11","7.19.11-jdk11","7.19.11-ubuntu-jdk11","eap-ubuntu-jdk11","eap-jdk17","eap-jdk11","9.0.0-m41-jdk17","9.0.0-m41","9.0.0-m41-jdk11","9.0.0-m41-ubuntu-jdk17","9.0.0-m41-ubi9","9.0.0-m41-ubi9-jdk17","eap-ubuntu-jdk17","eap","9.0.0-m41-ubuntu-jdk11","9.0.0-m30-jdk17","9.0.0-m30-ubuntu-jdk17","9.0.0-m30-jdk11","9.0.0-m30","9.0.0-m30-ubuntu-jdk11","9.0.0-m26-ubuntu-jdk17","9.0.0-m26-jdk17","9.0.0-m26-ubuntu-jdk11","9.0.0-m26-ubi9","9.0.0-m26-jdk11","9.0.0-m26","9.0.0-m26-ubi9-jdk17","8.9.0-rc1-ubuntu-jdk11","8.9.0-rc1-jdk11","8.9.0-rc1-ubi9","8.9.0-rc1-jdk17","8.9.0-rc1-ubi9-jdk17","8.9.0-rc1-ubuntu-jdk17","8.9.0-rc1","9.0.0-m23-jdk11","9.0.0-m23-ubi9-jdk17","9.0.0-m23-ubi9","9.0.0-m23-ubuntu-jdk11","9.0.0-m23","9.0.0-m23-jdk17","9.0.0-m23-ubuntu-jdk17","8.9.0-beta2-ubi9-jdk17","8.9.0-beta2-ubi9","8.9.0-beta2-ubuntu-jdk17","8.9.0-beta2-jdk17","8.9.0-beta2-jdk11","8.9.0-beta2-ubuntu-jdk11","8.9.0-beta2","9.0.0-m16-ubuntu-jdk17","9.0.0-m16-jdk17","9.0.0-m16-ubi9-jdk17","9.0.0-m16-ubi9","9.0.0-m16-jdk11","9.0.0-m16-ubuntu-jdk11","9.0.0-m16","8.9.0-m58-ubuntu-jdk11","8.9.0-m58-jdk11","8.9.0-m58-jdk17","8.9.0-m58-ubuntu-jdk17","8.9.0-m58","9.0.0-m15-jdk17","9.0.0-m15-ubuntu-jdk17","9.0.0-m15","9.0.0-m15-jdk11","9.0.0-m15-ubuntu-jdk11","8.9.0-m50-jdk17","8.9.0-m50-ubuntu-jdk11","8.9.0-m50-ubuntu-jdk17","8.9.0-m50","8.9.0-m50-jdk11","9.0.0-m14-ubuntu-jdk11","9.0.0-m14-jdk17","9.0.0-m14-jdk11","9.0.0-m14","9.0.0-m14-ubuntu-jdk17","9.0.0-m13-jdk11","9.0.0-m13-jdk17","9.0.0-m13","9.0.0-m13-ubuntu-jdk17","9.0.0-m13-ubuntu-jdk11","8.9.0-m43-jdk17","8.9.0-m43","8.9.0-m43-jdk11","8.9.0-m43-ubuntu-jdk11","8.9.0-m43-ubuntu-jdk17","9.0.0-m11-jdk17","9.0.0-m11","9.0.0-m11-ubuntu-jdk17","9.0.0-m10-ubuntu-jdk11","9.0.0-m10-jdk11","9.0.0-m10-ubuntu-jdk17","9.0.0-m10-jdk17","9.0.0-m10","9.0.0-m09-ubuntu-jdk17","9.0.0-m09-jdk17","9.0.0-m09-ubuntu-jdk11","9.0.0-m09-jdk11","9.0.0-m09","8.8.0-rc1-jdk17","8.8.0-rc1-jdk11","8.8.0-rc1-ubuntu-jdk11","8.8.0-rc1","8.8.0-rc1-ubuntu-jdk17","9.0.0-m08-jdk17","9.0.0-m08-ubuntu-jdk17","9.0.0-m08","9.0.0-m08-ubuntu-jdk11","9.0.0-m08-jdk11","8.8.0-beta1-jdk17","8.8.0-beta1-ubuntu-jdk17","8.8.0-beta1-jdk11","8.8.0-beta1","8.8.0-beta1-ubuntu-jdk11","9.0.0-m07-ubuntu-jdk17","9.0.0-m07-jdk17","9.0.0-m07-jdk11","9.0.0-m07","9.0.0-m07-ubuntu-jdk11","7.16.2-ubuntu-jdk11","7.16.2","7.16.2-jdk11","7.13.7-ubuntu-jdk11","7.13.7-jdk11","7.13.7","7.11.0-ubuntu-jdk11","7.11.0-jdk11","7.11.0","7.13.12-ubuntu-jdk11","7.13.12-jdk11","7.13.12","7.16.3-ubuntu-jdk11","7.16.3-jdk11","7.16.3","7.13.8-ubuntu-jdk11","7.13.8-jdk11","7.13.8","7.11.1-ubuntu-jdk11","7.11.1-jdk11","7.11.1","7.13.13-ubuntu-jdk11","7.13.13-jdk11","7.13.13","7.16.4-ubuntu-jdk11","7.16.4-jdk11","7.16.4","7.11.2-ubuntu-jdk11","7.11.2-jdk11","7.11.2","7.13.9-ubuntu-jdk11","7.13.9-jdk11","7.13.9","7.16-ubuntu-jdk11","7.16-jdk11","7.16.5-ubuntu-jdk11","7.16.5-jdk11","7.16.5","7.16","7.13.14-ubuntu-jdk11","7.13.14-jdk11","7.13.14","7.14.0-ubuntu-jdk11","7.14.0-jdk11","7.14.0","7.11.3-ubuntu-jdk11","7.11.3-jdk11","7.11.3","7.13.15-ubuntu-jdk11","7.13.15-jdk11","7.11-ubuntu-jdk11","7.13.15","7.11.6-ubuntu-jdk11","7.11.6","7.11","7.17.0-ubuntu-jdk11","7.17.0-jdk11","7.17.0","7.14.1-ubuntu-jdk11","7.14.1-jdk11","7.14.1","7.11-jdk11","7.11.6-jdk11","7.13.16-ubuntu-jdk11","7.13.16-jdk11","7.13.16","7.17.1-ubuntu-jdk11","7.17.1-jdk11","7.17.1","7.14.2-ubuntu-jdk11","7.14.2-jdk11","7.14.2","7.13.17-ubuntu-jdk11","7.13.17-jdk11","7.13.17","7.12.0-ubuntu-jdk11","7.12.0-jdk11","7.12.0","7.17.2-ubuntu-jdk11","7.17.2-jdk11","7.17.2","7.14.3-ubuntu-jdk11","7.14.3-jdk11","7.14.3","7.13.18-ubuntu-jdk11","7.13.18-jdk11","7.13.18","7.17.3-ubuntu-jdk11","7.17.3-jdk11","7.17.3","7.14-ubuntu-jdk11","7.14-jdk11","7.14.4-ubuntu-jdk11","7.14.4-jdk11","7.14.4","7.14","7.12.1-ubuntu-jdk11","7.12.1-jdk11","7.12.1","7.17.4-ubuntu-jdk11","7.17.4-jdk11","7.17.4","7.13.19-ubuntu-jdk11","7.13.19-jdk11","7.13.19","7.12.2-ubuntu-jdk11","7.12.2-jdk11","7.12.2","7.15.0-ubuntu-jdk11","7.15.0-jdk11","7.15.0","7.13.2-ubuntu-jdk11","7.13.2-jdk11","7.13.2","7.12.3-ubuntu-jdk11","7.12.3-jdk11","7.12.3","7.17-ubuntu-jdk11","7.17-jdk11","7.17.5-ubuntu-jdk11","7.17.5-jdk11","7.17.5","7.17","7.15.1-ubuntu-jdk11","7.15.1-jdk11","7.15.1","7.13-ubuntu-jdk11","7.13-jdk11","7.13.20-ubuntu-jdk11","7.13.20-jdk11","7.13.20","7.13","7.12.4-ubuntu-jdk11","7.12.4-jdk11","7.12.4","7.18.0-ubuntu-jdk11","7.18.0-jdk11","7.18.0","7.15.2-ubuntu-jdk11","7.15.2-jdk11","7.15.2","7.12-ubuntu-jdk11","7.12.5","7.12-jdk11","7.12.5-ubuntu-jdk11","7.12.5-jdk11","7.12","7.15-ubuntu-jdk11","7.15-jdk11","7.15.3-ubuntu-jdk11","7.15.3-jdk11","7.15.3","7.15","7.13.3-ubuntu-jdk11","7.13.3-jdk11","7.13.3","7.18.1-ubuntu-jdk11","7.18.1-jdk11","7.18.1","7.18.2-ubuntu-jdk11","7.18.2-jdk11","7.18.2","7.13.4-ubuntu-jdk11","7.13.4-jdk11","7.13.4","7.13.0-ubuntu-jdk11","7.13.0-jdk11","7.13.0","7.16.0-ubuntu-jdk11","7.16.0-jdk11","7.16.0","7.18-ubuntu-jdk11","7.18-jdk11","7.18.3-ubuntu-jdk11","7.18.3-jdk11","7.18.3","7.18","7.13.5-ubuntu-jdk11","7.13.5-jdk11","7.13.5","7.13.1-ubuntu-jdk11","7.13.1-jdk11","7.13.1","7.16.1-ubuntu-jdk11","7.16.1-jdk11","7.16.1","7.13.6-ubuntu-jdk11","7.13.6-jdk11","7.13.6","7.13.11-ubuntu-jdk11","7.13.11-jdk11","7.13.11","8.8.0-m61-ubuntu-jdk11","8.8.0-m61-jdk11","8.8.0-m61","8.8.0-m61-ubuntu-jdk17","8.8.0-m61-jdk17","8.8.0-m46-ubuntu-jdk11","8.8.0-m46-ubuntu-jdk17","8.8.0-m46-jdk17","8.8.0-m46","8.8.0-m46-jdk11","8.7.0-rc1-ubuntu-jdk17","8.7.0-rc1-jdk17","8.7.0-rc1-jdk11","8.7.0-rc1","8.7.0-rc1-ubuntu-jdk11","8.7.0-beta1-ubuntu-jdk11","8.7.0-beta1","8.7.0-beta1-jdk11","8.7.0-beta1-jdk17","8.7.0-beta1-ubuntu-jdk17","8.7.0-m48-ubuntu-jdk17","8.7.0-m48-jdk17","8.7.0-m48-jdk11","8.7.0-m48-ubuntu-jdk11","8.7.0-m48","8.7.0-m41-ubuntu-jdk17","8.7.0-m41-ubuntu-jdk11","8.7.0-m41-jdk17","8.7.0-m41-jdk11","8.7.0-m41","8.6.0-rc1-ubuntu-jdk17","8.6.0-rc1-jdk17","8.6.0-rc1-ubuntu-jdk11","8.6.0-rc1-jdk11","8.6.0-rc1","8.6.0-beta2-jdk17","8.6.0-beta2","8.6.0-beta2-jdk11","8.6.0-beta2-ubuntu-jdk11","8.6.0-beta2-ubuntu-jdk17","8.6.0-beta1-jdk17","8.6.0-beta1-ubuntu-jdk17","8.6.0-beta1","8.6.0-beta1-ubuntu-jdk11","8.6.0-beta1-jdk11","8.6.0-m85-ubuntu-jdk17","8.6.0-m85-jdk17","8.6.0-m85","8.6.0-m85-ubuntu-jdk11","8.6.0-m85-jdk11","8.6.0-m79-jdk11","8.6.0-m79-ubuntu-jdk11","8.6.0-m79-ubuntu-jdk17","8.6.0-m79-jdk17","8.6.0-m79","8.5.0-rc1-jdk11","8.5.0-rc1","8.5.0-rc1-jdk17","8.5.0-rc1-ubuntu-jdk11","8.5.0-rc1-ubuntu-jdk17","8.5.0-beta1-ubuntu-jdk17","8.5.0-beta1-jdk17","8.5.0-beta1","8.5.0-beta1-ubuntu-jdk11","8.5.0-beta1-jdk11","8.5.0-m06-jdk17","8.5.0-m06-ubuntu-jdk17","8.5.0-m06-jdk11","8.5.0-m06","8.5.0-m06-ubuntu-jdk11","8.5.0-m05-jdk17","8.5.0-m05-jdk11","8.5.0-m05-ubuntu-jdk17","8.5.0-m05-ubuntu-jdk11","8.5.0-m05","8.5.0-m04-jdk17","8.5.0-m04-ubuntu-jdk11","8.5.0-m04-ubuntu-jdk17","8.5.0-m04","8.5.0-m04-jdk11","8.4.0-rc2-ubuntu-jdk17","8.4.0-rc2-jdk17","8.4.0-rc2-ubuntu-jdk11","8.4.0-rc2","8.4.0-rc2-jdk11","8.4.0-rc1-jdk17","8.4.0-rc1-ubuntu-jdk17","8.4.0-rc1-jdk11","8.4.0-rc1","8.4.0-rc1-ubuntu-jdk11","8.4.0-beta1-jdk17","8.4.0-beta1-ubuntu-jdk17","8.4.0-beta1","8.4.0-beta1-jdk11","8.4.0-beta1-ubuntu-jdk11","8.4.0-m48-jdk17","8.4.0-m48-ubuntu-jdk11","8.4.0-m48-jdk11","8.4.0-m48-ubuntu-jdk17","8.4.0-m48","8.4.0-m22","8.4.0-m22-jdk17","8.4.0-m22-ubuntu-jdk11","8.4.0-m22-jdk11","8.4.0-m22-ubuntu-jdk17","8.4.0-m21-ubuntu-jdk17","8.4.0-m21-jdk17","8.4.0-m21","8.4.0-m21-ubuntu-jdk11","8.4.0-m21-jdk11","8.3.0-rc2-ubuntu-jdk11","8.3.0-rc2-jdk11","8.3.0-rc2-ubuntu-jdk17","8.3.0-rc2-jdk17","8.3.0-rc2","8.3.0-rc1-ubuntu-jdk11","8.3.0-rc1-ubuntu-jdk17","8.3.0-rc1-jdk11","8.3.0-rc1-jdk17","8.3.0-rc1","8.3.0-beta1-jdk11","8.3.0-beta1-jdk17","8.3.0-beta1-ubuntu-jdk11","8.3.0-beta1","8.3.0-beta1-ubuntu-jdk17","8.3.0-tinymce6-m01-ubuntu-jdk11","8.3.0-tinymce6-m01","8.3.0-tinymce6-m01-ubuntu-jdk17","8.3.0-tinymce6-m01-jdk11","8.3.0-tinymce6-m01-jdk17","8.3.0-m48","8.3.0-m48-ubuntu-jdk17","8.3.0-m48-jdk11","8.3.0-m48-ubuntu-jdk11","8.3.0-m48-jdk17","8.3.0-m42-ubuntu-jdk17","8.3.0-m42-jdk17","8.3.0-m42-jdk11","8.3.0-m42-ubuntu-jdk11","8.3.0-m42","8.3.0-m37-jdk11","8.3.0-m37-ubuntu-jdk11","8.3.0-m37-jdk17","8.3.0-m37","8.3.0-m37-ubuntu-jdk17","8.3.0-m31-ubuntu-jdk17","8.3.0-m31","8.3.0-m31-ubuntu-jdk11","8.3.0-m31-jdk11","8.3.0-m31-jdk17","8.2.0-rc1-jdk11","8.2.0-rc1-jdk17","8.2.0-rc1-ubuntu-jdk11","8.2.0-rc1-ubuntu-jdk17","8.2.0-rc1","8.2.0-beta2","8.2.0-beta2-ubuntu-jdk11","8.2.0-beta2-jdk17","8.2.0-beta2-ubuntu-jdk17","8.2.0-beta2-jdk11","8.2.0-beta1-ubuntu-jdk11","8.2.0-beta1-ubuntu-jdk17","8.2.0-beta1-jdk17","8.2.0-beta1-jdk11","8.2.0-beta1","8.2.0-m36-jdk11","8.2.0-m36-jdk17","8.2.0-m36","8.2.0-m36-ubuntu-jdk17","8.2.0-m36-ubuntu-jdk11","8.2.0-m27-ubuntu-jdk11","8.2.0-m27-jdk17","8.2.0-m27-jdk11","8.2.0-m27","8.2.0-m27-ubuntu-jdk17","8.2.0-m20","8.2.0-m20-jdk11","8.2.0-m20-ubuntu-jdk11","8.2.0-m20-ubuntu-jdk17","8.2.0-m20-jdk17","8.1.0-rc1-jdk11","8.1.0-rc1","8.1.0-rc1-ubuntu-jdk11","8.1.0-rc1-ubuntu-jdk17","8.1.0-rc1-jdk17","8.1.0-beta2","8.1.0-beta2-jdk11","8.1.0-beta2-ubuntu-jdk11","8.1.0-beta2-ubuntu-jdk17","8.1.0-beta2-jdk17","8.1.0-beta1-jdk11","8.1.0-beta1","8.1.0-beta1-ubuntu-jdk11","8.1.0-beta1-jdk17","8.1.0-beta1-ubuntu-jdk17","8.1.0-m78-jdk11","8.1.0-m78-ubuntu-jdk17","8.1.0-m78-ubuntu-jdk11","8.1.0-m78","8.1.0-m78-jdk17","7.6.2-ubuntu-jdk11","7.6.2-jdk11","7.6.2","7.6.1-ubuntu-jdk11","7.6.1-jdk11","7.6.1","7.9-ubuntu-jdk11","7.9-jdk11","7.9.3-ubuntu-jdk11","7.9.3-jdk11","7.9.3","7.9.1-ubuntu-jdk11","7.9.1-jdk11","7.9.1","7.9.0-ubuntu-jdk11","7.9.0-jdk11","7.9.0","7.9","7.8-ubuntu-jdk11","7.8-jdk11","7.8.3-ubuntu-jdk11","7.8.3-jdk11","7.8.3","7.8","7.6.0-ubuntu-jdk11","7.6.0-jdk11","7.6.0","7.5-ubuntu-jdk11","7.5-jdk11","7.5.2-ubuntu-jdk11","7.5.2-jdk11","7.8.1-ubuntu-jdk11","7.8.1-jdk11","7.5.2","7.8.1","7.8.0-ubuntu-jdk11","7.8.0-jdk11","7.5.1-ubuntu-jdk11","7.8.0","7.7-ubuntu-jdk11","7.5.1-jdk11","7.7-jdk11","7.7.4-ubuntu-jdk11","7.5.1","7.7.4-jdk11","7.7.4","7.5.0-ubuntu-jdk11","7.7.3-ubuntu-jdk11","7.7.3-jdk11","7.7.3","7.5.0-jdk11","7.7.2-ubuntu-jdk11","7.7.2-jdk11","7.5.0","7.7.2","7.7","7.6-ubuntu-jdk11","7.5","7.6-jdk11","7.6.3-ubuntu-jdk11","7.4.9-ubuntu-jdk11","7.6.3-jdk11","7.6.3","7.6","7.4.9-jdk11","7.4.9","7.4.17-ubuntu-jdk11","7.4.17-jdk11","7.4-ubuntu-jdk11","7.4.17","7.4-jdk11","7.4.16-ubuntu-jdk11","7.4.8-ubuntu-jdk11","7.4.16-jdk11","7.4.8-jdk11","7.4.8","7.4.16","7.4.7-ubuntu-jdk11","7.4.7-jdk11","7.4.7","7.4.15-ubuntu-jdk11","7.4.6-ubuntu-jdk11","7.4.6-jdk11","7.4.6","7.4.15-jdk11","7.4.5-ubuntu-jdk11","7.4.5-jdk11","7.4.5","7.4.4-ubuntu-jdk11","7.4.15","7.4.4-jdk11","7.4.4","7.4.3-ubuntu-jdk11","7.4.3-jdk11","7.4.14-ubuntu-jdk11","7.4.3","7.4.1-ubuntu-jdk11","7.4.1-jdk11","7.4.14-jdk11","7.4.18-ubuntu-jdk11","7.4.18-jdk11","7.4.18","7.4.14","7.4.13-ubuntu-jdk11","7.4.13-jdk11","7.4.13","7.4.12-ubuntu-jdk11","7.4.12-jdk11","7.4.12","7.4.11-ubuntu-jdk11","7.4.11-jdk11","7.4.11","7.4.10-ubuntu-jdk11","7.4.10-jdk11","7.4.10","7.4.1","7.4.0-ubuntu-jdk11","7.4.0-jdk11","7.4.0","7.4","7.10-ubuntu-jdk11","7.10-jdk11","7.10.2-ubuntu-jdk11","7.10.2-jdk11","7.10.2","7.10.1-ubuntu-jdk11","7.10.1-jdk11","7.10.1","7.10.0-ubuntu-jdk11","7.10.0-jdk11","7.10.0","7.10","8.0.0-rc1-ubuntu-jdk11","8.0.0-rc1-jdk17","8.0.0-rc1-jdk11","8.0.0-rc1","8.0.0-rc1-ubuntu-jdk17","8.0.0-beta1-ubuntu-jdk17","8.0.0-beta1","8.0.0-beta1-ubuntu-jdk11","8.0.0-beta1-jdk17","8.0.0-beta1-jdk11","8.0.0-m90-ubuntu-jdk17","8.0.0-m90-ubuntu-jdk11","8.0.0-m90-jdk11","8.0.0-m90-jdk17","8.0.0-m90","8.0.0-m89-ubuntu-jdk17","8.0.0-m89-jdk17","8.0.0-m89-jdk11","8.0.0-m89","8.0.0-m89-ubuntu-jdk11","8.0.0-m86","8.0.0-m86-ubuntu-jdk17","8.0.0-m86-jdk11","8.0.0-m86-ubuntu-jdk11","8.0.0-m86-jdk17","8.0.0-m82-ubuntu-jdk11","8.0.0-m82-jdk11","8.0.0-m82","8.0.0-m82-ubuntu-jdk17","8.0.0-m82-jdk17","8.0.0-m76-ubuntu-jdk17","8.0.0-m76-ubuntu-jdk11","8.0.0-m76-jdk17","8.0.0-m76","8.0.0-m76-jdk11","8.0.0-m69","8.0.0-m69-ubuntu-jdk11","8.0.0-m69-jdk17","8.0.0-m69-ubuntu-jdk17","8.0.0-m69-jdk11","8.0.0-m60","8.0.0-m60-jdk17","8.0.0-m60-jdk11","8.0.0-m60-ubuntu-jdk17","8.0.0-m60-ubuntu-jdk11","7.20.0-rc1-ubuntu-jdk11","7.20.0-rc1-jdk11","7.20.0-rc1","8.0.0-struts-m48-ubuntu-jdk17","8.0.0-struts-m48-ubuntu-jdk11","8.0.0-struts-m48-jdk11","8.0.0-struts-m48","8.0.0-struts-m48-jdk17","7.20.0-beta4-jdk11","7.20.0-beta4-ubuntu-jdk11","7.20.0-beta4","8.0.0-m45-ubuntu-jdk11","8.0.0-m45-jdk17","8.0.0-m45-jdk11","8.0.0-m45-ubuntu-jdk17","8.0.0-m45","8.0.0-struts-m39-ubuntu-jdk11","8.0.0-struts-m39-ubuntu-jdk17","8.0.0-struts-m39-jdk11","8.0.0-struts-m39-jdk17","8.0.0-struts-m39","8.0.0-struts-m027-ubuntu-jdk11","8.0.0-struts-m027-ubuntu-jdk17","8.0.0-struts-m027","8.0.0-struts-m027-jdk17","8.0.0-struts-m027-jdk11","7.20.0-beta2-ubuntu-jdk11","7.20.0-beta2-jdk11","7.20.0-beta2-jdk17","7.20.0-beta2","7.20.0-beta2-ubuntu-jdk17","8.0.0-m026-ubuntu-jdk11","8.0.0-m026-ubuntu-jdk17","8.0.0-m026","8.0.0-m026-jdk17","8.0.0-m026-jdk11","8.0.0-struts-m020-ubuntu-jdk17","8.0.0-m023-jdk17","8.0.0-m023-ubuntu-jdk17","8.0.0-struts-m020-jdk17","7.20.0-m104-jdk17","7.20.0-m104-ubuntu-jdk17","8.0.0-m023-jdk11","7.20.0-m104","8.0.0-m023-ubuntu-jdk11","7.20.0-m104-ubuntu-jdk11","8.0.0-m023","7.20.0-m104-jdk11","8.0.0-m021-ubuntu-jdk11","8.0.0-m021","8.0.0-m021-jdk11","7.20.0-m99","7.20.0-m99-ubuntu-jdk11","7.20.0-m99-jdk11","8.0.0-struts-m020-jdk11","8.0.0-struts-m020-ubuntu-jdk11","8.0.0-struts-m020","8.0.0-m020-jdk11","8.0.0-m020","8.0.0-m020-ubuntu-jdk11","7.20.0-m78-ubuntu-jdk11","7.20.0-m78","7.20.0-m78-jdk11","7.19.0-rc3-jdk11","7.19.0-rc3-ubuntu-jdk11","7.19.0-rc3","8.0.0-m018-ubuntu-jdk11","8.0.0-m018-jdk11","8.0.0-m018","8.0.0-m017-ubuntu-jdk11","8.0.0-m017-jdk11","8.0.0-m017","7.19.0-rc2-ubuntu-jdk11","7.19.0-rc2-jdk11","7.19.0-rc2","8.0.0-m015-ubuntu-jdk11","8.0.0-m015","8.0.0-m015-jdk11","7.19.0-rc1-jdk11","7.19.0-rc1","7.19.0-rc1-ubuntu-jdk11","7.19.0-beta1-jdk11","7.19.0-beta1-ubuntu-jdk11","7.19.0-beta1","8.0.0-m014","8.0.0-m014-ubuntu-jdk11","8.0.0-m014-jdk11","7.17-ubuntu-18.04-adoptopenjdk11","7.17-ubuntu","7.17-adoptopenjdk11","7.17.5-ubuntu-18.04-adoptopenjdk11","7.17.5-ubuntu","7.17.5-adoptopenjdk11","eap-ubuntu-18.04-adoptopenjdk11","eap-ubuntu","8.0.0-m012-ubuntu-18.04-adoptopenjdk11","eap-adoptopenjdk11","8.0.0-m012-ubuntu","8.0.0-m012-adoptopenjdk11","8.0.0-m012","8.0.0-m012-jdk11","7.19.0-m02-jdk11","7.19.0-m02-ubuntu-18.04-adoptopenjdk11","7.19.0-m02-adoptopenjdk11","7.19.0-m02-ubuntu","7.19.0-m02","ubuntu-18.04-adoptopenjdk11","ubuntu","adoptopenjdk11","7-ubuntu-18.04-adoptopenjdk11","7-ubuntu","7-adoptopenjdk11","7.9.1-ubuntu-18.04-adoptopenjdk11","7.9.1-ubuntu","7.9.1-adoptopenjdk11","7.9.0-ubuntu-18.04-adoptopenjdk11","7.9.0-ubuntu","7.9.0-adoptopenjdk11","7.8-ubuntu-18.04-adoptopenjdk11","7.8-ubuntu","7.8-adoptopenjdk11","7.8.3-ubuntu-18.04-adoptopenjdk11","7.8.3-ubuntu","7.8.3-adoptopenjdk11","7.8.1-ubuntu-18.04-adoptopenjdk11","7.8.1-ubuntu","7.8.1-adoptopenjdk11","7.8.0-ubuntu-18.04-adoptopenjdk11","7.8.0-ubuntu","7.8.0-adoptopenjdk11","7.7-ubuntu-18.04-adoptopenjdk11","7.7-ubuntu","7.7-adoptopenjdk11","7.7.4-ubuntu-18.04-adoptopenjdk11","7.7.4-ubuntu","7.7.4-adoptopenjdk11","7.7.3-ubuntu-18.04-adoptopenjdk11","7.7.3-ubuntu","7.7.3-adoptopenjdk11","7.7.2-ubuntu-18.04-adoptopenjdk11","7.7.2-ubuntu","7.7.2-adoptopenjdk11","7.6-ubuntu-18.04-adoptopenjdk11","7.6-ubuntu","7.6-adoptopenjdk11","7.6.3-ubuntu-18.04-adoptopenjdk11","7.6.3-ubuntu","7.6.3-adoptopenjdk11","7.6.0-ubuntu-18.04-adoptopenjdk11","7.6.0-ubuntu","7.6.0-adoptopenjdk11","7.5-ubuntu-18.04-adoptopenjdk11","7.5-ubuntu","7.5-adoptopenjdk11","7.5.2-ubuntu-18.04-adoptopenjdk11","7.5.2-ubuntu","7.5.2-adoptopenjdk11","7.5.1-ubuntu-18.04-adoptopenjdk11","7.5.1-ubuntu","7.5.1-adoptopenjdk11","7.5.0-ubuntu-18.04-adoptopenjdk11","7.5.0-ubuntu","7.5.0-adoptopenjdk11","7.4-ubuntu-18.04-adoptopenjdk11","7.4-ubuntu","7.4-adoptopenjdk11","7.4.9-ubuntu-18.04-adoptopenjdk11","7.4.9-ubuntu","7.4.9-adoptopenjdk11","7.4.8-ubuntu-18.04-adoptopenjdk11","7.4.8-ubuntu","7.4.8-adoptopenjdk11","7.4.7-ubuntu-18.04-adoptopenjdk11","7.4.7-ubuntu","7.4.7-adoptopenjdk11","7.4.6-ubuntu-18.04-adoptopenjdk11","7.4.6-ubuntu","7.4.6-adoptopenjdk11","7.4.3-ubuntu-18.04-adoptopenjdk11","7.4.3-ubuntu","7.4.3-adoptopenjdk11","7.4.1-ubuntu","7.4.1-adoptopenjdk11","7.4.17-ubuntu-18.04-adoptopenjdk11","7.4.17-ubuntu","7.4.17-adoptopenjdk11","7.4.16-ubuntu-18.04-adoptopenjdk11","7.4.16-ubuntu","7.4.16-adoptopenjdk11","7.4.15-ubuntu-18.04-adoptopenjdk11","7.4.15-ubuntu","7.4.15-adoptopenjdk11","7.4.14-ubuntu-18.04-adoptopenjdk11","7.4.14-ubuntu","7.4.14-adoptopenjdk11","7.4.13-ubuntu-18.04-adoptopenjdk11","7.4.13-ubuntu","7.4.13-adoptopenjdk11","7.4.12-ubuntu-18.04-adoptopenjdk11","7.4.12-ubuntu","7.4.12-adoptopenjdk11","7.4.0-ubuntu-18.04-adoptopenjdk11","7.4.0-ubuntu","7.4.0-adoptopenjdk11","7.18-ubuntu-18.04-adoptopenjdk11","7.18-ubuntu","7.18-adoptopenjdk11","7.18.1-ubuntu-18.04-adoptopenjdk11","7.18.1-ubuntu","7.18.1-adoptopenjdk11","7.18.0-ubuntu-18.04-adoptopenjdk11","7.18.0-ubuntu","7.18.0-adoptopenjdk11","7.17.4-ubuntu-18.04-adoptopenjdk11","7.17.4-ubuntu","7.17.4-adoptopenjdk11","7.17.3-ubuntu-18.04-adoptopenjdk11","7.17.3-ubuntu","7.17.3-adoptopenjdk11","7.17.2-ubuntu-18.04-adoptopenjdk11","7.17.2-ubuntu","7.17.2-adoptopenjdk11","7.16-ubuntu-18.04-adoptopenjdk11","7.16-ubuntu","7.16-adoptopenjdk11","7.16.4-ubuntu-18.04-adoptopenjdk11","7.16.4-ubuntu","7.16.4-adoptopenjdk11","7.16.3-ubuntu-18.04-adoptopenjdk11","7.16.3-ubuntu","7.16.3-adoptopenjdk11","7.16.2-ubuntu-18.04-adoptopenjdk11","7.16.2-ubuntu","7.16.2-adoptopenjdk11","7.16.1-ubuntu-18.04-adoptopenjdk11","7.16.1-ubuntu","7.16.1-adoptopenjdk11","7.16.0-ubuntu-18.04-adoptopenjdk11","7.16.0-ubuntu","7.16.0-adoptopenjdk11","7.15-ubuntu-18.04-adoptopenjdk11","7.15-ubuntu","7.15-adoptopenjdk11","7.15.2-ubuntu-18.04-adoptopenjdk11","7.15.2-ubuntu","7.15.2-adoptopenjdk11","7.15.1-ubuntu-18.04-adoptopenjdk11","7.15.1-ubuntu","7.15.1-adoptopenjdk11","7.14-ubuntu-18.04-adoptopenjdk11","7.14.3-ubuntu-18.04-adoptopenjdk11","7.14.3-ubuntu","7.14.0-ubuntu","7.14.0-adoptopenjdk11","7.13-ubuntu-18.04-adoptopenjdk11","7.13-ubuntu","7.13-adoptopenjdk11","7.13.7-ubuntu-18.04-adoptopenjdk11","7.13.7-ubuntu","7.13.7-adoptopenjdk11","7.13.6-ubuntu-18.04-adoptopenjdk11","7.13.6-ubuntu","7.13.6-adoptopenjdk11","7.13.5-ubuntu-18.04-adoptopenjdk11","7.13.5-ubuntu","7.13.5-adoptopenjdk11","7.13.4-ubuntu-18.04-adoptopenjdk11","7.13.4-ubuntu","7.13.4-adoptopenjdk11","7.12.5-ubuntu-18.04-adoptopenjdk11","7.12.5-ubuntu","7.12.4-ubuntu-18.04-adoptopenjdk11","7.12.4-ubuntu","7.12.4-adoptopenjdk11","7.12.3-ubuntu-18.04-adoptopenjdk11","7.12.3-ubuntu","7.12.3-adoptopenjdk11","7.12.2-ubuntu-18.04-adoptopenjdk11","7.12.2-ubuntu","7.12.2-adoptopenjdk11","7.12.1-ubuntu-18.04-adoptopenjdk11","7.12.1-ubuntu","7.12.1-adoptopenjdk11","7.11.1-ubuntu-18.04-adoptopenjdk11","7.11.1-ubuntu","7.11.1-adoptopenjdk11","7.11.0-ubuntu-18.04-adoptopenjdk11","7.11.0-ubuntu","7.11.0-adoptopenjdk11","7.10-ubuntu-18.04-adoptopenjdk11","7.10-ubuntu","7.10-adoptopenjdk11","7.10.2-ubuntu-18.04-adoptopenjdk11","7.10.2-ubuntu","7.10.2-adoptopenjdk11","7.10.1-ubuntu-18.04-adoptopenjdk11","7.10.1-ubuntu","7.10.1-adoptopenjdk11","7.10.0-ubuntu-18.04-adoptopenjdk11","7.10.0-ubuntu","7.10.0-adoptopenjdk11","7.14-adoptopenjdk11","7.9-ubuntu-18.04-adoptopenjdk11","7.9-ubuntu","7.9-adoptopenjdk11","7.14.3-adoptopenjdk11","7.9.3-ubuntu-18.04-adoptopenjdk11","7.9.3-ubuntu","7.9.3-adoptopenjdk11","7.14.2-ubuntu-18.04-adoptopenjdk11","7.6.2-ubuntu-18.04-adoptopenjdk11","7.6.2-ubuntu","7.14.2-ubuntu","7.6.2-adoptopenjdk11","7.6.1-ubuntu-18.04-adoptopenjdk11","7.6.1-ubuntu","7.14.2-adoptopenjdk11","7.6.1-adoptopenjdk11","7.14.1-ubuntu-18.04-adoptopenjdk11","7.4.5-ubuntu-18.04-adoptopenjdk11","7.4.5-ubuntu","7.14.1-ubuntu","7.4.5-adoptopenjdk11","7.4.4-ubuntu-18.04-adoptopenjdk11","7.14.1-adoptopenjdk11","7.4.4-ubuntu","7.4.4-adoptopenjdk11","7.14.0-ubuntu-18.04-adoptopenjdk11","7.4.1-ubuntu-18.04-adoptopenjdk11","7.13.3-ubuntu-18.04-adoptopenjdk11","7.4.11-ubuntu-18.04-adoptopenjdk11","7.13.3-ubuntu","7.4.11-ubuntu","7.13.3-adoptopenjdk11","7.4.11-adoptopenjdk11","7.13.2-ubuntu-18.04-adoptopenjdk11","7.4.10-ubuntu-18.04-adoptopenjdk11","7.13.2-ubuntu","7.4.10-ubuntu","7.13.2-adoptopenjdk11","7.4.10-adoptopenjdk11","7.13.1-ubuntu-18.04-adoptopenjdk11","7.17.1-ubuntu-18.04-adoptopenjdk11","7.13.1-ubuntu","7.17.1-ubuntu","7.13.1-adoptopenjdk11","7.17.1-adoptopenjdk11","7.13.0-ubuntu-18.04-adoptopenjdk11","7.13.0-ubuntu","7.17.0-ubuntu-18.04-adoptopenjdk11","7.17.0-ubuntu","7.13.0-adoptopenjdk11","7.17.0-adoptopenjdk11","7.12-ubuntu-18.04-adoptopenjdk11","7.12-ubuntu","7.12-adoptopenjdk11","7.15.0-ubuntu-18.04-adoptopenjdk11","7.12.5-adoptopenjdk11","7.15.0-ubuntu","7.12.0-ubuntu-18.04-adoptopenjdk11","7.15.0-adoptopenjdk11","7.12.0-ubuntu","7.11-ubuntu-18.04-adoptopenjdk11","7.14-ubuntu","7.11-ubuntu","7.11.6-ubuntu-18.04-adoptopenjdk11","7.11.6-ubuntu","7.11.6-adoptopenjdk11","7.11.3-ubuntu-18.04-adoptopenjdk11","7.11.3-ubuntu","7.11.3-adoptopenjdk11","7.11.2-ubuntu-18.04-adoptopenjdk11","7.11.2-ubuntu","7.11.2-adoptopenjdk11","7.12.0-adoptopenjdk11","7.11-adoptopenjdk11","8.0.0-m011-jdk11","8.0.0-m011","8.0.0-m011-ubuntu-18.04-adoptopenjdk11","8.0.0-m011-ubuntu","7.19.0-m01-ubuntu-18.04-adoptopenjdk11","7.19.0-m01-jdk11","7.19.0-m01-adoptopenjdk11","8.0.0-m011-adoptopenjdk11","7.19.0-m01","7.19.0-m01-ubuntu","7.18.0-rc1-ubuntu-18.04-adoptopenjdk11","7.18.0-rc1-ubuntu","7.18.0-rc1-jdk11","7.18.0-rc1-adoptopenjdk11","7.18.0-rc1","7.18.0-beta1-ubuntu-18.04-adoptopenjdk11","7.18.0-beta1-ubuntu","7.18.0-beta1-jdk11","7.18.0-beta1-adoptopenjdk11","7.18.0-beta1","7.18.0-m68-jdk11","7.18.0-m68","7.18.0-m68-ubuntu-18.04-adoptopenjdk11","7.18.0-m68-ubuntu","7.18.0-m68-adoptopenjdk11","7.18.0-m61-ubuntu","7.18.0-m61-adoptopenjdk11","7.18.0-m61","7.18.0-m61-ubuntu-18.04-adoptopenjdk11","7.18.0-m61-jdk11","7.18.0-m53-ubuntu-18.04-adoptopenjdk11","7.18.0-m53-ubuntu","7.18.0-m53","7.18.0-m53-jdk11","7.18.0-m53-adoptopenjdk11","7.18.0-m46-ubuntu","7.18.0-m46-adoptopenjdk11","7.18.0-m46","7.18.0-m46-ubuntu-18.04-adoptopenjdk11","7.18.0-m46-jdk11","7.18.0-m40-ubuntu-18.04-adoptopenjdk11","7.18.0-m40-ubuntu","7.18.0-m40-jdk11","7.18.0-m40-adoptopenjdk11","7.18.0-m40","temurin-7.17.0","7.17.0-rc1-ubuntu-18.04-adoptopenjdk11","7.17.0-rc1-ubuntu","7.17.0-rc1-jdk11","7.17.0-rc1-adoptopenjdk11","7.17.0-rc1","7.4.6-ubuntu-jdk-11","7.4.5-ubuntu-jdk-11","7.4.4-ubuntu-jdk-11","7.4.3-ubuntu-jdk-11","7.4.1-ubuntu-jdk-11","7.4.0-ubuntu-jdk-11","7.3.2","7.3.2-jdk11","7.3.2-adoptopenjdk11","7.3.2-ubuntu-18.04-adoptopenjdk11","7.3.2-ubuntu","7.3.3-ubuntu-18.04-adoptopenjdk11","7.3.3-adoptopenjdk11","7.3.3-jdk11","7.3.3","7.3.3-ubuntu","7.3.4-jdk11","7.3.4-ubuntu-18.04-adoptopenjdk11","7.3.4-adoptopenjdk11","7.3.4-ubuntu","7.3.4","7.3.5-ubuntu","7.3-adoptopenjdk11","7.3-jdk11","7.3.5-ubuntu-18.04-adoptopenjdk11","7.3.5","7.3","7.3.5-adoptopenjdk11","7.3-ubuntu-18.04-adoptopenjdk11","7.3.5-jdk11","7.3-ubuntu","7.3.1-jdk11","7.3.1-ubuntu-18.04-adoptopenjdk11","7.3.1-adoptopenjdk11","7.3.1","7.3.1-ubuntu","7.17.0-beta1-jdk11","7.17.0-beta1-adoptopenjdk11","7.17.0-beta1","7.17.0-beta1-ubuntu-18.04-adoptopenjdk11","7.17.0-beta1-ubuntu","7.17.0-m61-adoptopenjdk11","7.17.0-m61","7.17.0-m61-ubuntu","7.17.0-m61-jdk11","7.17.0-m61-ubuntu-18.04-adoptopenjdk11","7.17.0-m59-ubuntu","7.17.0-m59-adoptopenjdk11","7.17.0-m59","7.17.0-m59-jdk11","7.17.0-m59-ubuntu-18.04-adoptopenjdk11","7.17.0-m47-jdk11","7.17.0-m47","7.17.0-m47-ubuntu","7.17.0-m47-adoptopenjdk11","7.17.0-m47-ubuntu-18.04-adoptopenjdk11","7.16.0-rc1","7.16.0-rc1-ubuntu-18.04-adoptopenjdk11","7.16.0-rc1-ubuntu","7.16.0-rc1-adoptopenjdk11","7.16.0-rc1-jdk11","7.2.0-adoptopenjdk11","7.2.0-ubuntu-18.04-adoptopenjdk11","7.2.0","7.2.0-jdk11","7.2.0-ubuntu","7.2.1","7.2.1-jdk11","7.2.1-ubuntu","7.2.1-adoptopenjdk11","7.2.1-ubuntu-18.04-adoptopenjdk11","7.2.2","7.2.2-adoptopenjdk11","7.2-jdk11","7.2-adoptopenjdk11","7.2-ubuntu","7.2","7.2.2-jdk11","7.2.2-ubuntu","7.2.2-ubuntu-18.04-adoptopenjdk11","7.2-ubuntu-18.04-adoptopenjdk11","7.16.0-beta2","7.16.0-beta2-adoptopenjdk11","7.16.0-beta2-ubuntu-18.04-adoptopenjdk11","7.16.0-beta2-ubuntu","7.16.0-beta2-jdk11","7.16.0-m49-ubuntu-18.04-adoptopenjdk11","7.16.0-m49-jdk11","7.16.0-m49-adoptopenjdk11","7.16.0-m49","7.16.0-m49-ubuntu","7.16.0-m39","7.16.0-m39-adoptopenjdk11","7.16.0-m39-ubuntu-18.04-adoptopenjdk11","7.16.0-m39-ubuntu","7.16.0-m39-jdk11","7.16.0-m30-ubuntu","7.16.0-m30","7.16.0-m30-adoptopenjdk11","7.16.0-m30-jdk11","7.16.0-m30-ubuntu-18.04-adoptopenjdk11","7.15.0-rc1-adoptopenjdk11","7.15.0-rc1-ubuntu-18.04-adoptopenjdk11","7.15.0-rc1-ubuntu","7.15.0-rc1","7.15.0-rc1-jdk11","7.15.0-beta3-jdk11","7.15.0-beta3-adoptopenjdk11","7.15.0-beta3","7.15.0-beta3-ubuntu-18.04-adoptopenjdk11","7.15.0-beta3-ubuntu","7.15.0-beta2-adoptopenjdk11","7.15.0-beta2-jdk11","7.15.0-beta2-ubuntu","7.15.0-beta2-ubuntu-18.04-adoptopenjdk11","7.15.0-beta2","7.15.0-beta1-ubuntu-18.04-adoptopenjdk11","7.15.0-beta1","7.15.0-beta1-jdk11","7.15.0-beta1-ubuntu","7.15.0-beta1-adoptopenjdk11","7.15.0-m35-jdk11","7.15.0-m35-adoptopenjdk11","7.15.0-m35-ubuntu-18.04-adoptopenjdk11","7.15.0-m35-ubuntu","7.15.0-m35","7.15.0-m27","7.15.0-m27-ubuntu","7.15.0-m27-ubuntu-18.04-adoptopenjdk11","7.15.0-m27-jdk11","7.15.0-m27-adoptopenjdk11","7.15.0-m20-ubuntu","7.15.0-m20","7.15.0-m20-ubuntu-18.04-adoptopenjdk11","7.15.0-m20-jdk11","7.15.0-m20-adoptopenjdk11","7.14.0-rc1-jdk11","7.14.0-rc1","7.14.0-rc1-ubuntu","7.14.0-rc1-ubuntu-18.04-adoptopenjdk11","7.14.0-rc1-adoptopenjdk11","7.14.0-beta1-ubuntu","7.14.0-beta1-jdk11","7.14.0-beta1","7.14.0-beta1-ubuntu-18.04-adoptopenjdk11","7.14.0-beta1-adoptopenjdk11","7.14.0-m176-jdk11","7.14.0-m176-ubuntu-18.04-adoptopenjdk11","7.14.0-m176","7.14.0-m176-ubuntu","7.14.0-m176-adoptopenjdk11","7.13.0-rc1-jdk11","7.13.0-rc1","7.13.0-rc1-adoptopenjdk11","7.13.0-rc1-ubuntu","7.13.0-rc1-ubuntu-18.04-adoptopenjdk11","7.13.0-beta-jdk11","7.13.0-beta","7.13.0-beta-ubuntu","7.13.0-beta-ubuntu-18.04-adoptopenjdk11","7.13.0-beta-adoptopenjdk11","7.13.0-m17","7.13.0-m17-adoptopenjdk11","7.13.0-m17-jdk11","7.13.0-m17-ubuntu-18.04-adoptopenjdk11","7.13.0-m17-ubuntu","7.13.0-m16-adoptopenjdk11","7.13.0-m16","7.13.0-m16-ubuntu","7.13.0-m16-ubuntu-18.04-adoptopenjdk11","7.13.0-m16-jdk11","6.13.20-ubuntu","6.5.2-jdk8","6.14.0-jdk8","6.15.7-jdk8","6.3.2-jdk8","6.6.8-adoptopenjdk8","6.11.1","6.6.15-ubuntu","6.1.1","6.13.10","6.8.3-ubuntu-18.04-adoptopenjdk8","6.5.2-ubuntu-18.04-adoptopenjdk8","6.13.20-adoptopenjdk8","6.14.0-ubuntu","6.15.7-ubuntu","6.6.8-ubuntu-18.04-adoptopenjdk8","6.3.2-ubuntu-18.04-adoptopenjdk8","6.11.1-ubuntu","6.6.15-jdk8","6.1.1-jdk8","6.13.10-adoptopenjdk8","6.8.3-adoptopenjdk8","6.5.2-adoptopenjdk8","6.13.20-ubuntu-18.04-adoptopenjdk8","6.14.0-ubuntu-18.04-adoptopenjdk8","6.15.7-adoptopenjdk8","6.6.8-ubuntu","6.3.2-adoptopenjdk8","6.6.15-ubuntu-18.04-adoptopenjdk8","6.8.3","6.13.3-ubuntu-18.04-adoptopenjdk8","6.13.3-adoptopenjdk8","6.6.0","6.13.3-ubuntu","6.6.0-ubuntu-18.04-adoptopenjdk8","6.13.3-jdk8","6.6.0-adoptopenjdk8","6.13.3","6.6.0-jdk8","6.6.0-ubuntu","6.13.4-ubuntu","6.13.4-jdk8","6.6.2","6.6.1-jdk8","6.13.4","6.6.2-adoptopenjdk8","6.6.1-ubuntu-18.04-adoptopenjdk8","6.13.4-ubuntu-18.04-adoptopenjdk8","6.6.2-ubuntu-18.04-adoptopenjdk8","6.6.1-ubuntu","6.13.4-adoptopenjdk8","6.6.2-ubuntu","6.6.1","6.6.2-jdk8","6.6.1-adoptopenjdk8","6.13.5-ubuntu-18.04-adoptopenjdk8","6.13.5-jdk8","6.6.3-ubuntu-18.04-adoptopenjdk8","6.6.10-jdk8","6.13.5","6.6.3-jdk8","6.6.10","6.13.5-adoptopenjdk8","6.6.3-adoptopenjdk8","6.6.10-ubuntu-18.04-adoptopenjdk8","6.13.5-ubuntu","6.6.3-ubuntu","6.6.10-ubuntu","6.6.3","6.6.10-adoptopenjdk8","6.13.6-ubuntu-18.04-adoptopenjdk8","6.13.6-ubuntu","6.6.4-jdk8","6.6.11","6.13.6-jdk8","6.6.4-adoptopenjdk8","6.6.11-adoptopenjdk8","6.13.6-adoptopenjdk8","6.6.11-jdk8","6.6.4-ubuntu-18.04-adoptopenjdk8","6.13.6","6.6.11-ubuntu","6.6.4","6.6.11-ubuntu-18.04-adoptopenjdk8","6.6.4-ubuntu","6.13.7-ubuntu-18.04-adoptopenjdk8","6.15.2-ubuntu","6.8.0-ubuntu","6.13.7-adoptopenjdk8","6.15.2-adoptopenjdk8","6.6.5-ubuntu-18.04-adoptopenjdk8","6.8.0-adoptopenjdk8","6.6.12-ubuntu-18.04-adoptopenjdk8","6.13.7-ubuntu","6.15.2","6.6.5-ubuntu","6.8.0-ubuntu-18.04-adoptopenjdk8","6.6.12-jdk8","6.13.7","6.15.2-jdk8","6.6.5","6.8.0-jdk8","6.6.12","6.13.7-jdk8","6.15.2-ubuntu-18.04-adoptopenjdk8","6.6.5-jdk8","6.8.0","6.6.12-ubuntu","6.6.5-adoptopenjdk8","6.6.12-adoptopenjdk8","6.13.0-jdk8","6.13.8-ubuntu","6.8.1","6.15.4-ubuntu","6.5.0-ubuntu","6.13.0-adoptopenjdk8","6.13.8","6.8.1-ubuntu-18.04-adoptopenjdk8","6.15.4-adoptopenjdk8","6.6.6-ubuntu","6.6.13-adoptopenjdk8","6.5.0-jdk8","6.13.0-ubuntu","6.13.8-jdk8","6.15.4","6.6.6-ubuntu-18.04-adoptopenjdk8","6.8.1-ubuntu","6.6.13","6.5.0-adoptopenjdk8","6.13.0","6.13.8-adoptopenjdk8","6.6.13-jdk8","6.8.1-jdk8","6.15.4-jdk8","6.6.6-jdk8","6.5.0","6.13.0-ubuntu-18.04-adoptopenjdk8","6.13.8-ubuntu-18.04-adoptopenjdk8","6.6.13-ubuntu-18.04-adoptopenjdk8","6.6.6","6.15.4-ubuntu-18.04-adoptopenjdk8","6.8.1-adoptopenjdk8","6.5.0-ubuntu-18.04-adoptopenjdk8","6.6.13-ubuntu","6.6.6-adoptopenjdk8","6.13.1-ubuntu","6.15.6-ubuntu-18.04-adoptopenjdk8","6.11.0-adoptopenjdk8","6.13.9-jdk8","6.1.0-ubuntu-18.04-adoptopenjdk8","6.5.1-ubuntu-18.04-adoptopenjdk8","6.3.1-jdk8","6.8.2-jdk8","6.13.1-jdk8","6.6.7-ubuntu","6.15.6-ubuntu","6.11.0-ubuntu-18.04-adoptopenjdk8","6.6.14-adoptopenjdk8","6.1.0-adoptopenjdk8","6.13.9","6.5.1-adoptopenjdk8","6.3.1","6.8.2-adoptopenjdk8","6.13.1-adoptopenjdk8","6.6.7-ubuntu-18.04-adoptopenjdk8","6.15.6-jdk8","6.6.14-ubuntu","6.11.0-ubuntu","6.13.9-ubuntu","6.1.0-ubuntu","6.5.1-ubuntu","6.3.1-ubuntu","6.8.2-ubuntu-18.04-adoptopenjdk8","6.13.1-ubuntu-18.04-adoptopenjdk8","6.6.7-adoptopenjdk8","6.15.6-adoptopenjdk8","6.6.14-jdk8","6.13.9-adoptopenjdk8","6.11.0-jdk8","6.1.0-jdk8","6.3.1-ubuntu-18.04-adoptopenjdk8","6.5.1","6.8.2","6.13.1","6.6.7-jdk8","6.15.6","6.6.14-ubuntu-18.04-adoptopenjdk8","6.13.9-ubuntu-18.04-adoptopenjdk8","6.1.0","6.11.0","6.3.1-adoptopenjdk8","6.5.1-jdk8","6.8.2-ubuntu","6.6.7","6.6.14","6.11.1-adoptopenjdk8","6.1.1-adoptopenjdk8","6.5.2-ubuntu","6.8.3-jdk8","6.13.10-ubuntu","6.6.8-jdk8","6.15.7-ubuntu-18.04-adoptopenjdk8","6.14.0-adoptopenjdk8","6.1.1-ubuntu-18.04-adoptopenjdk8","6.11.1-jdk8","6.8.3-ubuntu","6.6.15","6.15.7","6.14.0","6.1.1-ubuntu","6.3.2","6.5.2","6.13.10-ubuntu-18.04-adoptopenjdk8","6.6.8","6.3.2-ubuntu","6.13.10-jdk8","6.11.1-ubuntu-18.04-adoptopenjdk8","6.6.15-adoptopenjdk8","6.13.11-ubuntu-18.04-adoptopenjdk8","6.13.11-ubuntu","6.13.11-jdk8","6.13.11","6.13.11-adoptopenjdk8","6.13.12","6.13.12-jdk8","6.13.12-ubuntu-18.04-adoptopenjdk8","6.13.12-ubuntu","6.13.12-adoptopenjdk8","6.13.13-jdk8","6.13.13-ubuntu","6.13.13","6.13.13-ubuntu-18.04-adoptopenjdk8","6.13.13-adoptopenjdk8","6.13.15-ubuntu","6.13.15-adoptopenjdk8","6.13.15","6.13.15-jdk8","6.13.15-ubuntu-18.04-adoptopenjdk8","6.13.17","6.13.17-adoptopenjdk8","6.13.17-jdk8","6.13.17-ubuntu-18.04-adoptopenjdk8","6.13.17-ubuntu","6.13.18","6.13.18-ubuntu","6.13.18-jdk8","6.13.18-ubuntu-18.04-adoptopenjdk8","6.13.18-adoptopenjdk8","6.13.19-jdk8","6.13.19-ubuntu-18.04-adoptopenjdk8","6.13.19","6.13.19-adoptopenjdk8","6.13.19-ubuntu","6.13.2-adoptopenjdk8","6.13.2-jdk8","6.13.2","6.13.2-ubuntu","6.13.2-ubuntu-18.04-adoptopenjdk8","6.13.20-jdk8","6.13.20","7.13.0-m15-adoptopenjdk11","7.13.0-m15-ubuntu","7.13.0-m15-jdk11","7.13.0-m15","7.13.0-m14-jdk11","7.13.0-m14-ubuntu-18.04-adoptopenjdk11","7.13.0-m14-adoptopenjdk11","7.13.0-m14-ubuntu","7.13.0-m14","7.1.0-ubuntu","7.1.0-jdk11","7.1.0-ubuntu-18.04-adoptopenjdk11","7.1.0","7.1.0-adoptopenjdk11","7.1.1-ubuntu","7.1.1-adoptopenjdk11","7.1.1-ubuntu-18.04-adoptopenjdk11","7.1.1-jdk11","7.1.1","7.13.0-m13-adoptopenjdk11","7.13.0-m13","7.13.0-m13-ubuntu-18.04-adoptopenjdk11","7.13.0-m13-ubuntu","7.13.0-m13-jdk11","7.1.2-ubuntu-18.04-adoptopenjdk11","6.4.2-jdk8","6.7.3-adoptopenjdk8","6.12.3-jdk8","6.15.10","6.10.1","6.0.6-jdk8","6.4.2-adoptopenjdk8","6.7-jdk8","6.15.10-adoptopenjdk8","6.2.3","6.0.6-ubuntu-18.04-adoptopenjdk8","6.7-adoptopenjdk8","7.0.2-ubuntu","6-jdk8","6.2.3-jdk8","6.0.6-adoptopenjdk8","7.0.2-jdk8","6.7","6.12.4-jdk8","6","6.2.3-ubuntu","6.10.2-ubuntu","6.0.6","6.4-adoptopenjdk8","7.0.2","6.7-ubuntu-18.04-adoptopenjdk8","6.12-ubuntu","6.15-ubuntu","6.2.3-ubuntu-18.04-adoptopenjdk8","6.10.2-adoptopenjdk8","6.0.6-ubuntu","6.4","7.0.2-ubuntu-18.04-adoptopenjdk8","6.12.4-ubuntu","6.7.3-ubuntu","6.2.3-adoptopenjdk8","6.10.2","6.4-ubuntu","7.0.2-adoptopenjdk8","6.12-adoptopenjdk8","6.10.2-ubuntu-18.04-adoptopenjdk8","6.4.3","6.12.4-adoptopenjdk8","6.0-jdk8","6.4.3-ubuntu-18.04-adoptopenjdk8","6.10.2-jdk8","6.12.4","6.2.4-jdk8","6.4.3-jdk8","6.0-ubuntu","6.12.4-ubuntu-18.04-adoptopenjdk8","7.0.3-adoptopenjdk8","6.2.4","6.0","6.4.3-adoptopenjdk8","6.12","7.0.3-jdk8","6.2.4-ubuntu","6.0.7-jdk8","6.10-adoptopenjdk8","6.4-ubuntu-18.04-adoptopenjdk8","6.12-jdk8","7.0.3-ubuntu-18.04-adoptopenjdk8","6.10.3","6.0.7-ubuntu-18.04-adoptopenjdk8","6.2-jdk8","6.4-jdk8","6.12-ubuntu-18.04-adoptopenjdk8","6.0-adoptopenjdk8","6.2","6.10.3-ubuntu","7.0.3","6.4.3-ubuntu","6.0-ubuntu-18.04-adoptopenjdk8","6.2-ubuntu-18.04-adoptopenjdk8","6.10.3-jdk8","7.0.3-ubuntu","6.0.7-adoptopenjdk8","6.2-adoptopenjdk8","6.10.3-ubuntu-18.04-adoptopenjdk8","6.0.7","6.10-ubuntu-18.04-adoptopenjdk8","6.2.4-ubuntu-18.04-adoptopenjdk8","6.0.7-ubuntu","6.10","6.2.4-adoptopenjdk8","7.0.4-jdk8","6.10.3-adoptopenjdk8","6.2-ubuntu","7.0.4","6.10-ubuntu","7.0.4-ubuntu-18.04-adoptopenjdk8","6.10-jdk8","7.0.4-adoptopenjdk8","7.0.4-ubuntu","7.0.5-adoptopenjdk8","7.0-adoptopenjdk8","7.0.5-ubuntu","7.0-ubuntu","7.0.5-jdk8","7.0.5-ubuntu-18.04-adoptopenjdk8","7.0","7.0-ubuntu-18.04-adoptopenjdk8","7.0-jdk8","7.0.5","7.13.0-m12-jdk11","7.13.0-m12-ubuntu-18.04-adoptopenjdk11","7.13.0-m12","7.13.0-m12-ubuntu","7.13.0-m12-adoptopenjdk11","6.14.1-adoptopenjdk8","6.14.1-ubuntu-18.04-adoptopenjdk8","6.14.1-ubuntu","6.8-ubuntu","6.14.1","6.8-adoptopenjdk8","6.14.1-jdk8","6.8.5-jdk8","6.8.5","6.8.5-ubuntu","6.8-ubuntu-18.04-adoptopenjdk8","6.1.2-adoptopenjdk8","6.14.2-ubuntu","6.8-jdk8","6.1.2-ubuntu","6.14.2-adoptopenjdk8","6.8","6.1.2","6.14.2","6.8.5-ubuntu-18.04-adoptopenjdk8","6.1.2-ubuntu-18.04-adoptopenjdk8","6.14.2-jdk8","6.8.5-adoptopenjdk8","6.1.2-jdk8","6.14.2-ubuntu-18.04-adoptopenjdk8","6.3.3-adoptopenjdk8","6.1.3-ubuntu","6.14-adoptopenjdk8","6.3.3-jdk8","6.1.3-ubuntu-18.04-adoptopenjdk8","6.3.3-ubuntu-18.04-adoptopenjdk8","6.14","6.1.3-jdk8","6.3.3-ubuntu","6.14-ubuntu-18.04-adoptopenjdk8","6.1.3","6.9.0-ubuntu-18.04-adoptopenjdk8","6.3.3","6.14.3-ubuntu-18.04-adoptopenjdk8","6.1.3-adoptopenjdk8","6.9.0-adoptopenjdk8","6.0.1-adoptopenjdk8","6.14-ubuntu","6.9.0-jdk8","6.0.1-ubuntu","6.14-jdk8","6.9.0","6.0.1","6.14.3-ubuntu","6.9.0-ubuntu","6.3.4-jdk8","6.0.1-ubuntu-18.04-adoptopenjdk8","6.1.4-ubuntu-18.04-adoptopenjdk8","6.14.3","6.3-adoptopenjdk8","6.6.16-ubuntu-18.04-adoptopenjdk8","6.6.9-jdk8","6.1-jdk8","6.0.1-jdk8","6.14.3-adoptopenjdk8","6.3.4","6.6.9-ubuntu-18.04-adoptopenjdk8","6.1.4","6.6.16-ubuntu","6.14.3-jdk8","6.3-ubuntu-18.04-adoptopenjdk8","6.6.9","6.1-adoptopenjdk8","6.6.16","6.9.1-adoptopenjdk8","6.6.9-adoptopenjdk8","6.1.4-ubuntu","6.3-jdk8","6.6.16-adoptopenjdk8","6.9.1-ubuntu","6.0.2-adoptopenjdk8","6.6.9-ubuntu","6.3.4-ubuntu","6.6.16-jdk8","6.1-ubuntu","6.0.2","6.9.1-ubuntu-18.04-adoptopenjdk8","6.3","6.0.2-ubuntu","6.1.4-jdk8","6.9.1-jdk8","6.3-ubuntu","6.0.2-jdk8","6.1-ubuntu-18.04-adoptopenjdk8","6.9.1","6.3.4-ubuntu-18.04-adoptopenjdk8","6.7.0-adoptopenjdk8","6.0.2-ubuntu-18.04-adoptopenjdk8","6.1.4-adoptopenjdk8","6.6","6.15.1-jdk8","6.3.4-adoptopenjdk8","6.7.0","6.1","6.6.17-ubuntu","6.15.1-adoptopenjdk8","6.7.0-ubuntu","6.6-jdk8","6.9.3-jdk8","6.15.1-ubuntu","6.7.0-jdk8","6.0.3-ubuntu-18.04-adoptopenjdk8","6.6.17","6.9.3","6.15.1-ubuntu-18.04-adoptopenjdk8","6.7.0-ubuntu-18.04-adoptopenjdk8","6.0.3","6.6.17-ubuntu-18.04-adoptopenjdk8","6.9-adoptopenjdk8","6.15.1","6.0.3-adoptopenjdk8","6.9-jdk8","6.6-ubuntu","6.0.3-jdk8","6.6.17-jdk8","6.9.3-adoptopenjdk8","6.0.3-ubuntu","6.4.0-adoptopenjdk8","6.7.1-jdk8","6.10.0-ubuntu-18.04-adoptopenjdk8","6.6-adoptopenjdk8","6.9-ubuntu-18.04-adoptopenjdk8","6.4.0","6.7.1-adoptopenjdk8","6.10.0-jdk8","6.6.17-adoptopenjdk8","6.9-ubuntu","6.4.0-ubuntu-18.04-adoptopenjdk8","6.7.1-ubuntu","6.15.10-ubuntu","6.10.0-adoptopenjdk8","6.9.3-ubuntu-18.04-adoptopenjdk8","6.6-ubuntu-18.04-adoptopenjdk8","6.4.0-ubuntu","6.7.1","6.15","6.10.0-ubuntu","6.9.3-ubuntu","6.0.4-jdk8","6.4.0-jdk8","6.7.1-ubuntu-18.04-adoptopenjdk8","6-adoptopenjdk8","6.10.0","6.9","6.0.4-adoptopenjdk8","6.0.4-ubuntu","6.15-jdk8","6.0.4-ubuntu-18.04-adoptopenjdk8","6.15.10-ubuntu-18.04-adoptopenjdk8","6.4.1-ubuntu","6.0.4","7.1-ubuntu-18.04-adoptopenjdk11","6.7.2-ubuntu-18.04-adoptopenjdk8","6.4.1-ubuntu-18.04-adoptopenjdk8","6-ubuntu-18.04-adoptopenjdk8","6.7.2-ubuntu","7.1.2","6.10.1-ubuntu-18.04-adoptopenjdk8","6.4.1-adoptopenjdk8","6.15.10-jdk8","6.7.2","6.10.1-ubuntu","7.1","6.4.1-jdk8","6.15-ubuntu-18.04-adoptopenjdk8","6.7.2-jdk8","6.10.1-adoptopenjdk8","6.0.5","7.1.2-ubuntu","7.0.1-jdk8","6.4.1","6.7.2-adoptopenjdk8","6.10.1-jdk8","6.0.5-adoptopenjdk8","7.1-ubuntu","7.0.1","6.0.5-ubuntu-18.04-adoptopenjdk8","7.1.2-jdk11","7.0.1-adoptopenjdk8","6-ubuntu","6.0.5-jdk8","7.0.1-ubuntu","6.15-adoptopenjdk8","6.4.2-ubuntu-18.04-adoptopenjdk8","6.0.5-ubuntu","7.1.2-adoptopenjdk11","7.0.1-ubuntu-18.04-adoptopenjdk8","6.7.3-ubuntu-18.04-adoptopenjdk8","7.1-adoptopenjdk11","7.1-jdk11","6.4.2","6.4.2-ubuntu","6.7.3","6.7.3-jdk8","6.7-ubuntu","6.13","6.13.21-adoptopenjdk8","6.13.21-ubuntu-18.04-adoptopenjdk8","6.13-ubuntu","6.13.21-jdk8","6.13-adoptopenjdk8","6.13-ubuntu-18.04-adoptopenjdk8","6.13.21-ubuntu","6.13-jdk8","6.13.21","6.12.2-jdk8","6.12.3-ubuntu-18.04-adoptopenjdk8","6.12.3-adoptopenjdk8","6.12.3","6.12.3-ubuntu","7.13.0-m09-jdk11","7.13.0-m09","7.13.0-m09-adoptopenjdk11","7.13.0-m09-ubuntu-18.04-adoptopenjdk11","7.13.0-m09-ubuntu","6.11.2-ubuntu","6.11-adoptopenjdk8","6.11.2-adoptopenjdk8","6.11.2-ubuntu-18.04-adoptopenjdk8","6.11.2","6.11-ubuntu-18.04-adoptopenjdk8","6.11.2-jdk8","6.11-jdk8","6.11","6.11-ubuntu","6.15.8-ubuntu-18.04-adoptopenjdk8","6.15.8-ubuntu","6.15.8-adoptopenjdk8","6.15.8","6.15.8-jdk8","6.12.0-jdk8","6.5.3-ubuntu","6.12.0","6.5.3-jdk8","6.5","6.15.9","6.5-ubuntu","6.15.9-ubuntu","6.5-jdk8","6.15.9-jdk8","6.5.3-adoptopenjdk8","6.15.9-adoptopenjdk8","6.5.3-ubuntu-18.04-adoptopenjdk8","6.15.9-ubuntu-18.04-adoptopenjdk8","6.5-ubuntu-18.04-adoptopenjdk8","6.5-adoptopenjdk8","6.12.0-ubuntu","6.5.3","6.12.0-ubuntu-18.04-adoptopenjdk8","6.2.0","6.12.0-adoptopenjdk8","6.2.0-jdk8","6.2.0-adoptopenjdk8","6.2.0-ubuntu-18.04-adoptopenjdk8","6.12.1-ubuntu-18.04-adoptopenjdk8","6.2.0-ubuntu","6.12.1","6.12.1-ubuntu","6.12.1-adoptopenjdk8","6.2.1-jdk8","6.12.1-jdk8","6.2.1-adoptopenjdk8","6.2.1-ubuntu","6.2.1","6.2.1-ubuntu-18.04-adoptopenjdk8","6.12.2-ubuntu","6.12.2","6.12.2-adoptopenjdk8","6.2.2-ubuntu","6.12.2-ubuntu-18.04-adoptopenjdk8","6.2.2-ubuntu-18.04-adoptopenjdk8","6.2.2-adoptopenjdk8","6.2.2","6.2.2-jdk8","7.13.0-m08","7.13.0-m08-adoptopenjdk11","7.13.0-m08-ubuntu-18.04-adoptopenjdk11","7.13.0-m08-jdk11","7.13.0-m08-ubuntu","6.15.2-alpine-adoptopenjdk8","6.15.2-alpine","6.13.0-alpine-adoptopenjdk8","6.13.0-alpine","6.15.4-alpine","6.15.4-alpine-adoptopenjdk8","6.13.1-alpine","6.13.1-alpine-adoptopenjdk8","6.15.6-alpine","6.15.6-alpine-adoptopenjdk8","6.13.10-alpine","6.13.10-alpine-adoptopenjdk8","6.15.7-alpine","6.15.7-alpine-adoptopenjdk8","6.13.11-alpine","6.0.1-alpine","6.15.8-alpine-adoptopenjdk8","6.13.11-alpine-adoptopenjdk8","6.0.1-alpine-adoptopenjdk8","6.15.8-alpine","6.6.9-alpine","6.13.12-alpine-adoptopenjdk8","6.13.3-alpine","6.0.2-alpine","6.12.0-alpine-adoptopenjdk8","6.15.9-alpine-adoptopenjdk8","6.13.12-alpine","6.7.0-alpine-adoptopenjdk8","6.6.0-alpine","6.13.3-alpine-adoptopenjdk8","6.0.2-alpine-adoptopenjdk8","6.12.0-alpine","6.15.9-alpine","6.6.0-alpine-adoptopenjdk8","6.7.0-alpine","6.13.13-alpine","6.0.3-alpine-adoptopenjdk8","6.13.4-alpine-adoptopenjdk8","6.2.0-alpine","6.12.1-alpine-adoptopenjdk8","6.6.2-alpine-adoptopenjdk8","6.6.1-alpine","6.7.1-alpine","6.13.13-alpine-adoptopenjdk8","6.0.3-alpine","6.2.0-alpine-adoptopenjdk8","6.12.1-alpine","6.13.4-alpine","6.6.2-alpine","6.6.1-alpine-adoptopenjdk8","6.7.1-alpine-adoptopenjdk8","6.10.0-alpine-adoptopenjdk8","6.13.15-alpine","6.0.4-alpine","6.13.5-alpine","6.2.1-alpine","6.6.3-alpine","6.12.2-alpine-adoptopenjdk8","6.15.1-alpine-adoptopenjdk8","6.7.2-alpine-adoptopenjdk8","6.6.10-alpine-adoptopenjdk8","6.10.0-alpine","6.13.15-alpine-adoptopenjdk8","6.0.4-alpine-adoptopenjdk8","6.13.5-alpine-adoptopenjdk8","6.2.1-alpine-adoptopenjdk8","6.6.3-alpine-adoptopenjdk8","6.12.2-alpine","6.15.1-alpine","6.7.2-alpine","6.6.10-alpine","7.13.0-m07-jdk11","7.13.0-m07-ubuntu-18.04-adoptopenjdk11","7.13.0-m07-ubuntu","7.13.0-m07-adoptopenjdk11","7.13.0-m07","6.6.4-alpine","6.6.4-alpine-adoptopenjdk8","6.6.5-alpine-adoptopenjdk8","6.6.5-alpine","6.6.6-alpine","6.6.6-alpine-adoptopenjdk8","6.6.7-alpine-adoptopenjdk8","6.6.7-alpine","6.6.8-alpine","6.6.8-alpine-adoptopenjdk8","6.6.9-alpine-adoptopenjdk8","6.4.0-alpine","6.4.0-alpine-adoptopenjdk8","6.4.1-alpine","6.4.1-alpine-adoptopenjdk8","7.13.0-m06-ubuntu-18.04-adoptopenjdk11","7.13.0-m06-adoptopenjdk11","7.13.0-m06","7.13.0-m06-ubuntu","7.13.0-m06-jdk11","6.7-alpine-adoptopenjdk8","6.7.3-alpine-adoptopenjdk8","6.7.3-alpine","6.10.1-alpine","6.7-alpine","6.2.2-alpine","6.10.1-alpine-adoptopenjdk8","6.2.2-alpine-adoptopenjdk8","6.4.2-alpine","6-alpine-adoptopenjdk8","6.4.2-alpine-adoptopenjdk8","6.10.2-alpine","6.15.10-alpine","6.13.17-alpine","6.2.3-alpine-adoptopenjdk8","6.13.6-alpine-adoptopenjdk8","6.12.3-alpine","6.10.2-alpine-adoptopenjdk8","6.15-alpine","6.13.17-alpine-adoptopenjdk8","6.8.0-alpine","6.13.6-alpine","6.2.3-alpine","6.4.3-alpine","6.12.3-alpine-adoptopenjdk8","6.8.0-alpine-adoptopenjdk8","6.15-alpine-adoptopenjdk8","6.6.11-alpine-adoptopenjdk8","6.4-alpine-adoptopenjdk8","6-alpine","6.6.11-alpine","6.10.3-alpine","6.4-alpine","6.13.18-alpine-adoptopenjdk8","6.15.10-alpine-adoptopenjdk8","6.12-alpine","6.2-alpine","6.13.7-alpine-adoptopenjdk8","6.10-alpine","6.4.3-alpine-adoptopenjdk8","6.8.1-alpine-adoptopenjdk8","6.13.18-alpine","6.12-alpine-adoptopenjdk8","6.13.7-alpine","6.10.3-alpine-adoptopenjdk8","6.2.4-alpine","6.8.1-alpine","6.6.12-alpine-adoptopenjdk8","6.12.4-alpine-adoptopenjdk8","6.10-alpine-adoptopenjdk8","6.2-alpine-adoptopenjdk8","6.6.12-alpine","6.12.4-alpine","6.13.19-alpine-adoptopenjdk8","6.2.4-alpine-adoptopenjdk8","6.13.8-alpine","6.8.2-alpine-adoptopenjdk8","6.13.19-alpine","6.13.8-alpine-adoptopenjdk8","6.5.0-alpine-adoptopenjdk8","6.8.2-alpine","6.6.13-alpine","6.5.0-alpine","6.6.13-alpine-adoptopenjdk8","6.13.2-alpine","6.11.0-alpine-adoptopenjdk8","6.13.9-alpine-adoptopenjdk8","6.8.3-alpine-adoptopenjdk8","6.13.2-alpine-adoptopenjdk8","6.11.0-alpine","6.3.1-alpine","6.13.9-alpine","6.5.1-alpine","6.8.3-alpine","6.6.14-alpine","6.3.1-alpine-adoptopenjdk8","6.6.14-alpine-adoptopenjdk8","6.5.1-alpine-adoptopenjdk8","6.13.20-alpine","6.11.1-alpine","6.14.0-alpine","6.8-alpine-adoptopenjdk8","6.11.1-alpine-adoptopenjdk8","6.13.20-alpine-adoptopenjdk8","6.3.2-alpine-adoptopenjdk8","6.14.0-alpine-adoptopenjdk8","6.6.15-alpine","6.5.2-alpine-adoptopenjdk8","6.8.5-alpine","6.3.2-alpine","6.6.15-alpine-adoptopenjdk8","6.8.5-alpine-adoptopenjdk8","6.5.2-alpine","6.13.21-alpine-adoptopenjdk8","6.11.2-alpine","6.8-alpine","6.14.1-alpine-adoptopenjdk8","6.13.21-alpine","6.3.3-alpine-adoptopenjdk8","6.11-alpine","6.6.16-alpine","6.14.1-alpine","6.5-alpine","6.13-alpine","6.3.3-alpine","6.11.2-alpine-adoptopenjdk8","6.6.16-alpine-adoptopenjdk8","6.5-alpine-adoptopenjdk8","6.13-alpine-adoptopenjdk8","6.11-alpine-adoptopenjdk8","6.5.3-alpine","6.14.2-alpine","6.3-alpine","6.5.3-alpine-adoptopenjdk8","6.6-alpine-adoptopenjdk8","6.9.0-alpine-adoptopenjdk8","6.14.2-alpine-adoptopenjdk8","6.3-alpine-adoptopenjdk8","6.6.17-alpine","6.9.0-alpine","6.3.4-alpine-adoptopenjdk8","6.6-alpine","6.6.17-alpine-adoptopenjdk8","6.3.4-alpine","6.14.3-alpine","6.9.1-alpine","6.14-alpine-adoptopenjdk8","6.9.1-alpine-adoptopenjdk8","6.1-alpine","6.14.3-alpine-adoptopenjdk8","6.14-alpine","6.9-alpine","6.9.3-alpine-adoptopenjdk8","6.9.3-alpine","6.9-alpine-adoptopenjdk8","6.1.3-alpine","6.1.4-alpine","6.1.4-alpine-adoptopenjdk8","6.1-alpine-adoptopenjdk8","6.1.3-alpine-adoptopenjdk8","6.1.2-alpine-adoptopenjdk8","6.1.2-alpine","6.0.5-alpine-adoptopenjdk8","6.0.5-alpine","6.0.6-alpine-adoptopenjdk8","6.0.6-alpine","7.13.0-m04-jdk11","7.13.0-m04","7.13.0-m04-ubuntu-18.04-adoptopenjdk11","7.13.0-m04-ubuntu","7.13.0-m04-adoptopenjdk11","7.13.0-m03-ubuntu-18.04-adoptopenjdk11","7.13.0-m03","7.13.0-m03-ubuntu","7.13.0-m03-jdk11","7.13.0-m03-adoptopenjdk11","7.13.0-m02-jdk11","7.13.0-m02-adoptopenjdk11","7.13.0-m02-ubuntu","7.13.0-m02","7.13.0-m02-ubuntu-18.04-adoptopenjdk11","6.0.7-alpine","6.0-alpine","6.0-alpine-adoptopenjdk8","6.0.7-alpine-adoptopenjdk8","6.1.0-alpine","6.1.0-alpine-adoptopenjdk8","6.1.1-alpine","6.1.1-alpine-adoptopenjdk8"],"bamboo":["latest","9.4-ubuntu","9.4-jdk11","9.4.3-ubuntu","9.4","9.4.3","9.4.3-jdk11","ubuntu","jdk11","9-ubuntu","9-jdk11","9.5-ubuntu","9.5-jdk11","9.5.1-ubuntu","9.5.1-jdk11","9.5.1","9.5","9","9.2-ubuntu","9.2-jdk11","9.2","9.2.11-jdk11","9.2.11-ubuntu","9.2.11","9.3.3-ubuntu","9.3.3-jdk11","9.3.3","9.2.4-ubuntu","9.2.4-jdk11","9.3.2-ubuntu","9.3.2-jdk11","9.0-ubuntu","9.2.4","9.3.2","9.0-jdk11","9.0.4-ubuntu","9.0.4-jdk11","9.0.4","9.0","8.2.6-ubuntu","8.2.6","8.2.6-jdk11","8.1.11-ubuntu","8.1.11-jdk11","8.1.11","8.0.0-jdk11","8.0.0-ubuntu","8.1.8-ubuntu","9.3.4-ubuntu","8.1.8-jdk11","9.3.4-jdk11","8.1.8","8.0.0","8.0.4-ubuntu","9.3.4","8.0.4-jdk11","9.2.5-ubuntu","9.2.5-jdk11","8.0.4","9.2.5","9.1.0-ubuntu","9.1.0-jdk11","9.1.0","8.2.7-ubuntu","8.2.7-jdk11","8.2.7","8.1-ubuntu","8.1-jdk11","8.1.12-ubuntu","8.1.12-jdk11","8.1","8.1.12","8.0.5-ubuntu","8.0.5-jdk11","8.0.1-ubuntu","9.1.1-jdk11","8.1.9-ubuntu","8.0.1-jdk11","8.0.1","8.1.9-jdk11","9.1.1","8.1.9","8.2.8-ubuntu","8.2.8-jdk11","8.0.5","8.2.8","8.1.2-ubuntu","8.1.2-jdk11","8.1.2","8.0.10-ubuntu","9.3.5-ubuntu","8.0.10-jdk11","9.3.5-jdk11","8.0.10","9.3.5","9.2.6-jdk11","9.2.6-ubuntu","9.2.6","9.1.1-ubuntu","8.2.0-ubuntu","8.2.0-jdk11","9.1.2-ubuntu","8.2.0","9.1.2-jdk11","8.0.6-ubuntu","8.0.6-jdk11","9.1.2","8.0.6","8-ubuntu","8-jdk11","8.2-ubuntu","8.2-jdk11","8.2.9-ubuntu","8.2.9-jdk11","8.2.9","8.2","8.1.3-ubuntu","8.1.3-jdk11","8.1.3","8.0.11-jdk11","8.0.11-ubuntu","8.0.11","9.3-ubuntu","8","9.3-jdk11","9.3.6-ubuntu","9.3.6-jdk11","9.3.6","9.3","9.2.7-ubuntu","9.2.7-jdk11","9.2.7","8.2.1-ubuntu","8.2.1-jdk11","8.2.1","8.0.7-ubuntu","9.4.0-jdk11","8.0.7-jdk11","9.2.8-ubuntu","8.0.7","9.2.8-jdk11","9.2.8","9.1-ubuntu","9.1-jdk11","9.1.3-ubuntu","9.1.3-jdk11","9.1.3","9.1","9.0.0-jdk11","9.0.0-ubuntu","9.0.0","8.1.4-ubuntu","8.1.4-jdk11","8.1.4","8.0.8-ubuntu","8.0.8-jdk11","8.0.8","8.0.12-ubuntu","8.0.12","8.0.12-jdk11","8.2.2-ubuntu","8.2.2-jdk11","8.1.5-jdk11","8.2.2","8.1.5-ubuntu","8.1.5","8.0-ubuntu","8.0-jdk11","8.0.13-jdk11","8.0.13-ubuntu","8.0.13","9.4.1-ubuntu","8.0","9.4.1-jdk11","9.4.1","9.2.9-ubuntu","9.2.9","9.2.9-jdk11","9.2.1-ubuntu","9.2.1-jdk11","9.2.1","9.0.1-ubuntu","9.0.1-jdk11","9.0.1","8.2.3-ubuntu","8.2.3-jdk11","8.2.3","9.4.2-jdk11","8.0.9-ubuntu","8.0.9-jdk11","9.0.2-ubuntu","8.0.9","9.0.2-jdk11","9.0.2","8.1.6-ubuntu","8.1.6-jdk11","8.1.6","8.0.2-ubuntu","8.0.2-jdk11","8.0.2","7-ubuntu","7-jdk8","7.2-ubuntu","7.2-jdk8","7.2.7-jdk8","7.2.7-ubuntu","7.2.7","7.2.5-ubuntu","7.2.5-jdk8","7.2.5","7.2.3-ubuntu","7.2.3-jdk8","7.2.3","7.2.10-ubuntu","7.2.10-jdk8","7.2.10","7.2.0-ubuntu","7.2.0-jdk8","7.2.0","7.2","7.1.3-ubuntu","7.1.3-jdk8","7.1.3","7","9.3.0-ubuntu","9.3.0-jdk11","9.3.0","9.2.10-ubuntu","9.2.10-jdk11","9.2.10","8.2.4-ubuntu","8.2.4-jdk11","8.2.4","8.1.1-ubuntu","8.1.1-jdk11","8.1.1","8.1.7-ubuntu","8.1.7-jdk11","8.1.7","8.0.3-ubuntu","8.0.3-jdk11","8.0.3","jdk17","9-jdk17","9.5-jdk17","9.5.0-ubuntu","9.5.0-jdk17","9.5.0-jdk11","9.5.0","9.4-jdk17","9.4.2-ubuntu","9.4.0-ubuntu","9.4.2-jdk17","9.4.2","9.4.0","9.4.0-jdk17","9.3.1-ubuntu","9.3.1-jdk11","9.3.1","9.2.3-ubuntu","9.2.3-jdk11","9.2.3","9.0.3-ubuntu","9.0.3-jdk11","9.0.3","8.2.5-ubuntu","8.2.5-jdk11","8.2.5","8.1.10-ubuntu","8.1.10-jdk11","8.1.10","7.2.9-ubuntu","7.2.9","7.2.9-jdk8","7.2.6-ubuntu","7.2.6-jdk8","7.2.6","7.2.4-ubuntu","7.2.4-jdk8","7.2.4","7.2.2-ubuntu","7.2.2-jdk8","7.2.2","7.2.1-ubuntu","7.2.1-jdk8","7.2.1","7.1-ubuntu","7.1-jdk8","7.1.4-ubuntu","7.1.4-jdk8","7.1.4","7.1.2-ubuntu","7.1.2-jdk8","7.1.2","7.1.1-ubuntu","7.1.1-jdk8","7.1.1","7.1","9.4.1-jdk17","eap-jdk8","9.0.0-rc1-ubuntu","9.0.0-rc1-jdk11","9.0.0-rc1","eap-jdk11","eap-ubuntu","9.0.0-rc1-jdk8","eap","8.0.0-rc4","8.0.0-rc1","7.0.6","7.0","7.1.0-rc1","7.0.4","6.10","6.10.6","7.0.3","6.10.5","7.0.2","7.0.1","7.0.0-rc2","7.0.0-rc1","6.10.4","6.10.3","6.10.2","6.9.2","6.9","6.8.3","6.8","6.9.1","6.8.2","6.9.0","6.9.0-rc1","6.7","6.7.3","6.8.1","6.8.0","6.7.2","6.7.1","6.6.3","6.6","6.7.0-rc2","6.6.2","6.6.1","6.6.0"],"bitbucket":["8.2.1-ubuntu-jdk11","8.2.1-jdk11","8.2.1","8.16.2-ubuntu-jdk17","8.16.2-jdk17","8.16.2","8.1.1-ubuntu-jdk11","8.1.1-jdk11","8.1.1","8.8-ubuntu-jdk17","8.8-jdk17","8.8.7-ubuntu-jdk17","8.8.7-jdk17","8.8.7","8.8","8.3.2-ubuntu-jdk11","7.21.5-ubuntu-jdk11","8.3.2-jdk11","7.21.5-jdk11","8.3.2","8.2.2-ubuntu-jdk11","7.21.5","8.2.2-jdk11","8.2.2","8.1.2-ubuntu-jdk11","8.1.2-jdk11","8.1.2","7.21.2-ubuntu-jdk11","7.21.2-jdk11","7.21.2","8.3.3-ubuntu-jdk11","8.3.3-jdk11","8.3.3","8.16-ubuntu-jdk17","8.16-jdk17","8.16.3-ubuntu-jdk17","8.16.3-jdk17","8.16.3","8.16","8.14.2-ubuntu-jdk17","8.15.1-ubuntu-jdk17","8.15.1-jdk17","8.14.2-jdk17","8.14.3-ubuntu-jdk17","8.15.1","8.14.2","8.14.3-jdk17","8.9.4-ubuntu-jdk17","8.9.4-jdk17","8.14.4-ubuntu-jdk17","8.14.3","8.9.4","8.14.4-jdk17","8.14.5-ubuntu-jdk17","8.14.4","8.8.1-ubuntu-jdk17","8.8.1-jdk17","8.14-ubuntu-jdk17","8.8.1","8.14-jdk17","8.14.5-jdk17","8.11.3-ubuntu-jdk17","8.11.3-jdk17","8.15.0-ubuntu-jdk17","8.14.5","8.15.0-jdk17","8.11.3","8.14","8.0.1-ubuntu-jdk11","8.15.0","8.0.1-jdk11","8.0.1","8.7.0-ubuntu-jdk11","8.7.0-jdk11","8.7.0","8.10.5-ubuntu-jdk17","8.10.5-jdk17","8.10.5","7.21.14-ubuntu-jdk11","7.21.14-jdk11","8.9.0-ubuntu-jdk17","7.21.14","8.9.0-jdk17","8.9.0","8.5-ubuntu-jdk11","8.5-jdk11","8.5.4-ubuntu-jdk11","8.5.4-jdk11","8.5.4","8.5","8.4.3-ubuntu-jdk11","8.4.3-jdk11","8.4.3","7.21.6-ubuntu-jdk11","7.21.6-jdk11","7.21.6","7.21.20-ubuntu-jdk11","7.21.20-jdk11","7.21.20","7.21.1-ubuntu-jdk11","7.21.1-jdk11","7.21.1","8.3-ubuntu-jdk11","8.3-jdk11","8.3.4-ubuntu-jdk11","8.3.4-jdk11","8.3.4","8.3","8.2.3-ubuntu-jdk11","8.2.3-jdk11","8.2.3","8.15.2-ubuntu-jdk17","8.15.2-jdk17","8.15.2","8.1.3-ubuntu-jdk11","8.1.3-jdk11","8.1.3","8.9.5-ubuntu-jdk17","8.9.5-jdk17","8.10.0-ubuntu-jdk17","8.10.0-jdk17","8.10.0","8.0.2-ubuntu-jdk11","8.0.2-jdk11","8.0.2","7.20.0-ubuntu-jdk11","7.20.0-jdk11","7.20.0","8.9.5","8.8.2-ubuntu-jdk17","8.8.2-jdk17","8.8.2","8.17.0-ubuntu-jdk17","8.17.0-jdk17","8.17.0","8.11.4-ubuntu-jdk17","8.11.4-jdk17","8.11.4","8.10-ubuntu-jdk17","8.10-jdk17","8.10.6-ubuntu-jdk17","8.10.6-jdk17","8.10.6","8.10","8.9.1-ubuntu-jdk17","8.9.1-jdk17","8.7.1-ubuntu-jdk11","8.9.1","8.7.1-jdk11","8.7.1","8.4-ubuntu-jdk11","8.4-jdk11","8.4.4-ubuntu-jdk11","8.4.4-jdk11","8.4.4","8.4","8.12.1-ubuntu-jdk17","8.12.1-jdk17","8.12.1","7.21.7-ubuntu-jdk11","7.21.7-jdk11","7.21.7","7.21.21-ubuntu-jdk11","7.21.21-jdk11","7.21.21","7.21.15-ubuntu-jdk11","7.21.15-jdk11","7.21.15","7.21.10-jdk11","7.21.10-ubuntu-jdk11","7.21.10","8.2-ubuntu-jdk11","8.2-jdk11","8.2.4-ubuntu-jdk11","8.2.4-jdk11","8.2.4","8.2","8.15.3-ubuntu-jdk17","8.15.3-jdk17","8.15.3","8.12-ubuntu-jdk17","8.12-jdk17","8.12.6-ubuntu-jdk17","8.12.6-jdk17","8.12.6","8.12","8.6.0-ubuntu-jdk11","8.6.0","8.6.0-jdk11","8.1.4-ubuntu-jdk11","8.1.4-jdk11","8.1.4","8.13.4-ubuntu-jdk17","8.13.4-jdk17","8.13.4","8.0.3-ubuntu-jdk11","8.0.3-jdk11","8.0.3","7.20.1-ubuntu-jdk11","8.9.6-ubuntu-jdk17","7.20.1-jdk11","8.9.6-jdk17","7.20.1","8.9.6","8.17-ubuntu-jdk17","8.17-jdk17","8.17.1-ubuntu-jdk17","8.17.1-jdk17","8.17.1","8.17","8.11.5-ubuntu-jdk17","8.11.5-jdk17","8.11.5","8.10.1-ubuntu-jdk17","8.10.1-jdk17","8.10.1","8.8.3-ubuntu-jdk17","8.8.3-jdk17","8.8.3","8.7.2-ubuntu-jdk11","8.7.2-jdk11","8.7.2","7-ubuntu-jdk11","7-jdk11","7.21-ubuntu-jdk11","7.21-jdk11","7.21.8-ubuntu-jdk11","7.21.8-jdk11","7.21.8","7.21.22-ubuntu-jdk11","7.21.22-jdk11","7.21.22","7.21","7","8.9-ubuntu-jdk17","8.9-jdk17","8.9.10-ubuntu-jdk17","8.9.10-jdk17","8.9.10","8.9","8.12.2-ubuntu-jdk17","8.12.2-jdk17","8.12.2","7.21.16-ubuntu-jdk11","7.21.16-jdk11","7.21.16","8.6.1-ubuntu-jdk11","8.6.1-jdk11","8.6.1","8.4.0-ubuntu-jdk11","8.4.0-jdk11","8.4.0","8.15-ubuntu-jdk17","8.15-jdk17","8.15.4-ubuntu-jdk17","8.15.4-jdk17","8.15.4","8.15","8.13.5-ubuntu-jdk17","8.13.5-jdk17","8.13.5","8.0.4-ubuntu-jdk11","8.0.4-jdk11","8.0.4","7.21.11-ubuntu-jdk11","7.21.11-jdk11","7.21.11","7.20.2-ubuntu-jdk11","7.20.2-jdk11","7.20.2","8.9.7-ubuntu-jdk17","8.9.7-jdk17","8.9.7","8.5.0-ubuntu-jdk11","8.5.0-jdk11","8.5.0","8.1-ubuntu-jdk11","8.1-jdk11","8.1.5-ubuntu-jdk11","8.1.5-jdk11","8.1.5","8.11.0-ubuntu-jdk17","8.11.0-jdk17","8.11.0","8.1","8.8.4-ubuntu-jdk17","8.8.4-jdk17","8.8.4","8.7.3-ubuntu-jdk11","8.13.0-ubuntu-jdk17","8.13.0-jdk17","8.13.0","8.11-ubuntu-jdk17","8.11-jdk17","8.11.6-ubuntu-jdk17","8.11.6-jdk17","8.11.6","8.11","8.10.2-ubuntu-jdk17","8.10.2-jdk17","latest","8.10.2","jdk17","8-ubuntu-jdk17","8-jdk17","8.7.3-jdk11","8.7.3","8.18.0-ubuntu-jdk17","8.12.3-ubuntu-jdk17","8.12.3-jdk17","8.12.3","8","7.21.9-ubuntu-jdk11","7.21.9-jdk11","7.21.9","ubuntu-jdk17","7.21.17-ubuntu-jdk11","8.6.2-ubuntu-jdk11","8.6.2-jdk11","8.6.2","8.4.1-ubuntu-jdk11","8.4.1-jdk11","8.4.1","8.3.0-ubuntu-jdk11","8.3.0-jdk11","8.3.0","8.18-ubuntu-jdk17","8.18-jdk17","8.18.0-jdk17","8.18.0","8.18","8.13-ubuntu-jdk17","8.13-jdk17","8.13.6-ubuntu-jdk17","8.13.6-jdk17","8.13.6","8.13","8.0-ubuntu-jdk11","8.0-jdk11","8.0.5-ubuntu-jdk11","8.0.5-jdk11","8.0.5","8.0","7.21.17-jdk11","7.21.17","7.20-ubuntu-jdk11","7.20-jdk11","7.20.3-ubuntu-jdk11","7.20.3-jdk11","7.20.3","7.20","8.9.8-ubuntu-jdk17","8.9.8-jdk17","8.9.8","8.5.1-ubuntu-jdk11","8.5.1-jdk11","8.5.1","8.11.1-ubuntu-jdk17","8.11.1-jdk17","8.11.1","7.21.3-ubuntu-jdk11","7.21.3-jdk11","7.21.3","7.21.12-ubuntu-jdk11","7.21.12-jdk11","7.21.12","8.9.2-ubuntu-jdk17","8.9.2-jdk17","8.9.2","8.8.5-ubuntu-jdk17","8.8.5-jdk17","8.8.5","8.7.4-ubuntu-jdk11","8.7.4-jdk11","8.7.4","8.13.1-ubuntu-jdk17","8.13.1-jdk17","8.13.1","8.10.3-ubuntu-jdk17","8.6.3-ubuntu-jdk11","8.10.3-jdk17","8.6.3-jdk11","8.10.3","8.6.3","8.16.0-ubuntu-jdk17","8.16.0-jdk17","8.16.0","8.12.4-ubuntu-jdk17","8.12.4-jdk17","8.12.4","8.0.0","7.21.18-ubuntu-jdk11","7.21.18","8.2.0-ubuntu-jdk11","8.2.0-jdk11","8.2.0","8.12.0-ubuntu-jdk17","8.12.0-jdk17","8.12.0","8.0.0-ubuntu-jdk11","8.0.0-jdk11","7.21.4-ubuntu-jdk11","7.21.4-jdk11","7.21.4","7.21.18-jdk11","8.9.9","8.5.2-ubuntu-jdk11","8.5.2-jdk11","8.5.2","8.4.2-ubuntu-jdk11","8.4.2-jdk11","8.4.2","8.3.1-ubuntu-jdk11","8.3.1-jdk11","8.3.1","8.11.2-ubuntu-jdk17","8.11.2-jdk17","8.11.2","8.9.9-ubuntu-jdk17","8.9.9-jdk17","8.14.0-ubuntu-jdk17","8.14.0-jdk17","8.14.0","8.13.2-ubuntu-jdk17","8.13.2-jdk17","8.13.2","7.21.13-ubuntu-jdk11","7.21.13-jdk11","7.21.13","8.7-ubuntu-jdk11","8.7-jdk11","8.7.5-ubuntu-jdk11","8.7.5-jdk11","8.7.5","8.7","8.6-ubuntu-jdk11","8.6-jdk11","8.6.4-ubuntu-jdk11","8.6.4-jdk11","8.6.4","8.6","7.21.0-ubuntu-jdk11","7.21.0-jdk11","7.21.0","8.9.3-ubuntu-jdk17","8.9.3-jdk17","8.9.3","8.8.6-ubuntu-jdk17","8.8.6-jdk17","8.8.6","8.8.0-ubuntu-jdk17","8.8.0-jdk17","8.8.0","8.16.1-ubuntu-jdk17","8.16.1-jdk17","8.16.1","8.12.5-ubuntu-jdk17","8.12.5-jdk17","8.12.5","8.1.0-ubuntu-jdk11","8.1.0-jdk11","8.10.4-ubuntu-jdk17","8.10.4-jdk17","8.10.4","8.1.0","7.21.19-ubuntu-jdk11","7.21.19-jdk11","7.21.19","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.5.3","8.14.1-ubuntu-jdk17","8.14.1-jdk17","8.14.1","8.13.3-ubuntu-jdk17","8.13.3-jdk17","8.13.3","8.15-ubuntu-jdk11","8.15-jdk11","8.15.3-ubuntu-jdk11","8.15.3-jdk11","8.8.0-ubuntu-jdk11","8.8.0-jdk11","8.16.0-ubuntu-jdk11","8.16.0-jdk11","8.8.1-jdk11","8.12.3-ubuntu-jdk11","8.12.3-jdk11","8.8.1-ubuntu-jdk11","8.16.1-ubuntu-jdk11","8.10.0-ubuntu-jdk11","8.10.0-jdk11","8.16.1-jdk11","7.6.9-ubuntu-jdk11","7.6.9-jdk11","8.12.4-ubuntu-jdk11","8.12.4-jdk11","7.6.9","8.8.2-ubuntu-jdk11","8.8.2-jdk11","7.19-ubuntu-jdk11","7.19-jdk11","7.19.5-ubuntu-jdk11","7.19.5-jdk11","7.19.5","7.19","7.7.0-ubuntu-jdk11","8.16-ubuntu-jdk11","8.16-jdk11","8.16.2-ubuntu-jdk11","7.7.0","8.16.2-jdk11","8.10.1-ubuntu-jdk11","8.10.1-jdk11","7.7.0-jdk11","8.12.5-ubuntu-jdk11","8.8.3-ubuntu-jdk11","8.12.5-jdk11","8.8.3-jdk11","7.7-ubuntu-jdk11","7.7-jdk11","7.7.1-ubuntu-jdk11","7.7.1-jdk11","7.7.1","7.7","7.10.0-ubuntu-jdk11","7.10.0-jdk11","7.10.0","8.10.2-ubuntu-jdk11","8.10.2-jdk11","ubuntu-jdk11","jdk11","8-jdk11","8-ubuntu-jdk11","8.17-ubuntu-jdk11","8.17-jdk11","8.17.0-ubuntu-jdk11","8.17.0-jdk11","8.12-ubuntu-jdk11","8.12-jdk11","8.12.6-ubuntu-jdk11","8.12.6-jdk11","7.16.2-ubuntu-jdk11","7.16.2-jdk11","7.16.2","8.8.4-ubuntu-jdk11","8.8.4-jdk11","7.10-ubuntu-jdk11","7.10-jdk11","7.10.1-ubuntu-jdk11","7.10.1-jdk11","7.10.1","7.10","7.17.3-ubuntu-jdk11","7.17.3-jdk11","8.10.3-ubuntu-jdk11","7.17.3","8.10.3-jdk11","7.8.0-ubuntu-jdk11","7.8.0-jdk11","7.8.0","7.16-jdk11","7.16-ubuntu-jdk11","7.16.3-jdk11","7.16.3-ubuntu-jdk11","7.16.3","7.16","8.13.0-ubuntu-jdk11","8.13.0-jdk11","8.8.5-ubuntu-jdk11","8.8.5-jdk11","7.11.1","7.6.13-ubuntu-jdk11","7.6.13-jdk11","7.17.4-ubuntu-jdk11","7.6.13","7.17.4-jdk11","7.17.4","7.11.1-ubuntu-jdk11","7.11.1-jdk11","7.8-ubuntu-jdk11","8.10.4-ubuntu-jdk11","7.8-jdk11","7.8.1-jdk11","7.8.1-ubuntu-jdk11","7.8.1","7.8","8.10.4-jdk11","7.17.0-ubuntu-jdk11","7.17.0-jdk11","7.17.0","8.13.1-ubuntu-jdk11","8.13.1-jdk11","7.6.14-ubuntu-jdk11","7.6.14-jdk11","7.6.14","7.11-ubuntu-jdk11","7.11-jdk11","7.11.2-ubuntu-jdk11","7.11.2-jdk11","8.8.6-ubuntu-jdk11","8.8.6-jdk11","7.11.2","7.11","7.17.5-ubuntu-jdk11","7.17.5-jdk11","7.17.5","7.9.0-ubuntu-jdk11","7.9.0-jdk11","7.9.0","8.10.5-ubuntu-jdk11","8.10.5-jdk11","7.6.15-ubuntu-jdk11","7.6.15-jdk11","7.6.15","7.17.1-ubuntu-jdk11","7.17.1-jdk11","7.17.1","8.13.2-ubuntu-jdk11","8.13.2-jdk11","7.17.6-ubuntu-jdk11","7.17.6-jdk11","7.17.6","7.12.0-jdk11","7.12.0","8.8-ubuntu-jdk11","8.8-jdk11","7.12.0-ubuntu-jdk11","8.8.7-ubuntu-jdk11","8.8.7-jdk11","7.9-ubuntu-jdk11","7.9-jdk11","7.9.1-ubuntu-jdk11","7.9.1-jdk11","7.9.1","7.9","8.10-ubuntu-jdk11","8.10-jdk11","8.10.6-ubuntu-jdk11","8.10.6-jdk11","7.6.16-ubuntu-jdk11","7.6.16-jdk11","7.6.16","7.17.10-ubuntu-jdk11","7.17.10-jdk11","7.17.10","7.17.7-ubuntu-jdk11","7.17.7-jdk11","7.17.7","7.12-ubuntu-jdk11","7.12-jdk11","7.12.1-ubuntu-jdk11","7.12.1-jdk11","7.12","8.13.3-ubuntu-jdk11","8.13.3-jdk11","7.12.1","7.6.17-ubuntu-jdk11","7.6.17-jdk11","7.6.17","7.17.11-ubuntu-jdk11","7.17.11-jdk11","7.17.11","7.17.8-ubuntu-jdk11","7.17.8-jdk11","7.17.8","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.13.4-ubuntu-jdk11","8.13.4-jdk11","8.11.0-ubuntu-jdk11","8.11.0-jdk11","7.6.18-ubuntu-jdk11","7.6.18-jdk11","7.6.18","7.13.0-ubuntu-jdk11","7.13.0-jdk11","7.13.0","7.17.12-ubuntu-jdk11","7.17.12-jdk11","7.17.12","7.17.9-ubuntu-jdk11","7.17.9-jdk11","7.17.9","8.13-ubuntu-jdk11","8.13-jdk11","8.13.5-ubuntu-jdk11","8.13.5-jdk11","7.6.19-ubuntu-jdk11","7.6.19-jdk11","7.6.19","7.13-ubuntu-jdk11","7.13-jdk11","7.13.1-ubuntu-jdk11","7.13.1-jdk11","7.13.1","7.13","8.9.1-ubuntu-jdk11","8.9.1-jdk11","8.11.1-ubuntu-jdk11","8.11.1-jdk11","7.17.13-ubuntu-jdk11","7.17.13-jdk11","7.17.13","7.18.0-ubuntu-jdk11","7.18.0-jdk11","7.18.0","7.6.2-ubuntu-jdk11","7.6.2-jdk11","8.9.2-ubuntu-jdk11","7.6.2","8.9.2-jdk11","8.14.0-ubuntu-jdk11","8.14.0-jdk11","8.11.2-ubuntu-jdk11","8.11.2-jdk11","7.17.14-ubuntu-jdk11","7.17.14-jdk11","7.17.14","7.14.0-ubuntu-jdk11","7.14.0-jdk11","7.14.0","7.6.20-ubuntu-jdk11","7.6.20-jdk11","7.6.20","7.18.1-ubuntu-jdk11","7.18.1-jdk11","7.18.1","7.14.1-ubuntu-jdk11","7.14.1","7.18.2-ubuntu-jdk11","7.17.15-ubuntu-jdk11","7.17.15-jdk11","7.17.15","7.14.1-jdk11","8.9.3-ubuntu-jdk11","8.9.3-jdk11","8.14.1-ubuntu-jdk11","8.14.1-jdk11","8.11.3-ubuntu-jdk11","8.11.3-jdk11","7.6.21-ubuntu-jdk11","7.6.21-jdk11","7.6.21","7.18.2-jdk11","7.18.2","7.14-ubuntu-jdk11","7.14.2-ubuntu-jdk11","7.14.2-jdk11","7.6.22-ubuntu-jdk11","7.6.22-jdk11","7.6.22","7.5.0-ubuntu-jdk11","7.5.0-jdk11","7.5.0","7.18.3","7.17.16-ubuntu-jdk11","7.17.16-jdk11","7.17.16","7.14-jdk11","7.14.2","7.14","8.14.2-ubuntu-jdk11","8.14.2-jdk11","7.18.3-ubuntu-jdk11","7.18.3-jdk11","8.9.4-ubuntu-jdk11","8.9.4-jdk11","8.11.4-ubuntu-jdk11","8.11.4-jdk11","7.6-ubuntu-jdk11","7.6-jdk11","7.6.23-ubuntu-jdk11","7.6.23-jdk11","7.6.23","7.6","7.5.1-ubuntu-jdk11","7.5.1-jdk11","7.5.1","7.17.17-ubuntu-jdk11","7.17.17-jdk11","7.17.17","7.18-ubuntu-jdk11","7.18-jdk11","7.18.4-ubuntu-jdk11","7.18.4-jdk11","7.18.4","7.18","8.14.3-ubuntu-jdk11","7.15.0-jdk11","8.14.3-jdk11","8.11.5-ubuntu-jdk11","8.11.5-jdk11","7.15.0-ubuntu-jdk11","7.15.0","8.9.5-ubuntu-jdk11","8.9.5-jdk11","7.5-ubuntu-jdk11","7.5-jdk11","7.5.2-ubuntu-jdk11","7.5.2-jdk11","7.5.2","7.5","7.17.18-ubuntu-jdk11","7.17.18-jdk11","7.17.18","7.15.1-ubuntu-jdk11","7.15.1-jdk11","7.15.1","8.14-jdk11","8.14.4-jdk11","8.9.6-ubuntu-jdk11","7.6.3-ubuntu-jdk11","7.6.3-jdk11","8.9.6-jdk11","7.6.3","8.14-ubuntu-jdk11","8.14.4-ubuntu-jdk11","8.11-ubuntu-jdk11","8.11-jdk11","8.11.6-ubuntu-jdk11","8.11.6-jdk11","7.19.0-ubuntu-jdk11","7.19.0-jdk11","7.19.0","7.17.19-ubuntu-jdk11","7.17.19-jdk11","7.17.19","7.15.2-ubuntu-jdk11","7.15.2-jdk11","7.15.2","7.6.4-ubuntu-jdk11","7.6.4-jdk11","7.6.4","7.6.0-ubuntu-jdk11","7.6.0-jdk11","7.6.0","8.9.7-ubuntu-jdk11","8.9.7-jdk11","7.19.1-ubuntu-jdk11","7.19.1-jdk11","7.19.1","7.17.2-ubuntu-jdk11","7.17.2","7.17.2-jdk11","7.15-ubuntu-jdk11","7.15-jdk11","7.15.3-ubuntu-jdk11","7.15.3-jdk11","7.15.3","7.15","8.15.0-ubuntu-jdk11","8.15.0-jdk11","8.12.0-ubuntu-jdk11","8.12.0-jdk11","7.6.5-ubuntu-jdk11","7.6.5-jdk11","7.6.5","7.6.1-ubuntu-jdk11","7.6.1-jdk11","7.6.1","8.9.8-ubuntu-jdk11","8.9.8-jdk11","7.19.2-ubuntu-jdk11","7.19.2-jdk11","7.19.2","7.17.20-ubuntu-jdk11","7.17.20-jdk11","7.17.20","7.6.6-ubuntu-jdk11","7.6.6-jdk11","7.6.6","7.6.10-ubuntu-jdk11","7.6.10-jdk11","7.6.10","8.15.1-ubuntu-jdk11","8.15.1-jdk11","8.12.1-ubuntu-jdk11","8.12.1-jdk11","7.19.3-ubuntu-jdk11","7.19.3-jdk11","7.19.3","7.16.0-ubuntu-jdk11","7.16.0-jdk11","8.9-ubuntu-jdk11","7.16.0","8.9-jdk11","8.9.9-ubuntu-jdk11","8.9.9-jdk11","7.6.7-ubuntu-jdk11","7.6.7-jdk11","7.6.7","7.6.11-ubuntu-jdk11","7.6.11-jdk11","7.6.11","7.17-ubuntu-jdk11","7.17-jdk11","7.17.21-ubuntu-jdk11","7.17.21-jdk11","7.17.21","7.17","8.15.2-ubuntu-jdk11","8.15.2-jdk11","8.12.2-ubuntu-jdk11","8.12.2-jdk11","7.19.4-ubuntu-jdk11","7.19.4-jdk11","7.19.4","7.16.1-ubuntu-jdk11","7.16.1-jdk11","7.16.1","7.6.8-ubuntu-jdk11","7.6.8-jdk11","7.6.8","7.6.12-ubuntu-jdk11","7.6.12-jdk11","7.6.12","8.14.0-eap01","8.14.0-eap01-ubuntu-jdk11","8.14.0-eap01-jdk11","7.4-ubuntu-jdk11","7.4-jdk11","7.4.1-ubuntu-jdk11","7.4.1-jdk11","7.4.1","7.4.0-ubuntu-jdk11","7.4.0-jdk11","7.4.0","7.3-ubuntu-jdk11","7.3-jdk11","7.3.2-ubuntu-jdk11","7.3.2-jdk11","7.3.2","7.3","7.4.2-ubuntu-jdk11","7.4.2-jdk11","7.4.2","7.4","7.3.1-ubuntu-jdk11","7.3.1-jdk11","7.3.1","7.3.0-ubuntu-jdk11","7.3.0-jdk11","7.3.0","7.2.5-ubuntu-jdk11","7.2.5-jdk11","7.2.5","7.2.4-ubuntu-jdk11","7.2.4-jdk11","7.2.4","7.2.3-ubuntu-jdk11","7.2.3-jdk11","7.2.3","7.2.2-ubuntu-jdk11","7.2.2-jdk11","7.2.2","7.2.1-ubuntu-jdk11","7.2.1-jdk11","7.2.1","7.2.0-ubuntu-jdk11","7.2.0-jdk11","7.2.0","7.1-ubuntu-jdk11","7.1-jdk11","7.1.4-ubuntu-jdk11","7.1.4-jdk11","7.1.4","7.1.3-ubuntu-jdk11","7.1.3-jdk11","7.1.3","7.1.2-ubuntu-jdk11","7.1.2-jdk11","7.1.2","7.1.1-ubuntu-jdk11","7.1.1-jdk11","7.1.1","7.1.0-ubuntu-jdk11","7.1.0-jdk11","7.1.0","7.1","7.2-ubuntu-jdk11","7.2-jdk11","7.2.6-ubuntu-jdk11","7.2.6-jdk11","7.2.6","7.2","eap-ubuntu-jdk11","eap-jdk11","eap","8.0.0-eap05-ubuntu-jdk11","8.0.0-eap05-jdk11","8.0.0-eap05","7.0-jdk11","7.0-ubuntu-jdk11","7.0.5-ubuntu-jdk11","7.0.5-jdk11","7.0.5","7.0","6.10.17-ubuntu-jdk11","6.10.17","6.10.17-jdk11","6.10","6","6-ubuntu-jdk11","6-jdk11","6.10-ubuntu-jdk11","6.10-jdk11","6.8.2-jdk11","6.10.11-jdk11","6.8.2","6.10.11","6.8.2-ubuntu-jdk11","6.10.11-ubuntu-jdk11","7.0.0-jdk11","7.0.0-ubuntu-jdk11","7.0.0","6.8.3","6.7.1","6.7.1-ubuntu-jdk11","6.10.12-jdk11","6.8.3-ubuntu-jdk11","6.7.1-jdk11","6.10.12","6.8.3-jdk11","6.10.12-ubuntu-jdk11","7.0.1-ubuntu-jdk11","7.0.1-jdk11","7.0.1","6.7.2-jdk11","6.10.3","6.8.4-jdk11","6.7.2","6.10.3-jdk11","6.8","6.10.13-jdk11","6.7.2-ubuntu-jdk11","6.10.3-ubuntu-jdk11","6.8-ubuntu-jdk11","6.10.13-ubuntu-jdk11","6.8-jdk11","6.10.13","6.8.4-ubuntu-jdk11","7.0.2-ubuntu-jdk11","6.8.4","7.0.2","6.7.3-ubuntu-jdk11","7.0.2-jdk11","6.10.4-jdk11","6.7.3","6.10.4-ubuntu-jdk11","6.7.3-jdk11","6.10.14-ubuntu-jdk11","6.10.4","6.10.14","6.10.14-jdk11","7.0.3-jdk11","6.9.0-ubuntu-jdk11","7.0.3","6.7.4-ubuntu-jdk11","6.10.5-jdk11","6.9.0","7.0.3-ubuntu-jdk11","6.7.4","6.10.5-ubuntu-jdk11","6.9.0-jdk11","6.7.4-jdk11","6.10.15-jdk11","6.10.5","6.10.15","6.10.15-ubuntu-jdk11","7.0.4-jdk11","6.9.1-jdk11","6.7-jdk11","7.0.4-ubuntu-jdk11","6.10.7-jdk11","6.9.1","6.7","7.0.4","6.10.7","6.9.1-ubuntu-jdk11","6.7-ubuntu-jdk11","6.10.7-ubuntu-jdk11","6.7.5","6.10.16-jdk11","6.7.5-jdk11","6.7.5-ubuntu-jdk11","6.10.16","6.9.2-ubuntu-jdk11","6.10.16-ubuntu-jdk11","6.10.8","6.9.2-jdk11","6.10.8-ubuntu-jdk11","6.9.2","6.10.8-jdk11","6.9.3-ubuntu-jdk11","6.10.9","6.8.0-jdk11","6.9-jdk11","6.10.9-jdk11","6.8.0-ubuntu-jdk11","6.9.3","6.10.9-ubuntu-jdk11","6.8.0","6.9.3-jdk11","6.9","6.9-ubuntu-jdk11","6.10.2","6.7.0-jdk11","6.8.1-jdk11","6.10.2-jdk11","6.7.0-ubuntu-jdk11","6.8.1","6.10.2-ubuntu-jdk11","6.7.0","6.8.1-ubuntu-jdk11","6.10.0-jdk11","6.10.0-ubuntu-jdk11","6.10.0","6.10.1-jdk11","6.10.1-ubuntu-jdk11","6.10.1","6.10.10-jdk11","6.10.10","6.10.10-ubuntu-jdk11","7.14-ubuntu","7.14-jdk8","7.14.1-ubuntu","7.14-ubuntu-jdk8","7.14.1-jdk8","7.14.1-ubuntu-jdk8","7.11.2-jdk8","7.11-ubuntu","7.11.2-ubuntu","7.11-ubuntu-jdk8","7.11-jdk8","7.11.2-ubuntu-jdk8","7.15.0-jdk8","7.1.2-ubuntu","7.15.0-ubuntu","7.1.2-jdk8","7.15.0-ubuntu-jdk8","7.1.2-ubuntu-jdk8","7.6.4-ubuntu","6.7-jdk8","7.2.2-jdk8","7.6.4-jdk8","7.12.0-jdk8","6.7.5-ubuntu","7.5.1-ubuntu","7.6.4-ubuntu-jdk8","6.7.5-ubuntu-jdk8","7.2.2-ubuntu-jdk8","6.9.0-ubuntu","7.15-ubuntu-jdk8","7.12.0-ubuntu","7.5.1-jdk8","7.1.3-jdk8","7.2.2-ubuntu","6.9.0-jdk8","6.7-ubuntu","7.15.1-ubuntu","7.12.0-ubuntu-jdk8","7.5.1-ubuntu-jdk8","6.9.0-ubuntu-jdk8","6.7.5-jdk8","7.15-ubuntu","7.0.2-ubuntu-jdk8","7.1.3-ubuntu-jdk8","6.7-ubuntu-jdk8","7.15-jdk8","7.0.2-jdk8","7.1.3-ubuntu","7.0.2-ubuntu","7.15.1-jdk8","7.15.1-ubuntu-jdk8","7.2.3-jdk8","7.12.1-ubuntu","7.5.2-ubuntu-jdk8","7.6.5-jdk8","7.2.3-ubuntu","6.9.1-ubuntu","7.5-ubuntu-jdk8","7.6.5-ubuntu-jdk8","7.12.1-jdk8","6.9.1-ubuntu-jdk8","7.1.4-jdk8","7.5-jdk8","7.2.3-ubuntu-jdk8","7.12-jdk8","7.1.4-ubuntu","7.5.2-ubuntu","7.6.5-ubuntu","6.9.1-jdk8","7.0.3-ubuntu-jdk8","7.12-ubuntu-jdk8","6.10.2-jdk8","6.8.0-jdk8","7.12-ubuntu","7.1-ubuntu","7.5.2-jdk8","6.8.0-ubuntu-jdk8","6.10.9-ubuntu-jdk8","7.0.3-jdk8","6.10.2-ubuntu","7.12.1-ubuntu-jdk8","7.1-ubuntu-jdk8","7.5-ubuntu","6.10.9-jdk8","7.0.3-ubuntu","6.10.2-ubuntu-jdk8","7.1.4-ubuntu-jdk8","6.8.0-ubuntu","7.2.4-ubuntu","7.16.0-ubuntu","6.10.9-ubuntu","7.6.6-ubuntu","7.16-jdk8","6.9.2-jdk8","7.1-jdk8","7.16.0-jdk8","7.2.4-ubuntu-jdk8","7.6.6-ubuntu-jdk8","6.9.2-ubuntu-jdk8","7.2.4-jdk8","7.0.4-jdk8","7.6.6-jdk8","6.10.3-ubuntu","6.9.2-ubuntu","7.16.0-ubuntu-jdk8","6.8.1-ubuntu","6.10.3-ubuntu-jdk8","6.7.0-jdk8","7.0.4-ubuntu","6.8.1-ubuntu-jdk8","6.7.0-ubuntu-jdk8","7-ubuntu-jdk8","7.0.4-ubuntu-jdk8","6.10.3-jdk8","6.8.1-jdk8","7-jdk8","7.13.0-jdk8","6.7.0-ubuntu","ubuntu","7.2.5-jdk8","7.6.0-jdk8","7.13.0-ubuntu-jdk8","7.6.7-jdk8","6.9-ubuntu","7.6.0-ubuntu","7.2.5-ubuntu-jdk8","6.10.11-jdk8","7.13.0-ubuntu","7.10.0-ubuntu-jdk8","6.9-jdk8","jdk8","7.10.0-jdk8","6.9.3-ubuntu-jdk8","7.6.7-ubuntu","7.0.5-ubuntu","6.10.4-ubuntu-jdk8","6.8.2-ubuntu-jdk8","ubuntu-jdk8","7.2.5-ubuntu","7.6.0-ubuntu-jdk8","7.10.0-ubuntu","6.9.3-ubuntu","7.6.7-ubuntu-jdk8","6.10.4-ubuntu","6.8.2-ubuntu","7.0-ubuntu","7.16-ubuntu","6.10.4-jdk8","7.0-ubuntu-jdk8","6.7.1-ubuntu-jdk8","6.8.2-jdk8","7.0-jdk8","6.7.1-jdk8","7-ubuntu","6.9-ubuntu-jdk8","6.7.1-ubuntu","6.10.12-jdk8","7.16-ubuntu-jdk8","6.9.3-jdk8","7.13.1-ubuntu-jdk8","6.10.12-ubuntu","7.2.6-jdk8","7.6.1-jdk8","7.10-ubuntu-jdk8","7.6.8-jdk8","7.0.5-jdk8","6.10.12-ubuntu-jdk8","7.2-ubuntu-jdk8","7.10-ubuntu","7.6.8-ubuntu","7.13.1-ubuntu","7.0.5-ubuntu-jdk8","7.2-ubuntu","7.6.1-ubuntu","7.10.1-ubuntu-jdk8","6.8.3-jdk8","7.13-jdk8","6.10.5-ubuntu","7.2.6-ubuntu","7.6.1-ubuntu-jdk8","7.10.1-ubuntu","6.8.3-ubuntu-jdk8","6.7.2-ubuntu-jdk8","7.6.8-ubuntu-jdk8","7.13-ubuntu","7.2-jdk8","6.8.3-ubuntu","6.7.2-ubuntu","7.4.2-jdk8","6.10.5-jdk8","7.13-ubuntu-jdk8","7.2.6-ubuntu-jdk8","7.10-jdk8","7.4-ubuntu-jdk8","6.10.5-ubuntu-jdk8","7.13.1-jdk8","6-ubuntu","6.7.2-jdk8","7.10.1-jdk8","7.4-jdk8","7.4.2-ubuntu","7.6.2-jdk8","7.6-jdk8","7.0.0-ubuntu-jdk8","7.2.0-ubuntu","7.6.2-ubuntu-jdk8","7.0.0-ubuntu","6.10-ubuntu-jdk8","7.6.2-ubuntu","7.1.0-ubuntu","7.2.0-jdk8","6.10.7-jdk8","7.6.9-jdk8","7.0.0-jdk8","6.7.3-jdk8","6.10-ubuntu","7.1.0-ubuntu-jdk8","7.6-ubuntu-jdk8","7.2.0-ubuntu-jdk8","6.7.3-ubuntu-jdk8","6.10-jdk8","6.8-ubuntu-jdk8","7.1.0-jdk8","6.10.7-ubuntu","7.6-ubuntu","6.7.3-ubuntu","6-jdk8","6.8.4-jdk8","6.10.7-ubuntu-jdk8","6.10.13-jdk8","6.8-ubuntu","7.9.1-ubuntu-jdk8","7.6.9-ubuntu","6.10.13-ubuntu-jdk8","6.8.4-ubuntu","7.6.9-ubuntu-jdk8","6.10.13-ubuntu","6.8-jdk8","7.3.0-ubuntu-jdk8","7.6.3-jdk8","7.2.1-ubuntu","7.0.1-ubuntu-jdk8","7.5.0-ubuntu","6-ubuntu-jdk8","6.8.4-ubuntu-jdk8","7.14.0-ubuntu","7.11.1-ubuntu-jdk8","7.6.3-ubuntu-jdk8","7.3.0-ubuntu","7.0.1-jdk8","7.2.1-ubuntu-jdk8","7.5.0-jdk8","6.7.4-ubuntu","7.14.0-jdk8","7.11.1-jdk8","7.6.3-ubuntu","7.0.1-ubuntu","7.5.0-ubuntu-jdk8","7.1.1-ubuntu-jdk8","6.10.8-jdk8","7.14.0-ubuntu-jdk8","7.11.1-ubuntu","7.3.0-jdk8","7.2.1-jdk8","7.1.1-ubuntu","6.7.4-jdk8","6.10.8-ubuntu-jdk8","7.1.1-jdk8","6.7.4-ubuntu-jdk8","6.10.8-ubuntu","7.7.0-ubuntu-jdk8","7.7.0-ubuntu","7.7.0-jdk8","7.7.1-jdk8","7.7.1-ubuntu","7.7-jdk8","7.7-ubuntu","7.7-ubuntu-jdk8","6.10.0-ubuntu","7.7.1-ubuntu-jdk8","6.10.0-jdk8","6.10.0-ubuntu-jdk8","6.10.1-jdk8","6.10.1-ubuntu","7.8.0-ubuntu","6.10.1-ubuntu-jdk8","7.8.0-ubuntu-jdk8","7.8.0-jdk8","6.10.10-ubuntu","6.10.10-ubuntu-jdk8","7.8-ubuntu","6.10.10-jdk8","7.8.1-ubuntu","7.8.1-ubuntu-jdk8","7.8-ubuntu-jdk8","7.8-jdk8","6.10.11-ubuntu-jdk8","7.8.1-jdk8","6.10.11-ubuntu","7.9.0-jdk8","7.9.0-ubuntu","7.9.0-ubuntu-jdk8","7.9.1-jdk8","7.9-ubuntu-jdk8","7.9.1-ubuntu","7.9-ubuntu","7.9-jdk8","7.3.1-ubuntu-jdk8","7.3.1-ubuntu","7.3.1-jdk8","7.3.2-jdk8","7.3.2-ubuntu-jdk8","7.3-ubuntu","7.3-ubuntu-jdk8","7.3-jdk8","7.3.2-ubuntu","7.4.0-ubuntu-jdk8","7.4.0-ubuntu","7.4.0-jdk8","7.4.1-ubuntu-jdk8","7.4.1-ubuntu","7.4.1-jdk8","7.4.2-ubuntu-jdk8","7.4-ubuntu","6.5.0-ubuntu","6.5.0","6.5.0-jdk8","6.5.0-ubuntu-jdk8","6.5.1","6.6.3","6.5.1-ubuntu","6.6.3-ubuntu","6.5.1-ubuntu-jdk8","6.6.3-ubuntu-jdk8","6.5.1-jdk8","6.6.3-jdk8","6.5.2-jdk8","6.6.4-ubuntu","6.5.2-ubuntu-jdk8","6.6-ubuntu-jdk8","6.5.2-ubuntu","6.6","6.5.2","6.6.4-jdk8","6.6.4","6.6.4-ubuntu-jdk8","6.6-jdk8","6.5.1-ubuntu-jdk11","6.6.3-jdk11","6.6-ubuntu","6.5.1-jdk11","6.5-ubuntu","6.6.3-ubuntu-jdk11","6.5.3-jdk8","6.5.3-ubuntu-jdk8","6.5-ubuntu-jdk8","6.5","6.6-ubuntu-jdk11","6.5.2-jdk11","6.5.3-ubuntu","6.5.2-ubuntu-jdk11","6.6.4-jdk11","6.5.3","6.6.4-ubuntu-jdk11","6.5-jdk8","6.6-jdk11","6.5-jdk11","6.5.3-ubuntu-jdk11","6.5-ubuntu-jdk11","6.5.3-jdk11","6.6.0-ubuntu","6.6.0-jdk8","6.6.0-ubuntu-jdk8","6.6.0","6.6.0-jdk11","6.6.0-ubuntu-jdk11","6.6.1-jdk8","6.6.1","6.6.1-ubuntu","6.6.1-ubuntu-jdk8","6.6.1-jdk11","6.6.1-ubuntu-jdk11","6.6.2","6.6.2-ubuntu-jdk8","6.5.0-jdk11","6.6.2-ubuntu-jdk11","6.6.2-jdk8","6.5.0-ubuntu-jdk11","6.6.2-jdk11","6.6.2-ubuntu","6.4.0-ubuntu","6.4.0","6.4.0-ubuntu-jdk8","6.4.0-jdk8","6.0-ubuntu-jdk8","6.4.1-jdk8","6.0.11","6.4.1-ubuntu-jdk8","6.0-jdk8","6.4.1","6.0-ubuntu","6.4.1-ubuntu","6.0.11-ubuntu-jdk8","6.0","6.0.11-jdk8","6.0.11-ubuntu","6.4.2-jdk8","6.4.2-ubuntu-jdk8","6.4.2-ubuntu","6.4.2","6.0.2","6.4.3-jdk8","6.0.2-ubuntu","6.4.3-ubuntu","6.0.2-jdk8","6.4.3","6.0.2-ubuntu-jdk8","6.4.3-ubuntu-jdk8","6.0.3-ubuntu-jdk8","6.4.4-ubuntu","6.0.3","6.4.4-jdk8","6.0.3-jdk8","6.4-jdk8","6.0.3-ubuntu","6.4-ubuntu","6.4","6.4.4-ubuntu-jdk8","6.4-ubuntu-jdk8","6.4.4","6.0.4-jdk8","6.0.4","6.0.4-ubuntu","6.0.4-ubuntu-jdk8","6.0.5-jdk8","6.0.5-ubuntu-jdk8","6.0.5-ubuntu","6.0.5","6.0.6-jdk8","6.0.6-ubuntu-jdk8","6.0.6","6.0.6-ubuntu","6.0.7-ubuntu-jdk8","6.0.7","6.0.7-ubuntu","6.0.7-jdk8","6.0.9-ubuntu","6.0.9","6.0.9-jdk8","6.0.9-ubuntu-jdk8","6.1.0-ubuntu-jdk8","6.1.0-ubuntu","6.1.0","6.1.0-jdk8","6.2.6","6.2.6-ubuntu-jdk8","6.2.6-jdk8","6.2.6-ubuntu","6.2","6.2.7-ubuntu-jdk8","6.2-ubuntu","6.2.7-jdk8","6.2-ubuntu-jdk8","6.2-jdk8","6.2.7-ubuntu","6.2.7","6.3.0-jdk8","6.3.0-ubuntu-jdk8","6.3.0","6.3.0-ubuntu","6.3.1","6.3.1-jdk8","6.3.1-ubuntu-jdk8","6.3.1-ubuntu","6.3.2-ubuntu","6.3.2-jdk8","6.3.2-ubuntu-jdk8","6.3.2","6.3.3","6.3.3-jdk8","6.3.3-ubuntu-jdk8","6.3.3-ubuntu","6.3.4-ubuntu-jdk8","6.3.4-ubuntu","6.3.4","6.3.4-jdk8","6.3.5","6.3.5-ubuntu-jdk8","6.3.5-jdk8","6.3.5-ubuntu","6.3-ubuntu","6.3.6-ubuntu","6.3.6-ubuntu-jdk8","6.3.6","6.3","6.3.6-jdk8","6.3-ubuntu-jdk8","6.3-jdk8","6.1.1-ubuntu","6.1.1","6.1.1-jdk8","6.1.1-ubuntu-jdk8","6.1.2-jdk8","6.1.2","6.1.2-ubuntu","6.1.2-ubuntu-jdk8","6.1.3-ubuntu-jdk8","6.1.3","6.4.0-ubuntu-jdk11","6.2.6-jdk11","6.1.3-jdk8","6.4.0-jdk11","6.2.6-ubuntu-jdk11","6.1.3-ubuntu","6.0-jdk11","6.1.1-ubuntu-jdk11","6.4.1-jdk11","6.0.11-jdk11","6.1.4","6.2.7-ubuntu-jdk11","6.4.1-ubuntu-jdk11","6.0-ubuntu-jdk11","6.1.4-ubuntu-jdk8","6.1.1-jdk11","6.2-ubuntu-jdk11","6.0.11-ubuntu-jdk11","6.1.4-jdk8","6.2-jdk11","6.1.4-ubuntu","6.2.7-jdk11","6.4.2-ubuntu-jdk11","6.1.2-jdk11","6.4.2-jdk11","6.1.2-ubuntu-jdk11","6.2.0-ubuntu-jdk8","6.1.5-jdk8","6.2.0-jdk8","6.1.5-ubuntu","6.2.0-ubuntu","6.1.5","6.2.0","6.0.2-jdk11","6.3.0-ubuntu-jdk11","6.1.5-ubuntu-jdk8","6.4.3-jdk11","6.1.3-ubuntu-jdk11","6.0.2-ubuntu-jdk11","6.3.0-jdk11","6.1.3-jdk11","6.4.3-ubuntu-jdk11","6.2.1","6.1.6-ubuntu-jdk8","6.2.1-ubuntu-jdk8","6.0.3-ubuntu-jdk11","6.1.6-ubuntu","6.3.1-jdk11","6.2.1-ubuntu","6.1.4-ubuntu-jdk11","6.0.3-jdk11","6.1.6","6.3.1-ubuntu-jdk11","6.4.4-ubuntu-jdk11","6.2.1-jdk8","6.1.4-jdk11","6.1.6-jdk8","6.4.4-jdk11","6.4-ubuntu-jdk11","6.4-jdk11","6.0.4-ubuntu-jdk11","6.3.2-ubuntu-jdk11","6.2.0-jdk11","6.1.5-jdk11","6.2.2-ubuntu","6.0.4-jdk11","6.1.7","6.3.2-jdk11","6.2.0-ubuntu-jdk11","6.1.5-ubuntu-jdk11","6.2.2-ubuntu-jdk8","6.1.7-ubuntu-jdk8","6.2.2","6.1.7-ubuntu","6.2.2-jdk8","6.1.7-jdk8","6.0.5-jdk11","6.3.3-ubuntu-jdk11","6.2.1-jdk11","6.1.6-ubuntu-jdk11","6.0.5-ubuntu-jdk11","6.3.3-jdk11","6.2.1-ubuntu-jdk11","6.1.6-jdk11","6.2.3-ubuntu","6.1.8-jdk8","6.0.0-ubuntu","6.2.3-ubuntu-jdk8","6.1.8-ubuntu-jdk8","6.0.0-ubuntu-jdk8","6.2.3","6.1.8-ubuntu","6.0.0-jdk8","6.0.6-ubuntu-jdk11","6.3.4-ubuntu-jdk11","6.2.2-ubuntu-jdk11","6.1.8","6.2.3-jdk8","6.0.0","6.1.7-jdk11","6.0.6-jdk11","6.3.4-jdk11","6.2.2-jdk11","6.1.7-ubuntu-jdk11","6.2.3-ubuntu-jdk11","6.3.5-ubuntu-jdk11","6.0.7-ubuntu-jdk11","6.0.0-ubuntu-jdk11","6.1.8-jdk11","6.2.3-jdk11","6.0.7-jdk11","6.3.5-jdk11","6.0.0-jdk11","6.1.8-ubuntu-jdk11","6.1.9-ubuntu","6.2.4-jdk8","6.0.1-ubuntu-jdk8","6.1-ubuntu-jdk8","6.2.4-ubuntu-jdk8","6.0.1-jdk8","6.1.9-jdk8","6.2.4","6.0.1-ubuntu","6.1-ubuntu","6.2.4-ubuntu","6.0.1","6.0.9-jdk11","6.2.4-ubuntu-jdk11","6.1-jdk8","6.3-jdk11","6.0.1-ubuntu-jdk11","6.1.9-ubuntu-jdk11","6.0.9-ubuntu-jdk11","6.1.9-ubuntu-jdk8","6.2.4-jdk11","6.3.6-jdk11","6.0.1-jdk11","6.1-jdk11","6.1","6.3-ubuntu-jdk11","6.1-ubuntu-jdk11","6.1.9","6.3.6-ubuntu-jdk11","6.1.9-jdk11","6.2.5","6.0.10-ubuntu-jdk8","6.2.5-ubuntu","6.0.10-jdk8","6.1.0-ubuntu-jdk11","6.2.5-jdk11","6.2.5-jdk8","6.0.10-jdk11","6.0.10-ubuntu","6.2.5-ubuntu-jdk8","6.2.5-ubuntu-jdk11","6.1.0-jdk11","6.0.10-ubuntu-jdk11","6.0.10","7-eap","5.16.11-alpine","5-alpine","5.16.11","5.16","5","5.16-alpine","5.16.10","5.16.10-alpine","5.16.9","5.16.9-alpine","5.16.8","5.16.8-alpine","5.16.7-alpine","5.16.7","5.16.6-alpine","5.16.6","5.16.5-alpine","5.16.5","5.9.2-alpine","5.9.2","5.9-alpine","5.9","5.9.1-alpine","5.9.1","5.9.0-alpine","5.9.0","5.8.3-alpine","5.8.3","5.8.2-alpine","5.8.2","5.8.1-alpine","5.8.1","5.8.0-alpine","5.8.0","5.7.4-alpine","5.7.4","5.7-alpine","5.7","5.7.3-alpine","5.7.3","5.7.2-alpine","5.7.2","5.7.1-alpine","5.7.1","5.7.0-alpine","5.7.0","5.6.6-alpine","5.6.6","5.6-alpine","5.6","5.6.5-alpine","5.6.5","5.6.4-alpine","5.6.4","5.6.3-alpine","5.6.3","5.6.2-alpine","5.6.2","5.6.1-alpine","5.6.1","5.6.0-alpine","5.6.0","5.5.9-alpine","5.5.9","5.5-alpine","5.5","5.5.8-alpine","5.5.8","5.5.7-alpine","5.5.7","5.5.6-alpine","5.5.6","5.5.5-alpine","5.5.5","5.5.4-alpine","5.5.4","5.5.3-alpine","5.5.3","5.5.2-alpine","5.5.2","5.5.1-alpine","5.5.1","5.5.0-alpine","5.5.0","5.4.9-alpine","5.4.9","5.4-alpine","5.4","5.4.8-alpine","5.4.8","5.4.7-alpine","5.4.7","5.4.6-alpine","5.4.6","5.4.4-alpine","5.4.4","5.4.3-alpine","5.4.3","5.4.2-alpine","5.4.2","5.4.1-alpine","5.4.1","5.4.0-alpine","5.4.0","5.3.7-alpine","5.3.7","5.3-alpine","5.3","5.3.6-alpine","5.3.6","5.3.5-alpine","5.3.5","5.3.4-alpine","5.3.4","5.3.3-alpine","5.3.3","5.3.2-alpine","5.3.2","5.3.1-alpine","5.3.1","5.3.0-alpine","5.3.0","5.2.8-alpine","5.2.8","5.2-alpine","5.2","5.2.7-alpine","5.2.7","5.2.6-alpine","5.2.6","5.2.5-alpine","5.2.5","5.2.4-alpine","5.2.4","5.2.3-alpine","5.2.3","5.2.2-alpine","5.2.2","5.2.1-alpine","5.2.1","5.2.0-alpine","5.2.0","5.16.4-alpine","5.16.4","5.16.3-alpine","5.16.3","5.16.2-alpine","5.16.2","5.16.1-alpine","5.16.1","5.16.0-alpine","5.16.0","5.15.3-alpine","5.15.3","5.15-alpine","5.15","5.15.2-alpine","5.15.2","5.15.1-alpine","5.15.1","5.15.0-alpine","5.15.0","5.14.4-alpine","5.14.4","5.14-alpine","5.14","5.14.3-alpine","5.14.3","5.14.2-alpine","5.14.2","5.14.1-alpine","5.14.1","5.14.0-alpine","5.14.0","5.13.6-alpine","5.13.6","5.13-alpine","5.13","5.13.5-alpine","5.13.5","5.13.4-alpine","5.13.4","5.13.3-alpine","5.13.3","5.13.1-alpine","5.13.1","5.13.0-alpine","5.13.0","5.12.4-alpine","5.12.4","5.12-alpine","5.12","5.12.3-alpine","5.12.3","5.12.2-alpine","5.12.2","5.12.1-alpine","5.12.1","5.12.0-alpine","5.12.0","5.11.4-alpine","5.11.4","5.11-alpine","5.11","5.11.3-alpine","5.11.3","5.11.2-alpine","5.11.2","5.11.1-alpine","5.11.1","5.10.4-alpine","5.10.4","5.10-alpine","5.10","5.10.3-alpine","5.10.3","5.10.2-alpine","5.10.2","5.10.1-alpine","5.10.1","5.10.0-alpine","5.10.0","5.1.9-alpine","5.1.9","5.1-alpine","5.1","5.1.8-alpine","5.1.8","5.1.7-alpine","5.1.7","5.1.6-alpine","5.1.6","5.1.5-alpine","5.1.5","5.1.4-alpine","5.1.4","5.1.3-alpine","5.1.3","5.1.2-alpine","5.1.2","5.1.1-alpine","5.1.1","5.1.0-alpine","5.1.0","5.0.9-alpine","5.0.9","5.0.8-alpine","5.0.8","5.0.7-alpine","5.0.7","5.0.6-alpine","5.0.6","5.0.5-alpine","5.0.5","5.0.4-alpine","5.0.4","5.0.2-alpine","5.0.2","5.0.10-alpine","5.0.10","5.0-alpine","5.0","5.0.1-alpine","5.0.1","5.0.0-alpine","5.0.0","4.14.5-alpine","4.14.5","4.1.6-alpine","4.1.6","4.1-alpine","4.1","4.11.2-alpine","4.11.2","4.11-alpine","4.11","5.8.4","5.8","4.14","4.13","4.12","4.10","4.9","4.8","4.6","4.7","4.4","4.5","4.3","4.2","4.0"],"mysql":["latest","oraclelinux8","oracle","innovation-oraclelinux8","innovation-oracle","innovation","8.3.0-oraclelinux8","8.3.0-oracle","8.3.0","8.3-oraclelinux8","8.3-oracle","8.3","8.0.36-oraclelinux8","8.0.36-oracle","8.0.36","8.0-oraclelinux8","8.0-oracle","8.0","8-oraclelinux8","8-oracle","8","8.0.36-debian","8.0.36-bookworm","8.0-debian","8.0-bookworm","8.0.36-bullseye","8.0-bullseye","8.2.0-oraclelinux8","8.2.0-oracle","8.2.0","8.2-oraclelinux8","8.2-oracle","8.2","8.0.35-oraclelinux8","8.0.35-oracle","8.0.35","8.0.35-debian","8.0.35-bullseye","5.7.44-oraclelinux7","5.7-oraclelinux7","5-oraclelinux7","5.7.44-oracle","5.7.44","5.7-oracle","5.7","5-oracle","5","8.1.0-oracle","8.1.0","8.1-oracle","8.1","8.0.34-oracle","8.0.34","5.7.43-oracle","5.7.43","8.0.34-debian","debian","8-debian","5.7.42-debian","5.7-debian","5-debian","5.7.42-oracle","5.7.42","8.0.33-oracle","8.0.33","8.0.33-debian","8.0.32-debian","5.7.41-debian","8.0.32-oracle","8.0.32","5.7.41-oracle","5.7.41","8.0.31-debian","5.7.40-debian","8.0.31-oracle","8.0.31","5.7.40-oracle","5.7.40","8.0.30-oracle","8.0.30","5.7.39-oracle","5.7.39","8.0.30-debian","5.7.39-debian","8.0.29-oracle","8.0.29","5.7.38-oracle","5.7.38","8.0.29-debian","5.7.38-debian","8.0.28-debian","8.0.28","5.7.37-debian","5.7.37","8.0.28-oracle","5.7.37-oracle","8.0.27","5.7.36","5.6.51","5.6","8.0.26","5.7.35","8.0.25","5.7.34","8.0.24","8.0.23","5.7.33","8.0.22","5.7.32","5.6.50","8.0.21","5.7.31","5.6.49","8.0.20","5.7.30","5.6.48","8.0.19","5.7.29","5.6.47","8.0.18","5.7.28","5.6.46","8.0.17","5.7.27","5.6.45","8.0.16","5.7.26","5.6.44","5.5.62","5.5","5.6.43","5.7.25","8.0.15","8.0.14","5.6.42","5.7.24","8.0.13","5.5.61","5.6.41","5.7.23","8.0.12","5.5.60","5.6.40","5.7.22","8.0.11","5.5.59","5.6.39","5.7.21","8.0.4","8.0.4-rc","8.0.3","5.5.58","5.6.38","5.7.20","5.5.57","5.6.37","5.7.19","8.0.2","5.5.56","5.6.36","5.7.18","8.0.1","5.5.55","5.6.35","5.7.17","8.0.0","5.5.54","5.5.53","5.7.16","5.6.34","5.5.52","5.6.33","5.7.15","5.5.51","5.6.32","5.7.14","5.5.50","5.6.31","5.7.13","5.7.12","5.6.30","5.5.49","5.7.11","5.6.29","5.5.48","5.7.10","5.6.28","5.5.47","5.6.27","5.7.9","5.5.46","5.5.40","5.7.4","5.6.17","5.5.41","5.7.5-m15","5.6.21","5.6.20","5.6.22","5.7.4-m14","5.7.5","5.7.8","5.7.8-rc","5.6.26","5.5.45","5.7.7","5.7.7-rc","5.6.25","5.5.44","5.6.24","5.5.43","5.7.6","5.7.6-m16","5.6.23","5.5.42"],"postgres":["latest","bullseye","bookworm","16.2-bullseye","16.2-bookworm","16.2","16-bullseye","16-bookworm","16","15.6-bullseye","15.6-bookworm","15.6","15-bullseye","15-bookworm","15","14.11-bullseye","14.11-bookworm","14.11","14-bullseye","14-bookworm","14","13.14-bullseye","13.14-bookworm","13.14","13-bullseye","13-bookworm","13","12.18-bullseye","12.18-bookworm","12.18","12-bullseye","12-bookworm","12","alpine3.19","alpine3.18","alpine","16.2-alpine3.19","16.2-alpine3.18","16.2-alpine","16-alpine3.19","16-alpine3.18","16-alpine","15.6-alpine3.19","15.6-alpine3.18","15.6-alpine","15-alpine3.19","15-alpine3.18","15-alpine","14.11-alpine3.19","14.11-alpine3.18","14.11-alpine","14-alpine3.19","14-alpine3.18","14-alpine","13.14-alpine3.19","13.14-alpine3.18","13.14-alpine","13-alpine3.19","13-alpine3.18","13-alpine","12.18-alpine3.19","12.18-alpine3.18","12.18-alpine","12-alpine3.19","12-alpine3.18","12-alpine","16.1-bullseye","15.5-bullseye","15.5-bookworm","15.5","14.10-bullseye","14.10-bookworm","14.10","13.13-bullseye","13.13-bookworm","13.13","12.17-bullseye","12.17-bookworm","12.17","16.1-bookworm","16.1","16.1-alpine3.19","16.1-alpine3.18","16.1-alpine","15.5-alpine3.19","15.5-alpine3.18","15.5-alpine","14.10-alpine3.19","14.10-alpine3.18","14.10-alpine","13.13-alpine3.19","13.13-alpine3.18","13.13-alpine","12.17-alpine3.19","12.17-alpine3.18","12.17-alpine","11.22-bullseye","11.22-bookworm","11-bullseye","11-bookworm","11.22-alpine3.19","11.22-alpine","11-alpine3.19","11-alpine","12.17-alpine3.17","12-alpine3.17","11.22-alpine3.18","11.22-alpine3.17","11-alpine3.18","11-alpine3.17","alpine3.17","16.1-alpine3.17","16-alpine3.17","15.5-alpine3.17","15-alpine3.17","13.13-alpine3.17","13-alpine3.17","14.10-alpine3.17","14-alpine3.17","12.16-bullseye","11.21-bullseye","11.21-bookworm","13.12-bullseye","13.12-bookworm","13.12","12.16-bookworm","12.16","14.9-bullseye","14.9-bookworm","14.9","16.0-bullseye","15.4-bullseye","15.4-bookworm","15.4","16.0-bookworm","16.0","16.0-alpine3.18","16.0-alpine3.17","16.0-alpine","15.4-alpine3.18","15.4-alpine3.17","15.4-alpine","14.9-alpine3.18","14.9-alpine3.17","14.9-alpine","13.12-alpine3.18","13.12-alpine3.17","13.12-alpine","12.16-alpine3.18","12.16-alpine3.17","12.16-alpine","11.21-alpine3.18","11.21-alpine3.17","11.21-alpine","16rc1-bullseye","16rc1-bookworm","16rc1","16rc1-alpine3.18","16rc1-alpine3.17","16rc1-alpine","16beta3-bullseye","16beta3-bookworm","16beta3","16beta3-alpine3.18","16beta3-alpine3.17","16beta3-alpine","16beta2-alpine3.18","16beta2-alpine3.17","16beta2-alpine","15.3-alpine3.18","15.3-alpine3.17","15.3-alpine","14.8-alpine3.18","14.8-alpine3.17","14.8-alpine","13.11-alpine3.18","13.11-alpine3.17","13.11-alpine","12.15-alpine3.18","12.15-alpine3.17","12.15-alpine","11.20-alpine3.18","11.20-alpine3.17","11.20-alpine","16beta2-bullseye","16beta2-bookworm","16beta2","15.3-bullseye","15.3-bookworm","15.3","14.8-bullseye","14.8-bookworm","14.8","13.11-bullseye","13.11-bookworm","13.11","12.15-bullseye","12.15-bookworm","12.15","11.20-bullseye","11.20-bookworm","16beta1-bookworm","16beta1-alpine3.18","16beta1-alpine3.17","16beta1-alpine","16beta1","16beta1-bullseye","15.2-bullseye","15.2-alpine3.17","15.2-alpine","15.2","14.7-bullseye","14.7-alpine3.17","14.7-alpine","14.7","13.10-bullseye","13.10-alpine3.17","13.10-alpine","13.10","12.14-bullseye","12.14-alpine3.17","12.14-alpine","12.14","11.19-bullseye","11.19-alpine3.17","11.19-alpine","15.1-bullseye","15.1","14.6-bullseye","14.6","13.9-bullseye","13.9","12.13-bullseye","12.13","11.18-bullseye","15.1-alpine3.17","15.1-alpine","14.6-alpine3.17","14.6-alpine","13.9-alpine3.17","13.9-alpine","12.13-alpine3.17","12.13-alpine","11.18-alpine3.17","11.18-alpine","alpine3.16","15.1-alpine3.16","15-alpine3.16","14.6-alpine3.16","14-alpine3.16","13.9-alpine3.16","13-alpine3.16","12.13-alpine3.16","12-alpine3.16","11.18-alpine3.16","11-alpine3.16","10.23-alpine3.16","10.23-alpine","10-alpine3.16","10-alpine","10.23-bullseye","10-bullseye","15.0-bullseye","14.5-bullseye","14.5","13.8-bullseye","13.8","12.12-bullseye","12.12","11.17-bullseye","10.22-bullseye","15.0","15.0-alpine3.16","15.0-alpine","14.5-alpine3.16","14.5-alpine","13.8-alpine3.16","13.8-alpine","12.12-alpine3.16","12.12-alpine","11.17-alpine3.16","11.17-alpine","10.22-alpine3.16","10.22-alpine","15rc2-alpine3.16","15rc2-alpine","15rc2-bullseye","15rc2","15rc1-alpine3.16","15rc1-alpine","15rc1-bullseye","15rc1","15beta4-bullseye","15beta4","15beta4-alpine3.16","15beta4-alpine","15beta3-bullseye","15beta3","15beta3-alpine3.16","15beta3-alpine","15beta2-alpine3.16","15beta2-alpine","14.4-alpine3.16","14.4-alpine","13.7-alpine3.16","13.7-alpine","12.11-alpine3.16","12.11-alpine","11.16-alpine3.16","11.16-alpine","10.21-alpine3.16","10.21-alpine","12.11-bullseye","12.11","15beta2-bullseye","15beta2","14.4-bullseye","14.4","13.7-bullseye","13.7","11.16-bullseye","10.21-bullseye","15beta1-bullseye","15beta1","11.16-stretch","11.16","11-stretch","11","10.21-stretch","10.21","10-stretch","10","15beta1-alpine3.16","15beta1-alpine","14.3-alpine3.16","14.3-alpine","14.3-bullseye","14.3","15beta1-alpine3.15","alpine3.15","14.3-alpine3.15","14-alpine3.15","13.7-alpine3.15","13-alpine3.15","12.11-alpine3.15","12-alpine3.15","11.16-alpine3.15","11-alpine3.15","10.21-alpine3.15","10-alpine3.15","14.2-bullseye","14.2","13.6-bullseye","13.6","12.10-bullseye","12.10","11.15-bullseye","10.20-bullseye","11.15-stretch","11.15","10.20-stretch","10.20","14.2-alpine3.15","14.2-alpine","13.6-alpine3.15","13.6-alpine","12.10-alpine3.15","12.10-alpine","11.15-alpine3.15","11.15-alpine","10.20-alpine3.15","10.20-alpine","9.6.24-bullseye","9.6-bullseye","9-bullseye","9.6.24-stretch","9.6.24","9.6-stretch","9.6","9-stretch","9","14.1-bullseye","14.1","13.5-bullseye","13.5","12.9-bullseye","12.9","11.14-stretch","11.14-bullseye","11.14","10.19-stretch","10.19-bullseye","10.19","9.6.24-alpine3.15","9.6.24-alpine","9.6-alpine3.15","9.6-alpine","9-alpine3.15","9-alpine","14.1-alpine3.15","14.1-alpine","13.5-alpine3.15","13.5-alpine","12.9-alpine3.15","12.9-alpine","11.14-alpine3.15","11.14-alpine","10.19-alpine3.15","10.19-alpine","alpine3.14","9.6.24-alpine3.14","9.6-alpine3.14","9-alpine3.14","14.1-alpine3.14","14-alpine3.14","13.5-alpine3.14","13-alpine3.14","12.9-alpine3.14","12-alpine3.14","11.14-alpine3.14","11-alpine3.14","10.19-alpine3.14","10-alpine3.14","9.6.23-stretch","9.6.23-bullseye","9.6.23-alpine3.14","9.6.23-alpine","9.6.23","14.0-bullseye","14.0-alpine3.14","14.0-alpine","14.0","13.4-bullseye","13.4-alpine3.14","13.4-alpine","13.4","12.8-bullseye","12.8-alpine3.14","12.8-alpine","12.8","11.13-stretch","11.13-bullseye","11.13-alpine3.14","11.13-alpine","11.13","10.18-stretch","10.18-bullseye","10.18-alpine3.14","10.18-alpine","10.18","14rc1-bullseye","14rc1","14rc1-alpine3.14","14rc1-alpine","buster","9.6.23-buster","9.6-buster","9-buster","14beta3-buster","14beta3","13.4-buster","13-buster","12.8-buster","12-buster","11.13-buster","11-buster","10.18-buster","10-buster","14beta3-alpine3.14","14beta3-alpine","9.6.22-alpine3.14","9.6.22-alpine","14beta2-alpine3.14","14beta2-alpine","13.3-alpine3.14","13.3-alpine","12.7-alpine3.14","12.7-alpine","11.12-alpine3.14","11.12-alpine","10.17-alpine3.14","10.17-alpine","9.6.22-stretch","9.6.22-buster","9.6.22","14beta2-buster","14beta2","13.3-buster","13.3","12.7-buster","12.7","11.12-stretch","11.12-buster","11.12","10.17-stretch","10.17-buster","10.17","alpine3.13","9-alpine3.13","13.3-alpine3.13","13-alpine3.13","12.7-alpine3.13","12-alpine3.13","11.12-alpine3.13","11-alpine3.13","10.17-alpine3.13","10-alpine3.13","9.6.22-alpine3.13","9.6-alpine3.13","9.6.21","9.5.25","9.5","13.2","12.6","11.11","10.16","9.6.21-alpine","9.5.25-alpine","9.5-alpine","13.2-alpine","12.6-alpine","11.11-alpine","10.16-alpine","9.6.20","9.5.24","13.1","12.5","11.10","10.15","9.6.20-alpine","9.5.24-alpine","13.1-alpine","12.5-alpine","11.10-alpine","10.15-alpine","9.6.19-alpine","9.5.23-alpine","13.0-alpine","12.4-alpine","11.9-alpine","10.14-alpine","9.6.19","9.5.23","13.0","12.4","11.9","10.14","13-rc1-alpine","13-rc1","13-beta3","13-beta3-alpine","9.6.18","9.5.22","13-beta2","12.3","11.8","10.13","9.6.18-alpine","9.5.22-alpine","13-beta2-alpine","12.3-alpine","11.8-alpine","10.13-alpine","13-beta1-alpine","13-beta1","9.6.17","9.5.21","11.7","10.12","9.6.17-alpine","9.5.21-alpine","12.2-alpine","11.7-alpine","10.12-alpine","12.2","9.4.26-alpine","9.4.26","9.4-alpine","9.4","9.6.16","9.5.20","9.4.25","12.1","11.6","10.11","9.6.16-alpine","9.5.20-alpine","9.4.25-alpine","12.1-alpine","11.6-alpine","10.11-alpine","9.6.15-alpine","9.5.19-alpine","9.4.24-alpine","12.0-alpine","11.5-alpine","10.10-alpine","9.6.15","9.5.19","9.4.24","12.0","11.5","10.10","12-rc1-alpine","12-rc1","12-beta4-alpine","12-beta4","12-beta3","12-beta3-alpine","9.6.14-alpine","9.5.18-alpine","9.4.23-alpine","12-beta2-alpine","11.4-alpine","10.9-alpine","9.6.14","9.5.18","9.4.23","11.4","10.9","12-beta2","9.6.13-alpine","9.5.17-alpine","9.4.22-alpine","12-beta1-alpine","11.3-alpine","10.8-alpine","9.6.13","9.5.17","9.4.22","12-beta1","11.3","10.8","11.2","9.4.21","9.5.16","9.6.12","10.7","9.4.21-alpine","9.5.16-alpine","9.6.12-alpine","10.7-alpine","11.2-alpine","9.4.20","9.5.15","9.6.11","10.6","11.1","9.4.20-alpine","9.5.15-alpine","9.6.11-alpine","10.6-alpine","11.1-alpine","9.3","9.3.25","9.3-alpine","9.3.25-alpine","10.5-alpine","10.5","11.0","11.0-alpine","9.3.24","9.4.19","9.5.14","9.6.10","11-rc1","11-rc1-alpine","11-beta4-alpine","9.4.19-alpine","11-beta4","9.3.24-alpine","9.5.14-alpine","9.6.10-alpine","11-beta3-alpine","11-beta3","9.3.23","9.5.13","9.5.13-alpine","9.4.18","9.6.9","10.4-alpine","10.4","11-beta2-alpine","11-beta2","9.4.18-alpine","9.3.23-alpine","9.6.9-alpine","11-beta1","11-beta1-alpine","9.6.8","10.3","9.3.22-alpine","9.3.22","9.4.17-alpine","9.4.17","9.5.12-alpine","9.5.12","9.6.8-alpine","10.3-alpine","9.3.21","9.4.16","9.5.11","9.6.7","10.2-alpine","10.2","9.5.11-alpine","9.5.10","9.3.21-alpine","9.4.16-alpine","9.6.7-alpine","9.3.20-alpine","9.4.15-alpine","9.5.10-alpine","9.6.6-alpine","10.1-alpine","9.3.20","9.4.15","9.6.6","10.1","9.3.19-alpine","9.3.19","9.4.14-alpine","9.4.14","9.5.9-alpine","9.5.9","9.6.5-alpine","9.6.5","10.0-alpine","10.0","9.2","9.2.23","10-rc1","9.2-alpine","9.2.23-alpine","10-rc1-alpine","10-beta4","10-beta4-alpine","9.2.22-alpine","9.2.22","9.3.18-alpine","9.3.18","9.4.13-alpine","9.4.13","9.5.8-alpine","9.5.8","9.6.4-alpine","9.6.4","10-beta3-alpine","10-beta3","9.2.21-alpine","9.2.21","9.3.17-alpine","9.3.17","9.4.12-alpine","9.4.12","9.5.7-alpine","9.5.7","9.6.3-alpine","9.6.3","10-beta2-alpine","10-beta2","10-beta1-alpine","10-beta1","9.2.20-alpine","9.2.20","9.3.16-alpine","9.3.16","9.4.11-alpine","9.4.11","9.5.6-alpine","9.5.6","9.6.2-alpine","9.6.2","9.2.19-alpine","9.2.19","9.3.15-alpine","9.3.15","9.4.10-alpine","9.4.10","9.5.5-alpine","9.5.5","9.6.1-alpine","9.6.1","9.1.24","9.1","9.5.4","9.4.9","9.3.14","9.2.18","9.1.23","9.6.0","9.6-rc1","9.6-beta4","9.4.8","9.1.22","9.6-beta3","9.5.3","9.3.13","9.2.17","9.6-beta2","9.6-beta1","9.5.2","9.4.7","9.3.12","9.2.16","9.1.21","9.5.1","9.4.6","9.3.11","9.2.15","9.1.20","9.5.0","9.4.5","9.3.10","9.2.14","9.1.19","9.0","9.0.22","9.5-rc1","9.5-beta2","9.5-beta1","9.4-rc1","9.3.5","9.4-beta2","9.2.9","9.4.0","9.1.14","9.4-beta3","9.0.18","9.5-alpha2","9.4.4","9.3.9","9.2.13","9.1.18","9.5-alpha1","9.4.3","9.3.8","9.2.12","9.1.17","9.0.21","9.4.2","9.3.7","9.2.11","9.1.16","9.0.20","8","8.4","8.4.22","9.4.1","9.3.6","9.2.10","9.1.15","9.0.19"],"mssql":["2017-CU1-ubuntu","2017-CU10","2017-CU10-ubuntu","2017-CU11","2017-CU11-ubuntu","2017-CU12","2017-CU12-ubuntu","2017-CU13","2017-CU13-ubuntu","2017-CU14","2017-CU14-ubuntu","2017-CU15","2017-CU15-GDR","2017-CU15-GDR-ubuntu","2017-CU15-GDR1-ubuntu-16.04","2017-CU15-ubuntu","2017-CU16","2017-CU16-ubuntu","2017-CU17","2017-CU17-ubuntu","2017-CU18-ubuntu-16.04","2017-CU19-ubuntu-16.04","2017-CU2-ubuntu","2017-CU20","2017-CU20-ubuntu","2017-CU20-ubuntu-16.04","2017-CU21-ubuntu-16.04","2017-CU22-GDR1-ubuntu-16.04","2017-CU22-OD1-ubuntu-16.04","2017-CU22-ubuntu-16.04","2017-CU23-ubuntu-16.04","2017-CU24-ubuntu-16.04","2017-CU25-ubuntu-16.04","2017-CU26-ubuntu-16.04","2017-CU27-ubuntu-16.04","2017-CU28-ubuntu-16.04","2017-CU29-GDR1-ubuntu-16.04","2017-CU29-ubuntu-16.04","2017-CU3-ubuntu","2017-CU30-ubuntu-18.04","2017-CU31-GDR1-ubuntu-18.04","2017-CU31-GDR2-ubuntu-18.04","2017-CU31-ubuntu-18.04","2017-CU4-ubuntu","2017-CU5-ubuntu","2017-CU6-ubuntu","2017-CU7-ubuntu","2017-CU8-ubuntu","2017-CU9-ubuntu","2017-GA-ubuntu","2017-GDR-ubuntu","2017-GDR3","2017-GDR3-ubuntu","2017-GDR4","2017-GDR4-ubuntu","2017-cu16","2017-cu16-ubuntu","2017-cu17","2017-cu17-ubuntu","2017-cu19","2017-cu19-ubuntu","2017-latest","2017-latest-ubuntu","2019-CU1-ubuntu-16.04","2019-CU10-ubuntu-16.04","2019-CU10-ubuntu-18.04","2019-CU10-ubuntu-20.04","2019-CU11-ubuntu-16.04","2019-CU11-ubuntu-18.04","2019-CU11-ubuntu-20.04","2019-CU12-ubuntu-16.04","2019-CU12-ubuntu-18.04","2019-CU12-ubuntu-20.04","2019-CU13-ubuntu-16.04","2019-CU13-ubuntu-18.04","2019-CU13-ubuntu-20.04","2019-CU14-ubuntu-16.04","2019-CU14-ubuntu-18.04","2019-CU14-ubuntu-20.04","2019-CU15-ubuntu-16.04","2019-CU15-ubuntu-18.04","2019-CU15-ubuntu-20.04","2019-CU16-GDR1-ubuntu-16.04","2019-CU16-GDR1-ubuntu-18.04","2019-CU16-GDR1-ubuntu-20.04","2019-CU16-ubuntu-16.04","2019-CU16-ubuntu-18.04","2019-CU16-ubuntu-20.04","2019-CU17-ubuntu-18.04","2019-CU17-ubuntu-20.04","2019-CU18-GDR1-ubuntu-18.04","2019-CU18-GDR1-ubuntu-20.04","2019-CU18-ubuntu-18.04","2019-CU18-ubuntu-20.04","2019-CU19-ubuntu-18.04","2019-CU19-ubuntu-20.04","2019-CU2-ubuntu-16.04","2019-CU20-ubuntu-18.04","2019-CU20-ubuntu-20.04","2019-CU21-ubuntu-20.04","2019-CU22-GDR1-ubuntu-20.04","2019-CU22-ubuntu-20.04","2019-CU23-ubuntu-20.04","2019-CU24-ubuntu-20.04","2019-CU25-GDR1-ubuntu-20.04","2019-CU25-ubuntu-20.04","2019-CU26-ubuntu-20.04","2019-CU3-ubuntu-16.04","2019-CU3-ubuntu-18.04","2019-CU4-ubuntu-16.04","2019-CU4-ubuntu-18.04","2019-CU5-ubuntu-16.04","2019-CU5-ubuntu-18.04","2019-CU6-ubuntu-16.04","2019-CU6-ubuntu-18.04","2019-CU8-GDR1-ubuntu-16.04","2019-CU8-GDR1-ubuntu-18.04","2019-CU8-OD2-ubuntu-16.04","2019-CU8-OD2-ubuntu-18.04","2019-CU8-ubuntu-16.04","2019-CU8-ubuntu-18.04","2019-CU9-ubuntu-16.04","2019-CU9-ubuntu-18.04","2019-GA-ubuntu-16.04","2019-GDR1-ubuntu-16.04","2019-GDR2-ubuntu-16.04","2019-gdr2-ubuntu-16.04","2019-gdr3-ubuntu-16.04","2019-latest","2022-CTP2.0-ubuntu","2022-CTP2.0-ubuntu-20.04","2022-CU1-ubuntu-20.04","2022-CU10-GDR1-ubuntu-20.04","2022-CU10-GDR1-ubuntu-22.04","2022-CU10-ubuntu-20.04","2022-CU10-ubuntu-22.04","2022-CU11-ubuntu-20.04","2022-CU11-ubuntu-22.04","2022-CU12-GDR1-ubuntu-20.04","2022-CU12-GDR1-ubuntu-22.04","2022-CU12-ubuntu-20.04","2022-CU12-ubuntu-22.04","2022-CU3-ubuntu-20.04","2022-CU4-ubuntu-20.04","2022-CU5-ubuntu-20.04","2022-CU6-ubuntu-20.04","2022-CU7-ubuntu-20.04","2022-CU8-GDR1-ubuntu-20.04","2022-CU8-ubuntu-20.04","2022-CU9-ubuntu-20.04","2022-RC0-ubuntu-20.04","2022-RC1-ubuntu-20.04","2022-RTM-CU1-ubuntu-20.04","2022-RTM-CU2-ubuntu-20.04","2022-RTM-GDR1-ubuntu-20.04","2022-RTM-ubuntu-20.04","2022-latest","2022-preview-ubuntu-22.04","latest","latest-ubuntu"]} \ No newline at end of file +{"jira":["latest","9.8.0-ubuntu-jdk11","9.8.0-jdk11","9.8.0","9.5-ubuntu-jdk11","9.5-jdk11","9.5.1-ubuntu-jdk11","9.5.1-jdk11","9.5.1","9.5","9.14.0-ubuntu-jdk11","9.14.0-jdk11","9.14-ubuntu-jdk11","9.14.0","9.14-jdk11","9.14.1-ubuntu-jdk11","9.14","9.14.1","9.12.5-ubuntu-jdk11","9.12.5-jdk11","9.12.5","9.0-ubuntu-jdk11","9.0-jdk11","9.0.0-ubuntu-jdk11","9.0.0-jdk11","9.0.0","9.0","9.8.1-ubuntu-jdk11","9.8.1-jdk11","9.8.1","9.14.1-jdk11","9.10-ubuntu-jdk11","9.10-jdk11","9.10.2-ubuntu-jdk11","9.10.2-jdk11","9.10.2","9.8","9.10","9.12.6-jdk11","9.12.6-ubuntu-jdk11","9.12.6","9.8-ubuntu-jdk11","9.8-jdk11","9.8.2-ubuntu-jdk11","9.8.2-jdk11","9.8.2","9.6-ubuntu-jdk11","9.6-jdk11","9.6.0-ubuntu-jdk11","9.6.0-jdk11","9.6.0","9.6","9.4.2-ubuntu-jdk11","9.4.2-jdk11","9.4.2","9.3.0-ubuntu-jdk11","9.3.0-jdk11","9.3.0","9.14.0-ubi9-jdk17","9.14.0-ubi9","9.12.7-ubi9-jdk17","9.12.7-ubi9","9.5.0-ubi9-jdk17","9.5.0-ubi9","9.12-ubuntu-jdk11","9.12-jdk11","9.12.7-ubuntu-jdk11","9.12.7-jdk11","9.12.7","9.12","9.1.0-ubuntu-jdk11","9.1.0-jdk11","ubuntu-jdk11","9.1.0","jdk11","9-ubuntu-jdk11","9-jdk11","9.4-ubuntu-jdk11","9.4-jdk11","9.4.20-ubuntu-jdk11","9.4.20-jdk11","9.4.20","9.4","9.3.1-ubuntu-jdk11","9.3.1-jdk11","9.3.1","9.15-ubuntu-jdk11","9.15-jdk11","9.15.0-ubuntu-jdk11","9.15.0-jdk11","9.15.0","9.15","9.12.0-ubuntu-jdk11","9.12.0-jdk11","9.12.0","9.11.0-ubuntu-jdk11","9.11.0-jdk11","9.11.0","9","9.8.0-ubi9-jdk17","9.8.0-ubi9","9.7.0-ubi9-jdk17","9.7.0-ubi9","9.5.1-ubi9-jdk17","9.5.1-ubi9","9.1-ubuntu-jdk11","9.1-jdk11","9.14.1-ubi9-jdk17","9.14.1-ubi9","9.1.1-ubuntu-jdk11","9.1.1-jdk11","9.1.1","9.9.0-ubuntu-jdk11","9.1","9.9.0-ubi9-jdk17","9.9.0-ubi9","9.9.0-jdk11","9.9.0","9.7.0-ubuntu-jdk11","9.7.0-jdk11","9.7.0","9.4.6-ubuntu-jdk11","9.4.6-jdk11","9.4.6","9.4.1-ubuntu-jdk11","9.4.1-jdk11","9.4.15-ubuntu-jdk11","9.4.15-jdk11","9.4.15","9.4.1","9.3.2-ubuntu-jdk11","9.3.2-jdk11","9.3.2","9.12.1-ubuntu-jdk11","9.12.1-jdk11","9.12.1","9.11.1-ubuntu-jdk11","9.11.1-jdk11","9.11.1","9.8.1-ubi9-jdk17","9.8.1-ubi9","9.7.1-ubi9-jdk17","9.7.1-ubi9","9.4.10-ubuntu-jdk11","9.4.10-jdk11","9.4.10","9.13.0-ubuntu-jdk11","9.13.0-ubi9-jdk17","9.13.0-ubi9","9.13.0-jdk11","9.13.0","9.11.3-ubi9-jdk17","9.11.3-ubi9","9.10.2-ubi9-jdk17","9.10.2-ubi9","9.9.1-ubuntu-jdk11","9.9.1-ubi9-jdk17","9.9.1-ubi9","9.9.1-jdk11","9.9.1","9.7.1-ubuntu-jdk11","9.7.1-jdk11","9.7.1","9.4.7-ubuntu-jdk11","9.4.7-jdk11","9.4.7","9.4.16-ubuntu-jdk11","9.4.16-jdk11","9.4.16","9.3-ubuntu-jdk11","9.3-jdk11","9.3.3-ubuntu-jdk11","9.3.3-jdk11","9.3.3","9.3","9.2.0-ubuntu-jdk11","9.2.0-jdk11","9.2.0","9.12.4-ubi9-jdk17","9.12.4-ubi9","9.12.1-ubi9-jdk17","9.12.1-ubi9","9.11.2-ubuntu-jdk11","9.11.2-jdk11","9.11.2","ubi9-jdk17","ubi9","9.8.2-ubi9-jdk17","9.8.2-ubi9","9.7.2-ubi9-jdk17","9.7.2-ubi9","9.6.0-ubi9-jdk17","9.6.0-ubi9","9.4.8-ubuntu-jdk11","9.4.8-jdk11","9.4.8","9.4.3-ubuntu-jdk11","9.4.3-jdk11","9.4.3","9.4.11-ubuntu-jdk11","9.4.11-jdk11","9.4.11","9.15.0-ubi9-jdk17","9.15.0-ubi9","9.13-ubuntu-jdk11","9.13-jdk11","9.13.1-ubuntu-jdk11","9.13.1-ubi9-jdk17","9.13.1-ubi9","9.13.1-jdk11","9.13.1","9.13","9.12.2-ubuntu-jdk11","9.12.2-jdk11","9.12.2","9.9-ubuntu-jdk11","9.9-jdk11","9.9.2-ubuntu-jdk11","9.9.2-ubi9-jdk17","9.9.2-ubi9","9.9.2-jdk11","9.9.2","9.9","9.7-ubuntu-jdk11","9.7-jdk11","9.7.2-ubuntu-jdk11","9.7.2-jdk11","9.7.2","9.7","9.4.17-ubuntu-jdk11","9.4.17-jdk11","9.4.17","9.2-ubuntu-jdk11","9.2-jdk11","9.2.1-ubuntu-jdk11","9.2.1-jdk11","9.2.1","9.2","9.12.5-ubi9-jdk17","9.12.5-ubi9","9.11-ubuntu-jdk11","9.11-jdk11","9.11.3-ubuntu-jdk11","9.11.3-jdk11","9.11.3","9.11.1-ubi9-jdk17","9.11.1-ubi9","9.11","9.10.0-ubuntu-jdk11","9.10.0-ubi9-jdk17","9.10.0-ubi9","9.10.0-jdk11","9.10.0","9.4.9-ubuntu-jdk11","9.4.9-jdk11","9.4.9","9.4.4-ubuntu-jdk11","9.4.4-jdk11","9.4.4","9.4.12-ubuntu-jdk11","9.4.12-jdk11","9.4.12","9.12.3-ubuntu-jdk11","9.12.3-jdk11","9.12.3","9.12.2-ubi9-jdk17","9.12.2-ubi9","9.4.18-ubuntu-jdk11","9.4.18-jdk11","9.4.18","9.5.0-ubuntu-jdk11","9.5.0-jdk11","9.5.0","9.4.5-ubuntu-jdk11","9.4.5-jdk11","9.4.5","9.4.19-ubuntu-jdk11","9.4.19-jdk11","9.4.19","9.4.14-ubuntu-jdk11","9.4.14-jdk11","9.4.14","9.4.0-ubuntu-jdk11","9.4.0-jdk11","9.4.0","9.12.6-ubi9-jdk17","9.12.6-ubi9","9.12.4-ubuntu-jdk11","9.12.4-jdk11","9.12.4","9.12.3-ubi9-jdk17","9.12.3-ubi9","9.12.0-ubi9-jdk17","9.12.0-ubi9","9.11.2-ubi9-jdk17","9.11.2-ubi9","9.11.0-ubi9-jdk17","9.11.0-ubi9","9.10.1-ubuntu-jdk11","9.10.1-ubi9-jdk17","9.10.1-ubi9","9.10.1-jdk11","9.10.1","10.0.0-EAP02-ubi9-jdk17","10.0.0-EAP02","10.0.0-EAP02-ubi9","10.0.0-EAP02-ubuntu-jdk11","10.0.0-EAP02-jdk11","9.15.0-RC","9.15.0-RC-jdk11","9.15.0-RC-ubuntu-jdk11","9.15.0-RC-ubi9","9.15.0-RC-ubi9-jdk17","9.15.0-EAP02-ubi9-jdk17","9.15.0-EAP02-ubi9","9.15.0-EAP02-jdk11","9.15.0-EAP02-ubuntu-jdk11","9.15.0-EAP02","8.22.3-ubuntu-jdk11","8.22.3-jdk11","8.22.3","8.22.4-ubuntu-jdk11","8.22.4-jdk11","8.22.4","8.22.5-ubuntu-jdk11","8.22.5","8.22.5-jdk11","8.16-ubuntu-jdk11","8.16-jdk11","8.16.2-ubuntu-jdk11","8.16.2-jdk11","8.16.2","8.16","8-ubuntu-jdk11","8-jdk11","8.22-ubuntu-jdk11","8.22-jdk11","8.22.6-ubuntu-jdk11","8.22.6-jdk11","8.22.6","8.22","8","8.17.0-ubuntu-jdk11","8.17.0-jdk11","8.17.0","8.20.21-ubuntu-jdk11","8.20.21-jdk11","8.20.21","8.20.4-ubuntu-jdk11","8.20.4-jdk11","8.20.4","8.17-ubuntu-jdk11","8.17-jdk11","8.17.1-ubuntu-jdk11","8.17.1-jdk11","8.17.1","8.17","8.20.22-ubuntu-jdk11","8.20.22-jdk11","8.20.22","8.13.5-ubuntu-jdk11","8.13.5-jdk11","8.13.5","8.20.5-ubuntu-jdk11","8.20.5-jdk11","8.20.5","8.13.19-ubuntu-jdk11","8.13.19-jdk11","8.13.19","8.13.6-jdk11","8.20.23-ubuntu-jdk11","8.20.23-jdk11","8.20.23","8.20.11-ubuntu-jdk11","8.20.11-jdk11","8.20.11","8.13.6-ubuntu-jdk11","8.13.6","8.13.2-ubuntu-jdk11","8.13.2-jdk11","8.13.2","8.20.6-ubuntu-jdk11","8.20.6-jdk11","8.20.6","8.18.0-ubuntu-jdk11","8.18.0-jdk11","8.18.0","8.13.0-ubuntu-jdk11","8.13.0-jdk11","8.13.0","8.20.24-ubuntu-jdk11","8.20.24-jdk11","8.20.24","8.20.12-ubuntu-jdk11","8.20.12-jdk11","8.20.12","8.13.7-ubuntu-jdk11","8.13.7-jdk11","8.20.7-ubuntu-jdk11","8.13.7","8.20.7-jdk11","8.20.7","8.18.1-ubuntu-jdk11","8.18.1-jdk11","8.18.1","8.13.20-ubuntu-jdk11","8.13.20-jdk11","8.13.20","8.13.1-ubuntu-jdk11","8.13.1-jdk11","8.13.1","8.13.8-ubuntu-jdk11","8.13.8-jdk11","8.13.8","8.20.25-ubuntu-jdk11","8.20.25-jdk11","8.20.25","8.20.13-ubuntu-jdk11","8.20.13-jdk11","8.20.13","8.13.21-ubuntu-jdk11","8.13.21-jdk11","8.13.21","8.13.10-ubuntu-jdk11","8.13.10-jdk11","8.20.8-ubuntu-jdk11","8.13.10","8.20.8-jdk11","8.20.8","8.18-ubuntu-jdk11","8.18-jdk11","8.18.2-ubuntu-jdk11","8.18.2-jdk11","8.18.2","8.18","8.13.9-ubuntu-jdk11","8.13.9-jdk11","8.13.9","8.20.26-ubuntu-jdk11","8.20.26-jdk11","8.20.26","8.20.14-ubuntu-jdk11","8.20.14-jdk11","8.20.14","8.13.22-ubuntu-jdk11","8.13.22-jdk11","8.13.22","8.20.9-ubuntu-jdk11","8.20.9-jdk11","8.20.9","8.14.0-ubuntu-jdk11","8.14.0-jdk11","8.14.0","8.13.11-ubuntu-jdk11","8.13.11-jdk11","8.13.11","8.20.15-ubuntu-jdk11","8.20.15-jdk11","8.20.15","8.13.24-ubuntu-jdk11","8.13.24-jdk11","8.13.24","8.20.27-ubuntu-jdk11","8.20.27-jdk11","8.20.27","8.19.0-ubuntu-jdk11","8.19.0-jdk11","8.19.0","8.14-ubuntu-jdk11","8.14-jdk11","8.14.1-ubuntu-jdk11","8.14.1-jdk11","8.14.1","8.14","8.13.12-ubuntu-jdk11","8.13.12-jdk11","8.13.12","8.21.0-ubuntu-jdk11","8.21.0-jdk11","8.21.0","8.20.28-ubuntu-jdk11","8.20.28-jdk11","8.20.28","8.20.16-ubuntu-jdk11","8.20.16-jdk11","8.20.16","8.13.25-ubuntu-jdk11","8.13.25-jdk11","8.13.25","8.13.13-ubuntu-jdk11","8.13.13-jdk11","8.13.13","8.21-ubuntu-jdk11","8.21-jdk11","8.21.1-ubuntu-jdk11","8.21.1-jdk11","8.21.1","8.21","8.19-ubuntu-jdk11","8.19-jdk11","8.19.1-ubuntu-jdk11","8.19.1-jdk11","8.19.1","8.19","8.20.17-ubuntu-jdk11","8.20.17-jdk11","8.20.17","8.15.0-ubuntu-jdk11","8.15.0-jdk11","8.15.0","8.13.26-ubuntu-jdk11","8.13.26-jdk11","8.13.26","8.13.14-ubuntu-jdk11","8.13.14-jdk11","8.20.29-ubuntu-jdk11","8.20.29-jdk11","8.20.29","8.13.14","8.13-ubuntu-jdk11","8.13-jdk11","8.13.27-ubuntu-jdk11","8.13.27-jdk11","8.13.27","8.13","8.20.3-ubuntu-jdk11","8.20.3-jdk11","8.20.3","8.20.18-ubuntu-jdk11","8.20.18-jdk11","8.20.18","8.15-ubuntu-jdk11","8.15-jdk11","8.15.1-ubuntu-jdk11","8.15.1-jdk11","8.15.1","8.15","8.13.15-ubuntu-jdk11","8.13.15-jdk11","8.13.15","8.22.0-ubuntu-jdk11","8.22.0-jdk11","8.22.0","8.20.19-ubuntu-jdk11","8.20.19-jdk11","8.20.19","8.20.0-ubuntu-jdk11","8.20.0-jdk11","8.20.0","8.13.16-ubuntu-jdk11","8.13.16-jdk11","8.20-ubuntu-jdk11","8.13.16","8.20-jdk11","8.20.30-ubuntu-jdk11","8.20.30-jdk11","8.20.30","8.20","8.22.1-ubuntu-jdk11","8.22.1-jdk11","8.22.1","8.20.2-ubuntu-jdk11","8.20.2-jdk11","8.20.2","8.20.1-ubuntu-jdk11","8.20.1-jdk11","8.20.1","8.13.17-ubuntu-jdk11","8.13.17-jdk11","8.13.17","8.16.0-ubuntu-jdk11","8.16.0-jdk11","8.16.0","8.13.3-ubuntu-jdk11","8.13.3-jdk11","8.13.3","8.22.2-ubuntu-jdk11","8.22.2-jdk11","8.22.2","8.20.20-ubuntu-jdk11","8.20.20-jdk11","8.20.20","8.20.10-ubuntu-jdk11","8.20.10-jdk11","8.20.10","8.13.4-ubuntu-jdk11","8.13.4-jdk11","8.13.4","8.13.18-ubuntu-jdk11","8.13.18-jdk11","8.13.18","8.16.1-ubuntu-jdk11","8.16.1-jdk11","8.16.1","9.15.0-EAP01-ubuntu-jdk11","9.15.0-EAP01","9.15.0-EAP01-jdk11","10.0.0-EAP01-jdk11","10.0.0-EAP01-ubuntu-jdk11","10.0.0-EAP01","9.14.0-RC-ubuntu-jdk11","9.14.0-RC-jdk11","9.14.0-RC","9.14.0-EAP02","9.14.0-EAP02-ubuntu-jdk11","9.14.0-EAP02-jdk11","9.14.0-EAP01-ubuntu-jdk11","9.14.0-EAP01-jdk11","9.14.0-EAP01","9.13.0-EAP02","9.13.0-EAP02-ubuntu-jdk11","9.13.0-EAP02-jdk11","9.4.13-jdk11","9.4.13","9.4.13-ubuntu-jdk11","9.13.0-EAP01-ubuntu-jdk11","9.13.0-EAP01-jdk11","9.13.0-EAP01","8.11.0-ubuntu-jdk11","8.11.0-jdk11","8.11.0","8.11-ubuntu-jdk11","8.11-jdk11","8.11.1-ubuntu-jdk11","8.11.1-jdk11","8.11.1","8.11","8.12.0-ubuntu-jdk11","8.12.0-jdk11","8.12.0","8.12.1-ubuntu-jdk11","8.12.1-jdk11","8.12.1","8.12.2-ubuntu-jdk11","8.12.2-jdk11","8.12.2","8.12-ubuntu-jdk11","8.12-jdk11","8.12.3-ubuntu-jdk11","8.12.3-jdk11","8.12.3","8.12","9.12.0-EAP01-ubuntu-jdk11","9.12.0-EAP01-jdk11","9.12.0-EAP01","9.11.0-RC-jdk11","9.11.0-RC-ubuntu-jdk11","9.11.0-RC","9.11.0-EAP02-ubuntu-jdk11","9.11.0-EAP02-jdk11","9.11.0-EAP02","9.11.0-EAP01-ubuntu-jdk11","9.11.0-EAP01-jdk11","9.11.0-EAP01","9.10.0-RC-ubuntu-jdk11","9.10.0-RC-jdk11","9.10.0-RC","9.10.0-EAP02-ubuntu-jdk11","9.10.0-EAP02-jdk11","9.10.0-EAP02","9.10.0-EAP01-ubuntu-jdk11","9.10.0-EAP01","9.10.0-EAP01-jdk11","9.9.0-RC02-jdk11","9.9.0-RC02-ubuntu-jdk11","9.9.0-RC02","9.9.0-RC01-jdk11","9.9.0-RC01-ubuntu-jdk11","9.9.0-RC01","9.9.0-EAP02-ubuntu-jdk11","9.9.0-EAP02-jdk11","9.9.0-EAP02","9.9.0-EAP01-jdk11","9.9.0-EAP01-ubuntu-jdk11","9.9.0-EAP01","9.8.0-RC01-jdk11","9.8.0-RC01-ubuntu-jdk11","9.8.0-RC01","9.8.0-EAP02","eap-jdk11","eap","9.8.0-EAP02-ubuntu-jdk11","eap-ubuntu-jdk11","9.8.0-EAP02-jdk11","9.8.0-EAP01","9.8.0-EAP01-jdk11","9.8.0-EAP01-ubuntu-jdk11","9.7.0-RC01-jdk11","9.7.0-RC01","9.7.0-RC01-ubuntu-jdk11","9.7.0-EAP02","9.7.0-EAP02-ubuntu-jdk11","9.7.0-EAP02-jdk11","9.7.0-EAP01-ubuntu-jdk11","9.7.0-EAP01-jdk11","9.7.0-EAP01","9.6.0-RC01","9.6.0-RC01-ubuntu-jdk11","9.6.0-EAP02-ubuntu-jdk11","9.6.0-EAP02","9.6.0-EAP02-jdk11","9.6.0-EAP01","9.6.0-EAP01-jdk11","9.6.0-EAP01-ubuntu-jdk11","9.5.0-RC01","9.5.0-RC01-ubuntu-jdk11","9.5.0-RC01-jdk11","9.5.0-EAP03-ubuntu-jdk11","9.5.0-EAP03-jdk11","9.5.0-EAP03","9.5.0-EAP02-ubuntu-jdk11","9.5.0-EAP02","9.5.0-EAP02-jdk11","9.4.0-RC02-jdk11","9.4.0-RC02","9.4.0-RC02-ubuntu-jdk11","9.4.0-RC01-ubuntu-jdk11","9.4.0-RC01-jdk11","9.4.0-RC01","9.4.0-EAP01","9.4.0-EAP01-ubuntu-jdk11","9.4.0-EAP01-jdk11","9.5.0-EAP01-ubuntu-jdk11","9.5.0-EAP01","9.5.0-EAP01-jdk11","9.3.0-RC01","9.3.0-RC01-ubuntu-jdk11","9.3.0-RC01-jdk11","9.3.0-EAP02-jdk11","9.3.0-EAP02","9.3.0-EAP02-ubuntu-jdk11","9.3.0-EAP01-jdk11","9.3.0-EAP01-ubuntu-jdk11","9.3.0-EAP01","9.2.0-RC01-ubuntu-jdk11","9.2.0-RC01-jdk11","9.2.0-RC01","9.2.0-EAP03-jdk11","9.2.0-EAP03-ubuntu-jdk11","9.2.0-EAP03","9.2.0-EAP02-ubuntu-jdk11","9.2.0-EAP02-jdk11","9.2.0-EAP02","9.2.0-EAP01-jdk11","9.2.0-EAP01-ubuntu-jdk11","9.2.0-EAP01","9.1.0-RC01-ubuntu-jdk11","9.1.0-RC01-jdk11","9.1.0-RC01","9.1.0-EAP02-ubuntu-jdk11","9.1.0-EAP02-jdk11","9.1.0-EAP02","9.1.0-EAP01-jdk11","9.1.0-EAP01-ubuntu-jdk11","9.1.0-EAP01","8.8.0-ubuntu-jdk11","8.8.0-jdk11","8.8.0","8.9-ubuntu-jdk11","8.9-jdk11","8.9.1-ubuntu-jdk11","8.9.1-jdk11","8.9.1","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.9.0","8.9","8.8-ubuntu-jdk11","8.8-jdk11","8.8.1-ubuntu-jdk11","8.8.1-jdk11","8.8.1","8.8","8.10-ubuntu-jdk11","8.10-jdk11","8.10.1-ubuntu-jdk11","8.10.1-jdk11","8.10.1","8.10.0-ubuntu-jdk11","8.10.0-jdk11","8.10.0","8.10","9.0.0-RC02-jdk11","9.0.0-RC02-ubuntu-jdk11","9.0.0-RC02","9.0.0-RC01-jdk11","9.0.0-RC01-ubuntu-jdk11","9.0.0-RC01","9.0.0-EAP04-ubuntu-jdk11","9.0.0-EAP04-jdk11","9.0.0-EAP04","9.0.0-EAP03-ubuntu-jdk11","9.0.0-EAP03-jdk11","9.0.0-EAP03","9.0.0-EAP02-ubuntu-jdk11","9.0.0-EAP02-jdk11","9.0.0-EAP02","9.0.0-EAP01-ubuntu-jdk11","9.0.0-EAP01-jdk11","9.0.0-EAP01","8.22.0-RC01-ubuntu-jdk11","8.22.0-RC01-jdk11","8.22.0-RC01","8.22.0-EAP02-ubuntu-jdk11","8.22.0-EAP02-jdk11","8.22.0-EAP02","8.22.0-EAP01-ubuntu-jdk11","8.22.0-EAP01-jdk11","8.22.0-EAP01","8.21.0-RC01-ubuntu-jdk11","8.21.0-RC01-jdk11","8.21.0-RC01","8.21.0-EAP02-ubuntu-jdk11","8.21.0-EAP02-jdk11","8.21.0-EAP02","8.21.0-EAP01-ubuntu-jdk11","8.21.0-EAP01-jdk11","8.21.0-EAP01","8.20-ubuntu-jdk-11","8.20.7-ubuntu-jdk-11","8.20.0-RC01-ubuntu-jdk11","8.20.0-RC01-jdk11","8.20.0-RC01","8.20.0-EAP01-ubuntu-jdk11","8.20.0-EAP01-jdk11","8.20.0-EAP01","8.19.0-RC01-ubuntu-jdk11","8.19.0-RC01-jdk11","8.19.0-RC01","8.19.0-EAP02-ubuntu-jdk11","8.19.0-EAP02-jdk11","8.19.0-EAP02","8.19.0-EAP01-ubuntu-jdk-11","8.19.0-EAP01-jdk11","8.19.0-EAP01","8.18.0-RC01-ubuntu-jdk11","8.18.0-RC01-jdk11","8.18.0-RC01","8.18.0-EAP02-ubuntu-jdk-11","8.18.0-EAP02-jdk11","8.18.0-EAP02","8.18.0-EAP01-ubuntu-jdk11","8.18.0-EAP01-jdk11","8.18.0-EAP01","8.17.0-RC02-ubuntu-jdk11","8.17.0-RC02-jdk11","8.17.0-RC02","8.17.0-RC01-ubuntu-jdk11","8.17.0-RC01-jdk11","8.17.0-RC01","8.17.0-EAP02-ubuntu-jdk11","8.17.0-EAP02-jdk11","8.17.0-EAP02","8.17.0-EAP01-ubuntu-jdk11","8.17.0-EAP01-jdk11","8.17.0-EAP01","8.8.0-ubuntu-jdk-11","8.8.1-ubuntu-jdk-11","8.8-ubuntu-jdk-11","8.7-jdk11","8.7-ubuntu-jdk11","8.7","8.7.1-jdk11","8.7.1","8.7.1-ubuntu-jdk11","8.7.0-jdk11","8.7.0-ubuntu-jdk11","8.7.0","8.5.18","8.5.18-jdk11","8.5.18-ubuntu-jdk11","8.5.19-jdk11","8.5.19-ubuntu-jdk11","8.5-jdk11","8.5-ubuntu-jdk11","8.5","8.5.19","8.5.2-ubuntu-jdk11","8.5.2","8.5.2-jdk11","8.5.3","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.5.4-ubuntu-jdk11","8.5.4","8.5.4-jdk11","8.5.5-jdk11","8.5.5-ubuntu-jdk11","8.5.5","8.5.6-ubuntu-jdk11","8.5.6","8.5.6-jdk11","8.5.7-ubuntu-jdk11","8.5.7","8.5.12-ubuntu-jdk11","8.5.7-jdk11","8.5.12","8.5.12-jdk11","8.5.8","8.5.8-jdk11","8.5.8-ubuntu-jdk11","8.5.13-jdk11","8.5.13","8.5.13-ubuntu-jdk11","8.5.9-jdk11","8.5.9","8.5.9-ubuntu-jdk11","8.5.14-jdk11","8.5.0-ubuntu-jdk11","8.5.14","8.5.0","8.5.14-ubuntu-jdk11","8.5.0-jdk11","8.6.0-jdk11","8.6.0","8.6.0-ubuntu-jdk11","8.5.15-jdk11","8.5.1","8.5.15","8.5.1-ubuntu-jdk11","8.5.15-ubuntu-jdk11","8.5.1-jdk11","8.6","8.6-ubuntu-jdk11","8.6.1-ubuntu-jdk11","8.6.1","8.5.16","8.5.10-ubuntu-jdk11","8.6.1-jdk11","8.5.16-ubuntu-jdk11","8.5.10","8.6-jdk11","8.5.16-jdk11","8.5.10-jdk11","8.5.17","8.5.11","8.5.17-ubuntu-jdk11","8.5.11-jdk11","8.5.17-jdk11","8.5.11-ubuntu-jdk11","8.14.0-jdk8","8.14.0-ubuntu-jdk8","8.14.0-ubuntu","8.14.1-jdk8","8.14-jdk8","8.14.1-ubuntu","8.14.1-ubuntu-jdk8","8.14-ubuntu","8.14-ubuntu-jdk8","8.15.0-ubuntu","8.15.0-jdk8","8.15.0-ubuntu-jdk8","8.15-ubuntu","8.15.1-ubuntu","8.15-ubuntu-jdk8","8.15.1-ubuntu-jdk8","8.15.1-jdk8","8.15-jdk8","8.16.0-ubuntu","8.16.0-ubuntu-jdk8","8.16.0-jdk8","8.18.1-jdk8","8.18.1-ubuntu","8.7.1-ubuntu-jdk8","8.18.1-ubuntu-jdk8","8.7-jdk8","8.16.1-jdk8","8.16.1-ubuntu-jdk8","8.7.1-ubuntu","8.16.1-ubuntu","8.7.1-jdk8","8.7-ubuntu","8.18.2-ubuntu","8.7-ubuntu-jdk8","8.18-ubuntu","8.18-jdk8","8.16-ubuntu","8.18.2-jdk8","8.18-ubuntu-jdk8","8.16-ubuntu-jdk8","8.10.0-ubuntu-jdk8","8.18.2-ubuntu-jdk8","8.10.0-ubuntu","8.16.2-jdk8","8.13.1-ubuntu","8.10.0-jdk8","8.16-jdk8","8.13.1-ubuntu-jdk8","8.16.2-ubuntu","8.12.0-ubuntu-jdk8","8.12.0-ubuntu","8.16.2-ubuntu-jdk8","8.8.0-ubuntu","8.8.0-jdk8","8.5.8-jdk8","8.5.15-ubuntu","8.10.1-ubuntu","8.5.8-ubuntu-jdk8","8.5.15-ubuntu-jdk8","8.8.0-ubuntu-jdk8","8.12.0-jdk8","8.5.15-jdk8","8.13.1-jdk8","8.5.8-ubuntu","8.10.1-ubuntu-jdk8","8.19-ubuntu-jdk8","8.10-ubuntu-jdk8","8.5.16-jdk8","8.12.1-ubuntu-jdk8","8.8.1-ubuntu-jdk8","8.10.1-jdk8","8.13-jdk8","8.12.1-ubuntu","8.8-ubuntu","8.10-ubuntu","8.13.10-jdk8","8.5.16-ubuntu-jdk8","8.5.9-ubuntu-jdk8","8.12.1-jdk8","8.8.1-ubuntu","8.13.10-ubuntu-jdk8","8.19.0-ubuntu-jdk8","8.5.16-ubuntu","8.5.9-jdk8","8.17.0-ubuntu-jdk8","8.10-jdk8","8.13-ubuntu","8-ubuntu-jdk8","8.5.9-ubuntu","8.8-jdk8","jdk8","8.8.1-jdk8","8.19-ubuntu","8.17.0-ubuntu","8.8-ubuntu-jdk8","8.5.3-ubuntu-jdk8","8.13.5-ubuntu-jdk8","8.19-jdk8","8.5.10-jdk8","8.17.0-jdk8","8.5.17-jdk8","8.5.3-jdk8","8.13.5-ubuntu","8.12.2-ubuntu","8.5.10-ubuntu","8.19.0-ubuntu","8.13-ubuntu-jdk8","8.6.0-ubuntu-jdk8","8.5.17-ubuntu","8.13.5-jdk8","8.12.2-ubuntu-jdk8","8.5.10-ubuntu-jdk8","8.6.0-ubuntu","8.5.17-ubuntu-jdk8","8.5.3-ubuntu","ubuntu-jdk8","8.12.2-jdk8","8.19.0-jdk8","8.17-ubuntu","8.6.0-jdk8","8-jdk8","8.17.1-jdk8","8.13.6-jdk8","ubuntu","8.11.0-ubuntu","8.5.11-jdk8","8-ubuntu","8.5.4-jdk8","8.5.18-ubuntu-jdk8","8.17.1-ubuntu","8.12.3-ubuntu","8.11.0-jdk8","8.13.6-ubuntu-jdk8","8.5.11-ubuntu","8.5.4-ubuntu","8.17.1-ubuntu-jdk8","8.6.1-jdk8","8.5.18-jdk8","8.13.6-ubuntu","8.5.11-ubuntu-jdk8","8.9.0-ubuntu-jdk8","8.5.4-ubuntu-jdk8","8.12.3-jdk8","8.17-jdk8","8.5-ubuntu-jdk8","8.11.0-ubuntu-jdk8","8.9.0-jdk8","8.12-jdk8","8.5-jdk8","8.9.0-ubuntu","8.12-ubuntu-jdk8","8.6.1-ubuntu","8.17-ubuntu-jdk8","8.12.3-ubuntu-jdk8","8.6.1-ubuntu-jdk8","8.5-ubuntu","8.13.7-jdk8","8.13.2-ubuntu-jdk8","8.5.5-jdk8","8.6-ubuntu","8.13.7-ubuntu-jdk8","8.11-jdk8","8.13.2-ubuntu","8.5.5-ubuntu-jdk8","8.6-jdk8","8.12-ubuntu","8.5.12-ubuntu-jdk8","8.11-ubuntu-jdk8","8.5.18-ubuntu","8.13.7-ubuntu","8.5.5-ubuntu","8.6-ubuntu-jdk8","8.5.12-ubuntu","8.13.2-jdk8","8.5.12-jdk8","8.11.1-jdk8","8.9-ubuntu-jdk8","8.11-ubuntu","8.9.1-jdk8","8.11.1-ubuntu","8.9.1-ubuntu-jdk8","8.11.1-ubuntu-jdk8","8.9-jdk8","8.13.3-ubuntu","8.13.8-ubuntu-jdk8","8.5.6-jdk8","8.9-ubuntu","8.5.0-jdk8","8.5.13-jdk8","8.5.0-ubuntu-jdk8","8.13.3-jdk8","8.5.6-ubuntu-jdk8","8.13.8-ubuntu","8.5.13-ubuntu","8.9.1-ubuntu","8.5.0-ubuntu","8.13.3-ubuntu-jdk8","8.5.6-ubuntu","8.13.8-jdk8","8.5.13-ubuntu-jdk8","8.18.0-jdk8","8.13.0-jdk8","8.5.7-jdk8","8.5.2-jdk8","8.13.9-ubuntu-jdk8","8.7.0-ubuntu","8.5.14-ubuntu","8.18.0-ubuntu","8.5.1-ubuntu-jdk8","8.5.7-ubuntu","8.5.2-ubuntu-jdk8","8.13.4-ubuntu-jdk8","8.13.0-ubuntu-jdk8","8.7.0-jdk8","8.18.0-ubuntu-jdk8","8.5.2-ubuntu","8.13.4-jdk8","8.13.0-ubuntu","8.13.9-jdk8","8.5.1-ubuntu","8.5.14-jdk8","8.5.7-ubuntu-jdk8","8.13.4-ubuntu","8.13.9-ubuntu","8.5.1-jdk8","8.5.14-ubuntu-jdk8","8.7.0-ubuntu-jdk8","8.13.10-ubuntu","8.3.5-jdk8","8.3.5","8.3.5-ubuntu-jdk8","8.3-ubuntu","8.3-jdk8","8.3","8.3.5-ubuntu","7.13.13-ubuntu","8.3-ubuntu-jdk8","7.13.13-jdk8","7.13.4-ubuntu-jdk8","7.13.13-ubuntu-jdk8","7.13.4","7.13.13","7.13.4-ubuntu","7.13.4-jdk8","8.2.4","8.2.4-ubuntu-jdk8","8.2.4-ubuntu","8.1.3-jdk8","7.13.14-ubuntu-jdk8","8.2.4-jdk8","7.13.14","8.1.3-ubuntu","7.13.5-jdk8","7.13.14-jdk8","8.1-ubuntu","7.13.5-ubuntu-jdk8","7.13.14-ubuntu","8.4.0","7.13.5-ubuntu","8.4.0-jdk8","8.2.5-ubuntu","7.13.5","8.4.0-ubuntu-jdk8","8.2.5","8.4.0-ubuntu","8.2.5-ubuntu-jdk8","8.2.5-jdk8","7.13.15","7.13.15-jdk8","7.13.6-ubuntu","7.13.15-ubuntu","7.13.6-ubuntu-jdk8","7.13.15-ubuntu-jdk8","8.4.1-ubuntu-jdk8","7.13.6","8.4.1-ubuntu","8.2","7.13.6-jdk8","8.4.1-jdk8","8.2-jdk8","8.4.1","8.2-ubuntu-jdk8","8.2-ubuntu","7.13.16-ubuntu-jdk8","8.2.6-ubuntu","7.13.16-jdk8","8.2.6-jdk8","7.13.8-ubuntu","7.13.16","8.2.6-ubuntu-jdk8","7.13.16-ubuntu","8.4.2","7.13.8-jdk8","8.2.6","8.4.2-jdk8","7.13.8-ubuntu-jdk8","8.4.2-ubuntu","7.13.8","8.4.2-ubuntu-jdk8","7.13.17-ubuntu-jdk8","8.4.1-ubuntu-jdk11","8.2.2-ubuntu-jdk11","7.13.17-ubuntu","8.4.1-jdk11","8.3.1-ubuntu-jdk11","8.2.2-jdk11","7.13.17","7.13.9-ubuntu","8.3.1-jdk11","7.13.17-jdk8","8.4.3-ubuntu","7.13.9","8.4.3-jdk8","7.13.9-ubuntu-jdk8","8.4.3","7.13.9-jdk8","8.2.3-jdk11","8.4.2-ubuntu-jdk11","8.4-jdk8","8.2.3-ubuntu-jdk11","8.3.2-ubuntu-jdk11","7.13-jdk8","8.4.3-ubuntu-jdk8","8.4.2-jdk11","8.3.2-jdk11","7.13-ubuntu-jdk8","8.4-ubuntu-jdk8","8.3.0-ubuntu-jdk8","7-jdk8","8.4-ubuntu","8.0.0-jdk8","8.3.0","7.13.18","8.4","8.0.0-ubuntu","8.3.0-ubuntu","7.13","8.2.4-ubuntu-jdk11","8.0.0-ubuntu-jdk8","8.4-jdk11","8.3.0-jdk8","7.13-ubuntu","8.2.4-jdk11","8.3.3-ubuntu-jdk11","8.0.0","8.4.3-jdk11","7-ubuntu","8.3.3-jdk11","8.4-ubuntu-jdk11","7","7-ubuntu-jdk8","8.4.3-ubuntu-jdk11","7.13.18-jdk8","8.3.1-jdk8","8.0.2","8.2.5-jdk11","7.13.18-ubuntu-jdk8","8.3.1","8.2.0","8.0.2-ubuntu","8.3.4-ubuntu-jdk11","8.2.5-ubuntu-jdk11","8.3.1-ubuntu-jdk8","7.13.18-ubuntu","8.2.0-ubuntu-jdk8","8.0.2-jdk8","8.3.4-jdk11","8.3.1-ubuntu","8.2.0-ubuntu","8.0.2-ubuntu-jdk8","8.2.0-jdk8","8.2-ubuntu-jdk11","8.3-jdk11","8.2.6-jdk11","8.3.2-ubuntu-jdk8","8.0-jdk8","8.3.5-jdk11","8.2.1","8.3.2","8.2-jdk11","8.0.3-jdk8","8.3.5-ubuntu-jdk11","8.2.1-ubuntu-jdk8","8.3.2-jdk8","8.2.6-ubuntu-jdk11","8.0-ubuntu","8.3-ubuntu-jdk11","8.2.1-ubuntu","8.3.2-ubuntu","8.0","8.2.1-jdk8","8.0.3","8.0.3-ubuntu","8.0-ubuntu-jdk8","7.13.2-jdk8","8.0.3-ubuntu-jdk8","8.3.3-jdk8","7.13.2-ubuntu-jdk8","8.2.2-ubuntu","8.3.3-ubuntu","7.13.2","8.2.2-ubuntu-jdk8","8.3.3","7.13.2-ubuntu","8.2.2-jdk8","8.2.0-ubuntu-jdk11","8.3.3-ubuntu-jdk8","8.2.2","8.2.0-jdk11","7.13.3-ubuntu","8.3.0-ubuntu-jdk11","7.13.3-ubuntu-jdk8","8.3.4-ubuntu-jdk8","8.2.3-ubuntu-jdk8","8.4.0-ubuntu-jdk11","8.2.1-ubuntu-jdk11","7.13.3-jdk8","8.3.0-jdk11","8.3.4-jdk8","8.2.3","8.4.0-jdk11","8.2.1-jdk11","7.13.3","8.3.4-ubuntu","8.2.3-jdk8","8.3.4","8.2.3-ubuntu","8.19.0-RC01-ubuntu-jdk8","8.19.0-RC01-jdk8","8.19.0-RC01-ubuntu","7.13.0-ubuntu-jdk8","7.13.0","7.13.0-ubuntu","7.13.0-jdk8","7.13.1-ubuntu-jdk8","7.13.1-jdk8","7.13.1-ubuntu","7.13.1","7.13.11-ubuntu","7.13.11","8.1.0-jdk8","8.1.0","7.13.11-ubuntu-jdk8","8.1.0-ubuntu-jdk8","7.13.11-jdk8","8.1.0-ubuntu","7.13.12-ubuntu","8.1.1-ubuntu","7.13.12","8.1.1-jdk8","7.13.12-jdk8","8.1.1","7.13.12-ubuntu-jdk8","8.1.1-ubuntu-jdk8","8.1.2-ubuntu-jdk8","8.1.2-ubuntu","8.1.2","8.1.2-jdk8","8.1.3-ubuntu-jdk8","8.1","8.1.3","8.1-jdk8","8.1-ubuntu-jdk8","eap-ubuntu","8.19.0-EAP02-ubuntu","eap-ubuntu-jdk8","8.19.0-EAP02-ubuntu-jdk8","eap-jdk8","8.19.0-EAP02-jdk8","8.19.0-EAP01-ubuntu","8.19.0-EAP01-jdk8","8.19.0-EAP01-ubuntu-jdk8","8.19.0-EAP01-ubuntu-jdk11","8.18.0-ssmith-perm-build-1","8.18.0-RC01-ubuntu","8.18.0-RC01-ubuntu-jdk8","8.18.0-RC01-jdk8","8.18.0-EAP02-ubuntu-jdk8","8.18.0-EAP02-jdk8","8.18.0-EAP02-ubuntu","8.18.0-EAP02-ubuntu-jdk11","8.18.0-EAP01-ubuntu","8.18.0-EAP01-jdk8","8.18.0-EAP01-ubuntu-jdk8","8.17.0-RC02-jdk8","8.17.0-RC02-ubuntu","8.17.0-RC02-ubuntu-jdk8","8.17.0-RC01-jdk8","8.17.0-RC01-ubuntu-jdk8","8.17.0-RC01-ubuntu","8.17.0-EAP02-ubuntu","8.17.0-EAP02-jdk8","8.17.0-EAP02-ubuntu-jdk8","8.17.0-EAP01-ubuntu","8.17.0-EAP01-ubuntu-jdk8","8.17.0-EAP01-jdk8","8.16.0-EAP02-ubuntu-jdk8","8.16.0-EAP02-jdk8","8.16.0-EAP02-ubuntu","8.16.0-EAP02","8.16.0-EAP02-jdk11","8.16.0-EAP02-ubuntu-jdk11","8.16.0-RC01-ubuntu-jdk11","8.16.0-RC01-jdk11","8.16.0-EAP03","8.16.0-EAP03-ubuntu-jdk11","8.16.0-EAP03-ubuntu-jdk8","8.16.0-EAP03-jdk11","8.16.0-EAP03-jdk8","8.16.0-EAP03-ubuntu","8.16.0-RC01-ubuntu-jdk8","8.16.0-RC01-ubuntu","8.16.0-RC01","8.16.0-RC01-jdk8","8.16.0-RC02-ubuntu-jdk11","8.16.0-RC02-jdk11","8.16.0-RC02","8.16.0-RC02-ubuntu","8.16.0-RC02-ubuntu-jdk8","8.16.0-RC02-jdk8","8.16.0-EAP01","8.16.0-EAP01-jdk8","8.16.0-EAP01-ubuntu","8.16.0-EAP01-ubuntu-jdk11","8.16.0-EAP01-jdk11","8.15.0-RC01","8.15.0-RC01-jdk8","8.15.0-RC01-ubuntu-jdk11","8.15.0-RC01-ubuntu","8.15.0-RC01-jdk11","8.15.0-EAP03-jdk11","8.15.0-EAP03-ubuntu-jdk11","8.15.0-EAP03-jdk8","8.15.0-EAP03","8.15.0-EAP03-ubuntu","8.15.0-EAP02-ubuntu","8.15.0-EAP02","8.15.0-EAP02-jdk8","8.15.0-EAP02-ubuntu-jdk11","8.15.0-EAP02-jdk11","8.15.0-EAP01-ubuntu-jdk11","8.15.0-EAP01-jdk11","8.15.0-EAP01","8.15.0-EAP01-ubuntu","8.15.0-EAP01-jdk8","8.14.0-RC01-jdk11","8.14.0-RC01-ubuntu-jdk11","8.14.0-RC01","8.14.0-RC01-ubuntu","8.14.0-RC01-jdk8","8.14.0-EAP01-ubuntu","8.14.0-EAP01","8.14.0-EAP01-jdk8","8.14.0-EAP01-ubuntu-jdk11","8.14.0-EAP01-jdk11","8.13.0-RC01","8.13.0-RC01-jdk11","8.13.0-RC01-ubuntu","8.13.0-RC01-ubuntu-jdk11","8.13.0-RC01-jdk8","8.13.0-EAP03-jdk8","8.13.0-EAP03","8.13.0-EAP03-ubuntu","8.13.0-EAP03-ubuntu-jdk11","8.13.0-EAP03-jdk11","8.13.0-EAP02","8.13.0-EAP02-ubuntu","8.13.0-EAP02-jdk8","8.13.0-EAP02-jdk11","8.13.0-EAP02-ubuntu-jdk11","8.12.0-RC02-jdk11","8.12.0-RC02-ubuntu-jdk11","8.12.0-RC02-jdk8","8.12.0-RC02-ubuntu","8.12.0-RC02","8.13.0-EAP01-jdk11","8.13.0-EAP01-ubuntu-jdk11","8.12.0-RC01-jdk11","8.12.0-RC01-ubuntu-jdk11","8.13.0-EAP01-jdk8","8.13.0-EAP01-ubuntu","8.13.0-EAP01","8.12.0-RC01-jdk8","8.12.0-RC01-ubuntu","8.12.0-RC01","8.12.0-EAP02-ubuntu-jdk11","8.12.0-EAP02-jdk11","8.12.0-EAP02","8.12.0-EAP02-ubuntu","8.12.0-EAP02-jdk8","8.12.0-EAP01-jdk11","8.12.0-EAP01-ubuntu-jdk11","8.12.0-EAP01","8.12.0-EAP01-jdk8","8.12.0-EAP01-ubuntu","8.5.6-RC02-jdk8","8.5.6-RC02-ubuntu","8.5.6-RC02","8.5.6-RC02-ubuntu-jdk11","8.5.6-RC02-jdk11","8.11.0-RC01","8.11.0-RC01-jdk8","8.11.0-RC01-ubuntu","8.11.0-RC01-jdk11","8.11.0-RC01-ubuntu-jdk11","8.11.0-EAP01-ubuntu-jdk11","8.11.0-EAP01-jdk11","8.11.0-EAP01-jdk8","8.11.0-EAP01-ubuntu","8.11.0-EAP01","8.10.0-RC01-ubuntu","8.10.0-RC01","8.10.0-RC01-jdk8","8.10.0-RC01-jdk11","8.10.0-RC01-ubuntu-jdk11","7.13.14-RC01","7.13.14-RC01-ubuntu","7.13.14-RC01-jdk8","8.10.0-EAP02-jdk8","8.10.0-EAP02","8.10.0-EAP02-ubuntu","8.10.0-EAP01-jdk8","8.10.0-EAP01","8.10.0-EAP01-ubuntu","8.10.0-EAP01-ubuntu-jdk11","7.13.15-RC01-ubuntu","8.10.0-EAP01-jdk11","7.13.15-RC01","7.13.15-RC01-jdk8","8.10.0-EAP02-ubuntu-jdk11","8.10.0-EAP02-jdk11","8.5.6-RC01-jdk11","8.5.6-RC01-ubuntu-jdk11","8.5.6-RC01","8.5.6-RC01-ubuntu","8.5.6-RC01-jdk8","8.5.5-RC01-jdk11","8.5.5-RC01-ubuntu-jdk11","8.5.5-RC01","8.5.5-RC01-jdk8","8.5.5-RC01-ubuntu","8.9.0-RC01-ubuntu","8.9.0-RC01-jdk8","8.9.0-RC01","8.9.0-RC01-jdk11","8.9.0-RC01-ubuntu-jdk11","8.9.0-EAP02-jdk11","8.9.0-EAP02-ubuntu-jdk11","8.9.0-EAP02-jdk8","8.9.0-EAP02-ubuntu","8.9.0-EAP02","8.9.0-EAP01-ubuntu-jdk11","8.9.0-EAP01-jdk11","8.9.0-EAP01-ubuntu","8.9.0-EAP01","8.9.0-EAP01-jdk8","8.8.0-RC02-ubuntu-jdk11","8.8.0-RC02-jdk11","8.8.0-RC02-jdk8","8.8.0-RC02-ubuntu","8.8.0-RC02"],"confluence":["8.7-ubuntu-jdk17","8.7-jdk17","8.7.2-ubuntu-jdk17","8.6.0-jdk17","8.7.2","8.6.0-ubuntu-jdk17","8.7.2-jdk17","8.6.0","8.7","8.5.5-ubuntu-jdk17","8.5.5-jdk17","8.8.0-ubuntu-jdk17","8.8.0-jdk17","8.5.5","8.7-jdk11","8.7-ubuntu-jdk11","8.8.0","8.7.2-ubuntu-jdk11","8.7.2-jdk11","8.1-jdk11","8.1-ubuntu-jdk11","8.1.4-ubuntu-jdk11","8.1.4-jdk11","8.1","8.1.4","7.19-ubuntu-jdk11","7.19-jdk11","7.19.21-ubuntu-jdk11","7.19.21","7.19.21-jdk11","8.8-ubuntu-jdk17","8.8.1-ubi9","8.8.1-ubi9-jdk17","8.8.0-ubuntu-jdk11","8.8.1-jdk17","8.8.0-jdk11","8.6.1","8.6.1-jdk17","8.6.1-ubuntu-jdk17","8.5.6-ubuntu-jdk17","8.5.6-jdk17","8.5.6","8.5.5-ubuntu-jdk11","8.5.1-ubuntu-jdk17","8.5.5-jdk11","8.5.1-jdk17","8.5.1","8.4.4-jdk17","8.4.4-ubuntu-jdk17","8.4.4","8.0.3-jdk11","8.0.3","8.0.3-ubuntu-jdk11","7.20.2-jdk11","7.20.2-ubuntu-jdk11","7.20.2","8.8-jdk17","8.8.1-ubuntu-jdk17","8.8.1","8.8","8.6.0-ubuntu-jdk11","8.0-jdk11","8.0.4-ubuntu-jdk11","8.3.3-ubuntu-jdk17","8.6.0-jdk11","8.0.4-jdk11","8.3.3","8.0","8.3.3-jdk17","7.19.7-ubuntu-jdk11","7.19.7-jdk11","8.8-ubuntu-jdk11","8.8-jdk11","8.8.1-ubuntu-jdk11","7.19.7","7.19.17-ubuntu-jdk11","8.8.1-jdk11","7.19.17-jdk11","7.19.17","8.7.2-ubi9-jdk17","8.6-ubuntu-jdk17","8.7.2-ubi9","8.6-jdk17","8.6.2-ubuntu-jdk17","8.6.2-ubi9-jdk17","8.6.2-ubi9","8.6","8.6.2-jdk17","8.6.2","8.5.7-ubuntu-jdk17","8.5.7","8.5.7-jdk17","8.5.6-ubuntu-jdk11","8.5.6-jdk11","8.5.2-ubuntu-jdk17","8.5.2-jdk17","8.4.5-ubuntu-jdk17","8.4-jdk17","8.5.2","8.4-ubuntu-jdk17","latest","8.4.5-jdk17","8.4.5","8.4.1-ubuntu-jdk17","8.4","8.4.1-jdk17","8.4.1","8.2.0-jdk11","8.2.0-ubuntu-jdk11","8.3.0","8.3.0-ubuntu-jdk17","8.3.0-jdk17","8.0.4","8.0-ubuntu-jdk11","7-jdk11","7-ubuntu-jdk11","7.20-ubuntu-jdk11","7.20-jdk11","7.20.3-jdk11","7.20.3-ubuntu-jdk11","7.20.3","7.20","8.6.1-ubuntu-jdk11","7","8.6.1-jdk11","8.5.1-jdk11","8.5.1-ubuntu-jdk11","8.4.4-ubuntu-jdk11","8.3-ubuntu-jdk17","8.4.4-jdk11","8.3-jdk17","8.3.4-ubuntu-jdk17","8.3.4","8.3.4-jdk17","8.3.3-ubuntu-jdk11","8.3.3-jdk11","8.3","7.19.3-ubuntu-jdk11","7.19.18-jdk11","7.19.3-jdk11","7.19.18-ubuntu-jdk11","7.19.3","7.19.18","7.19.12-ubuntu-jdk11","7.19.12-jdk11","7.19.12","ubuntu-jdk17","ubi9-jdk17","jdk17","ubi9","8-ubuntu-jdk17","8-jdk17","8.9-ubuntu-jdk17","8.9-jdk17","8.9.0-ubuntu-jdk17","8.9.0-ubi9","8.9.0-ubi9-jdk17","8.9.0","8.9.0-jdk17","8.9","8.5-ubuntu-jdk17","8.5-jdk17","8.5.8-ubuntu-jdk17","8","8.5","8.5.8","8.5.8-jdk17","7.19.8-jdk11","7.19.8-ubuntu-jdk11","7.19.8","7.19.0-ubuntu-jdk11","7.19.0-jdk11","7.19.0","8.5.8-ubi9-jdk17","8.5.7-ubuntu-jdk11","8.5.7-jdk11","8.5.8-ubi9","8.5.3-jdk17","8.5.3-ubuntu-jdk17","8.5.3","8.4.2-ubuntu-jdk17","8.4.2-jdk17","8.3.1-ubuntu-jdk17","8.4.2","8.3.1-jdk17","8.6-ubuntu-jdk11","8.3.1","8.6-jdk11","8.6.2-ubuntu-jdk11","8.2.1-jdk11","8.2.1-ubuntu-jdk11","8.6.2-jdk11","8.2.1","8.2.0-ubuntu-jdk17","8.5.2-ubuntu-jdk11","8.5.2-jdk11","8.2.0-jdk17","8.4-ubuntu-jdk11","8.4.5-ubuntu-jdk11","8.4-jdk11","8.4.5-jdk11","8.4.1-ubuntu-jdk11","8.3-ubuntu-jdk11","8.4.1-jdk11","8.3-jdk11","8.3.4-ubuntu-jdk11","8.3.4-jdk11","8.3.0-ubuntu-jdk11","8.3.0-jdk11","8.1.3-jdk17","8.1.3-ubuntu-jdk17","7.19.4-ubuntu-jdk11","7.19.4-jdk11","7.19.4","7.19.1-ubuntu-jdk11","7.19.19-ubuntu-jdk11","7.19.1-jdk11","7.19.19-jdk11","7.19.19","8.8.0-ubi9-jdk17","7.19.14-ubuntu-jdk11","7.19.14-jdk11","8.8.0-ubi9","8.7.1-ubuntu-jdk17","7.19.14","7.19","8.7.1-ubi9-jdk17","7.19.1","8.7.1-jdk17","8.7.1-ubi9","8.7.1","8.6.1-ubi9-jdk17","8.6.1-ubi9","8.6.0-ubi9-jdk17","8.6.0-ubi9","8.5.7-ubi9-jdk17","8.5.7-ubi9","8.5.6-ubi9-jdk17","8.5.6-ubi9","8.5.0-ubuntu-jdk17","8.5.0-jdk17","8.5.0","8.4.0-ubuntu-jdk17","8.4.0","8.4.0-jdk17","8.3.2-jdk17","8.3.2","8.3.2-ubuntu-jdk17","8.2.2-ubuntu-jdk17","8.2.2-ubuntu-jdk11","8.2.2-jdk17","8.2.2-jdk11","8.2.2","8.2.1-ubuntu-jdk17","8.2.1-jdk17","8.1.0-jdk11","8.1.0-ubuntu-jdk11","8.1.0","8.0.3-ubuntu-jdk17","8.0.3-jdk17","8.0.0-ubuntu-jdk11","8.0.0-jdk11","8.2.0","8.0.0","7.19.9-ubuntu-jdk11","7.19.9-jdk11","7.19.9","jdk11","ubuntu-jdk11","8-jdk11","8-ubuntu-jdk11","8.9-ubuntu-jdk11","8.9-jdk11","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.5-ubuntu-jdk11","8.5-jdk11","8.5.4-ubuntu-jdk17","8.5.8-jdk11","8.5.8-ubuntu-jdk11","8.5.4-jdk17","8.5.4","8.4.3-ubuntu-jdk17","8.4.3-jdk17","8.4.3","8.0.0-ubuntu-jdk17","8.0.0-jdk17","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.4.2-ubuntu-jdk11","8.4.2-jdk11","8.3.1-ubuntu-jdk11","8.3.1-jdk11","8.2-ubuntu-jdk17","8.2-jdk17","8.2.3-jdk17","8.2.3-ubuntu-jdk17","8.1-ubuntu-jdk17","8.1.4-ubuntu-jdk17","8.1.4-jdk17","8.1-jdk17","8.1.0-ubuntu-jdk17","8.1.0-jdk17","8.0-ubuntu-jdk17","8.0.4-ubuntu-jdk17","8.0-jdk17","8.0.4-jdk17","7.19.5-ubuntu-jdk11","7.19.5","7.19.2-ubuntu-jdk11","7.19.5-jdk11","7.19.2-jdk11","7.19.2","7.19.15-ubuntu-jdk11","7.19.15-jdk11","7.19.10-jdk11","7.19.15","7.19.10-ubuntu-jdk11","7.19.10","8.2-ubuntu-jdk11","8.2-jdk11","8.2.3-jdk11","8.2.3","8.2","8.1.1-ubuntu-jdk11","8.1.1-jdk11","8.1.1","8.0.1-ubuntu-jdk11","8.0.1-jdk11","7.20.0-ubuntu-jdk11","8.0.1","7.20.0-jdk11","7.20.0","8.5.4-jdk11","8.5.4-ubuntu-jdk11","8.5.0-ubuntu-jdk11","8.5.0-jdk11","8.7.1-jdk11","8.7.1-ubuntu-jdk11","8.4.3-jdk11","8.4.3-ubuntu-jdk11","8.4.0-ubuntu-jdk11","8.4.0-jdk11","8.1.1-ubuntu-jdk17","8.1.3-ubuntu-jdk11","8.3.2-ubuntu-jdk11","8.3.2-jdk11","8.0.2-ubuntu-jdk17","8.1.3","8.1.3-jdk11","8.0.2-ubuntu-jdk11","8.2.3-ubuntu-jdk11","8.0.2-jdk11","8.0.1-ubuntu-jdk17","8.0.2","7.20.1","7.20.1-jdk11","7.19.6-ubuntu-jdk11","8.1.1-jdk17","7.19.20","7.19.16-ubuntu-jdk11","7.19.6","8.0.2-jdk17","7.19.20-jdk11","7.19.16","8.0.1-jdk17","7.20.1-ubuntu-jdk11","7.19.6-jdk11","7.19.20-ubuntu-jdk11","7.19.16-jdk11","7.19.11","7.19.11-jdk11","7.19.11-ubuntu-jdk11","eap-ubuntu-jdk11","eap-jdk17","eap-jdk11","9.0.0-m41-jdk17","9.0.0-m41","9.0.0-m41-jdk11","9.0.0-m41-ubuntu-jdk17","9.0.0-m41-ubi9","9.0.0-m41-ubi9-jdk17","eap-ubuntu-jdk17","eap","9.0.0-m41-ubuntu-jdk11","9.0.0-m30-jdk17","9.0.0-m30-ubuntu-jdk17","9.0.0-m30-jdk11","9.0.0-m30","9.0.0-m30-ubuntu-jdk11","9.0.0-m26-ubuntu-jdk17","9.0.0-m26-jdk17","9.0.0-m26-ubuntu-jdk11","9.0.0-m26-ubi9","9.0.0-m26-jdk11","9.0.0-m26","9.0.0-m26-ubi9-jdk17","8.9.0-rc1-ubuntu-jdk11","8.9.0-rc1-jdk11","8.9.0-rc1-ubi9","8.9.0-rc1-jdk17","8.9.0-rc1-ubi9-jdk17","8.9.0-rc1-ubuntu-jdk17","8.9.0-rc1","9.0.0-m23-jdk11","9.0.0-m23-ubi9-jdk17","9.0.0-m23-ubi9","9.0.0-m23-ubuntu-jdk11","9.0.0-m23","9.0.0-m23-jdk17","9.0.0-m23-ubuntu-jdk17","8.9.0-beta2-ubi9-jdk17","8.9.0-beta2-ubi9","8.9.0-beta2-ubuntu-jdk17","8.9.0-beta2-jdk17","8.9.0-beta2-jdk11","8.9.0-beta2-ubuntu-jdk11","8.9.0-beta2","9.0.0-m16-ubuntu-jdk17","9.0.0-m16-jdk17","9.0.0-m16-ubi9-jdk17","9.0.0-m16-ubi9","9.0.0-m16-jdk11","9.0.0-m16-ubuntu-jdk11","9.0.0-m16","8.9.0-m58-ubuntu-jdk11","8.9.0-m58-jdk11","8.9.0-m58-jdk17","8.9.0-m58-ubuntu-jdk17","8.9.0-m58","9.0.0-m15-jdk17","9.0.0-m15-ubuntu-jdk17","9.0.0-m15","9.0.0-m15-jdk11","9.0.0-m15-ubuntu-jdk11","8.9.0-m50-jdk17","8.9.0-m50-ubuntu-jdk11","8.9.0-m50-ubuntu-jdk17","8.9.0-m50","8.9.0-m50-jdk11","9.0.0-m14-ubuntu-jdk11","9.0.0-m14-jdk17","9.0.0-m14-jdk11","9.0.0-m14","9.0.0-m14-ubuntu-jdk17","9.0.0-m13-jdk11","9.0.0-m13-jdk17","9.0.0-m13","9.0.0-m13-ubuntu-jdk17","9.0.0-m13-ubuntu-jdk11","8.9.0-m43-jdk17","8.9.0-m43","8.9.0-m43-jdk11","8.9.0-m43-ubuntu-jdk11","8.9.0-m43-ubuntu-jdk17","9.0.0-m11-jdk17","9.0.0-m11","9.0.0-m11-ubuntu-jdk17","9.0.0-m10-ubuntu-jdk11","9.0.0-m10-jdk11","9.0.0-m10-ubuntu-jdk17","9.0.0-m10-jdk17","9.0.0-m10","9.0.0-m09-ubuntu-jdk17","9.0.0-m09-jdk17","9.0.0-m09-ubuntu-jdk11","9.0.0-m09-jdk11","9.0.0-m09","8.8.0-rc1-jdk17","8.8.0-rc1-jdk11","8.8.0-rc1-ubuntu-jdk11","8.8.0-rc1","8.8.0-rc1-ubuntu-jdk17","9.0.0-m08-jdk17","9.0.0-m08-ubuntu-jdk17","9.0.0-m08","9.0.0-m08-ubuntu-jdk11","9.0.0-m08-jdk11","8.8.0-beta1-jdk17","8.8.0-beta1-ubuntu-jdk17","8.8.0-beta1-jdk11","8.8.0-beta1","8.8.0-beta1-ubuntu-jdk11","9.0.0-m07-ubuntu-jdk17","9.0.0-m07-jdk17","9.0.0-m07-jdk11","9.0.0-m07","9.0.0-m07-ubuntu-jdk11","7.16.2-ubuntu-jdk11","7.16.2","7.16.2-jdk11","7.13.7-ubuntu-jdk11","7.13.7-jdk11","7.13.7","7.11.0-ubuntu-jdk11","7.11.0-jdk11","7.11.0","7.13.12-ubuntu-jdk11","7.13.12-jdk11","7.13.12","7.16.3-ubuntu-jdk11","7.16.3-jdk11","7.16.3","7.13.8-ubuntu-jdk11","7.13.8-jdk11","7.13.8","7.11.1-ubuntu-jdk11","7.11.1-jdk11","7.11.1","7.13.13-ubuntu-jdk11","7.13.13-jdk11","7.13.13","7.16.4-ubuntu-jdk11","7.16.4-jdk11","7.16.4","7.11.2-ubuntu-jdk11","7.11.2-jdk11","7.11.2","7.13.9-ubuntu-jdk11","7.13.9-jdk11","7.13.9","7.16-ubuntu-jdk11","7.16-jdk11","7.16.5-ubuntu-jdk11","7.16.5-jdk11","7.16.5","7.16","7.13.14-ubuntu-jdk11","7.13.14-jdk11","7.13.14","7.14.0-ubuntu-jdk11","7.14.0-jdk11","7.14.0","7.11.3-ubuntu-jdk11","7.11.3-jdk11","7.11.3","7.13.15-ubuntu-jdk11","7.13.15-jdk11","7.11-ubuntu-jdk11","7.13.15","7.11.6-ubuntu-jdk11","7.11.6","7.11","7.17.0-ubuntu-jdk11","7.17.0-jdk11","7.17.0","7.14.1-ubuntu-jdk11","7.14.1-jdk11","7.14.1","7.11-jdk11","7.11.6-jdk11","7.13.16-ubuntu-jdk11","7.13.16-jdk11","7.13.16","7.17.1-ubuntu-jdk11","7.17.1-jdk11","7.17.1","7.14.2-ubuntu-jdk11","7.14.2-jdk11","7.14.2","7.13.17-ubuntu-jdk11","7.13.17-jdk11","7.13.17","7.12.0-ubuntu-jdk11","7.12.0-jdk11","7.12.0","7.17.2-ubuntu-jdk11","7.17.2-jdk11","7.17.2","7.14.3-ubuntu-jdk11","7.14.3-jdk11","7.14.3","7.13.18-ubuntu-jdk11","7.13.18-jdk11","7.13.18","7.17.3-ubuntu-jdk11","7.17.3-jdk11","7.17.3","7.14-ubuntu-jdk11","7.14-jdk11","7.14.4-ubuntu-jdk11","7.14.4-jdk11","7.14.4","7.14","7.12.1-ubuntu-jdk11","7.12.1-jdk11","7.12.1","7.17.4-ubuntu-jdk11","7.17.4-jdk11","7.17.4","7.13.19-ubuntu-jdk11","7.13.19-jdk11","7.13.19","7.12.2-ubuntu-jdk11","7.12.2-jdk11","7.12.2","7.15.0-ubuntu-jdk11","7.15.0-jdk11","7.15.0","7.13.2-ubuntu-jdk11","7.13.2-jdk11","7.13.2","7.12.3-ubuntu-jdk11","7.12.3-jdk11","7.12.3","7.17-ubuntu-jdk11","7.17-jdk11","7.17.5-ubuntu-jdk11","7.17.5-jdk11","7.17.5","7.17","7.15.1-ubuntu-jdk11","7.15.1-jdk11","7.15.1","7.13-ubuntu-jdk11","7.13-jdk11","7.13.20-ubuntu-jdk11","7.13.20-jdk11","7.13.20","7.13","7.12.4-ubuntu-jdk11","7.12.4-jdk11","7.12.4","7.18.0-ubuntu-jdk11","7.18.0-jdk11","7.18.0","7.15.2-ubuntu-jdk11","7.15.2-jdk11","7.15.2","7.12-ubuntu-jdk11","7.12.5","7.12-jdk11","7.12.5-ubuntu-jdk11","7.12.5-jdk11","7.12","7.15-ubuntu-jdk11","7.15-jdk11","7.15.3-ubuntu-jdk11","7.15.3-jdk11","7.15.3","7.15","7.13.3-ubuntu-jdk11","7.13.3-jdk11","7.13.3","7.18.1-ubuntu-jdk11","7.18.1-jdk11","7.18.1","7.18.2-ubuntu-jdk11","7.18.2-jdk11","7.18.2","7.13.4-ubuntu-jdk11","7.13.4-jdk11","7.13.4","7.13.0-ubuntu-jdk11","7.13.0-jdk11","7.13.0","7.16.0-ubuntu-jdk11","7.16.0-jdk11","7.16.0","7.18-ubuntu-jdk11","7.18-jdk11","7.18.3-ubuntu-jdk11","7.18.3-jdk11","7.18.3","7.18","7.13.5-ubuntu-jdk11","7.13.5-jdk11","7.13.5","7.13.1-ubuntu-jdk11","7.13.1-jdk11","7.13.1","7.16.1-ubuntu-jdk11","7.16.1-jdk11","7.16.1","7.13.6-ubuntu-jdk11","7.13.6-jdk11","7.13.6","7.13.11-ubuntu-jdk11","7.13.11-jdk11","7.13.11","8.8.0-m61-ubuntu-jdk11","8.8.0-m61-jdk11","8.8.0-m61","8.8.0-m61-ubuntu-jdk17","8.8.0-m61-jdk17","8.8.0-m46-ubuntu-jdk11","8.8.0-m46-ubuntu-jdk17","8.8.0-m46-jdk17","8.8.0-m46","8.8.0-m46-jdk11","8.7.0-rc1-ubuntu-jdk17","8.7.0-rc1-jdk17","8.7.0-rc1-jdk11","8.7.0-rc1","8.7.0-rc1-ubuntu-jdk11","8.7.0-beta1-ubuntu-jdk11","8.7.0-beta1","8.7.0-beta1-jdk11","8.7.0-beta1-jdk17","8.7.0-beta1-ubuntu-jdk17","8.7.0-m48-ubuntu-jdk17","8.7.0-m48-jdk17","8.7.0-m48-jdk11","8.7.0-m48-ubuntu-jdk11","8.7.0-m48","8.7.0-m41-ubuntu-jdk17","8.7.0-m41-ubuntu-jdk11","8.7.0-m41-jdk17","8.7.0-m41-jdk11","8.7.0-m41","8.6.0-rc1-ubuntu-jdk17","8.6.0-rc1-jdk17","8.6.0-rc1-ubuntu-jdk11","8.6.0-rc1-jdk11","8.6.0-rc1","8.6.0-beta2-jdk17","8.6.0-beta2","8.6.0-beta2-jdk11","8.6.0-beta2-ubuntu-jdk11","8.6.0-beta2-ubuntu-jdk17","8.6.0-beta1-jdk17","8.6.0-beta1-ubuntu-jdk17","8.6.0-beta1","8.6.0-beta1-ubuntu-jdk11","8.6.0-beta1-jdk11","8.6.0-m85-ubuntu-jdk17","8.6.0-m85-jdk17","8.6.0-m85","8.6.0-m85-ubuntu-jdk11","8.6.0-m85-jdk11","8.6.0-m79-jdk11","8.6.0-m79-ubuntu-jdk11","8.6.0-m79-ubuntu-jdk17","8.6.0-m79-jdk17","8.6.0-m79","8.5.0-rc1-jdk11","8.5.0-rc1","8.5.0-rc1-jdk17","8.5.0-rc1-ubuntu-jdk11","8.5.0-rc1-ubuntu-jdk17","8.5.0-beta1-ubuntu-jdk17","8.5.0-beta1-jdk17","8.5.0-beta1","8.5.0-beta1-ubuntu-jdk11","8.5.0-beta1-jdk11","8.5.0-m06-jdk17","8.5.0-m06-ubuntu-jdk17","8.5.0-m06-jdk11","8.5.0-m06","8.5.0-m06-ubuntu-jdk11","8.5.0-m05-jdk17","8.5.0-m05-jdk11","8.5.0-m05-ubuntu-jdk17","8.5.0-m05-ubuntu-jdk11","8.5.0-m05","8.5.0-m04-jdk17","8.5.0-m04-ubuntu-jdk11","8.5.0-m04-ubuntu-jdk17","8.5.0-m04","8.5.0-m04-jdk11","8.4.0-rc2-ubuntu-jdk17","8.4.0-rc2-jdk17","8.4.0-rc2-ubuntu-jdk11","8.4.0-rc2","8.4.0-rc2-jdk11","8.4.0-rc1-jdk17","8.4.0-rc1-ubuntu-jdk17","8.4.0-rc1-jdk11","8.4.0-rc1","8.4.0-rc1-ubuntu-jdk11","8.4.0-beta1-jdk17","8.4.0-beta1-ubuntu-jdk17","8.4.0-beta1","8.4.0-beta1-jdk11","8.4.0-beta1-ubuntu-jdk11","8.4.0-m48-jdk17","8.4.0-m48-ubuntu-jdk11","8.4.0-m48-jdk11","8.4.0-m48-ubuntu-jdk17","8.4.0-m48","8.4.0-m22","8.4.0-m22-jdk17","8.4.0-m22-ubuntu-jdk11","8.4.0-m22-jdk11","8.4.0-m22-ubuntu-jdk17","8.4.0-m21-ubuntu-jdk17","8.4.0-m21-jdk17","8.4.0-m21","8.4.0-m21-ubuntu-jdk11","8.4.0-m21-jdk11","8.3.0-rc2-ubuntu-jdk11","8.3.0-rc2-jdk11","8.3.0-rc2-ubuntu-jdk17","8.3.0-rc2-jdk17","8.3.0-rc2","8.3.0-rc1-ubuntu-jdk11","8.3.0-rc1-ubuntu-jdk17","8.3.0-rc1-jdk11","8.3.0-rc1-jdk17","8.3.0-rc1","8.3.0-beta1-jdk11","8.3.0-beta1-jdk17","8.3.0-beta1-ubuntu-jdk11","8.3.0-beta1","8.3.0-beta1-ubuntu-jdk17","8.3.0-tinymce6-m01-ubuntu-jdk11","8.3.0-tinymce6-m01","8.3.0-tinymce6-m01-ubuntu-jdk17","8.3.0-tinymce6-m01-jdk11","8.3.0-tinymce6-m01-jdk17","8.3.0-m48","8.3.0-m48-ubuntu-jdk17","8.3.0-m48-jdk11","8.3.0-m48-ubuntu-jdk11","8.3.0-m48-jdk17","8.3.0-m42-ubuntu-jdk17","8.3.0-m42-jdk17","8.3.0-m42-jdk11","8.3.0-m42-ubuntu-jdk11","8.3.0-m42","8.3.0-m37-jdk11","8.3.0-m37-ubuntu-jdk11","8.3.0-m37-jdk17","8.3.0-m37","8.3.0-m37-ubuntu-jdk17","8.3.0-m31-ubuntu-jdk17","8.3.0-m31","8.3.0-m31-ubuntu-jdk11","8.3.0-m31-jdk11","8.3.0-m31-jdk17","8.2.0-rc1-jdk11","8.2.0-rc1-jdk17","8.2.0-rc1-ubuntu-jdk11","8.2.0-rc1-ubuntu-jdk17","8.2.0-rc1","8.2.0-beta2","8.2.0-beta2-ubuntu-jdk11","8.2.0-beta2-jdk17","8.2.0-beta2-ubuntu-jdk17","8.2.0-beta2-jdk11","8.2.0-beta1-ubuntu-jdk11","8.2.0-beta1-ubuntu-jdk17","8.2.0-beta1-jdk17","8.2.0-beta1-jdk11","8.2.0-beta1","8.2.0-m36-jdk11","8.2.0-m36-jdk17","8.2.0-m36","8.2.0-m36-ubuntu-jdk17","8.2.0-m36-ubuntu-jdk11","8.2.0-m27-ubuntu-jdk11","8.2.0-m27-jdk17","8.2.0-m27-jdk11","8.2.0-m27","8.2.0-m27-ubuntu-jdk17","8.2.0-m20","8.2.0-m20-jdk11","8.2.0-m20-ubuntu-jdk11","8.2.0-m20-ubuntu-jdk17","8.2.0-m20-jdk17","8.1.0-rc1-jdk11","8.1.0-rc1","8.1.0-rc1-ubuntu-jdk11","8.1.0-rc1-ubuntu-jdk17","8.1.0-rc1-jdk17","8.1.0-beta2","8.1.0-beta2-jdk11","8.1.0-beta2-ubuntu-jdk11","8.1.0-beta2-ubuntu-jdk17","8.1.0-beta2-jdk17","8.1.0-beta1-jdk11","8.1.0-beta1","8.1.0-beta1-ubuntu-jdk11","8.1.0-beta1-jdk17","8.1.0-beta1-ubuntu-jdk17","8.1.0-m78-jdk11","8.1.0-m78-ubuntu-jdk17","8.1.0-m78-ubuntu-jdk11","8.1.0-m78","8.1.0-m78-jdk17","7.6.2-ubuntu-jdk11","7.6.2-jdk11","7.6.2","7.6.1-ubuntu-jdk11","7.6.1-jdk11","7.6.1","7.9-ubuntu-jdk11","7.9-jdk11","7.9.3-ubuntu-jdk11","7.9.3-jdk11","7.9.3","7.9.1-ubuntu-jdk11","7.9.1-jdk11","7.9.1","7.9.0-ubuntu-jdk11","7.9.0-jdk11","7.9.0","7.9","7.8-ubuntu-jdk11","7.8-jdk11","7.8.3-ubuntu-jdk11","7.8.3-jdk11","7.8.3","7.8","7.6.0-ubuntu-jdk11","7.6.0-jdk11","7.6.0","7.5-ubuntu-jdk11","7.5-jdk11","7.5.2-ubuntu-jdk11","7.5.2-jdk11","7.8.1-ubuntu-jdk11","7.8.1-jdk11","7.5.2","7.8.1","7.8.0-ubuntu-jdk11","7.8.0-jdk11","7.5.1-ubuntu-jdk11","7.8.0","7.7-ubuntu-jdk11","7.5.1-jdk11","7.7-jdk11","7.7.4-ubuntu-jdk11","7.5.1","7.7.4-jdk11","7.7.4","7.5.0-ubuntu-jdk11","7.7.3-ubuntu-jdk11","7.7.3-jdk11","7.7.3","7.5.0-jdk11","7.7.2-ubuntu-jdk11","7.7.2-jdk11","7.5.0","7.7.2","7.7","7.6-ubuntu-jdk11","7.5","7.6-jdk11","7.6.3-ubuntu-jdk11","7.4.9-ubuntu-jdk11","7.6.3-jdk11","7.6.3","7.6","7.4.9-jdk11","7.4.9","7.4.17-ubuntu-jdk11","7.4.17-jdk11","7.4-ubuntu-jdk11","7.4.17","7.4-jdk11","7.4.16-ubuntu-jdk11","7.4.8-ubuntu-jdk11","7.4.16-jdk11","7.4.8-jdk11","7.4.8","7.4.16","7.4.7-ubuntu-jdk11","7.4.7-jdk11","7.4.7","7.4.15-ubuntu-jdk11","7.4.6-ubuntu-jdk11","7.4.6-jdk11","7.4.6","7.4.15-jdk11","7.4.5-ubuntu-jdk11","7.4.5-jdk11","7.4.5","7.4.4-ubuntu-jdk11","7.4.15","7.4.4-jdk11","7.4.4","7.4.3-ubuntu-jdk11","7.4.3-jdk11","7.4.14-ubuntu-jdk11","7.4.3","7.4.1-ubuntu-jdk11","7.4.1-jdk11","7.4.14-jdk11","7.4.18-ubuntu-jdk11","7.4.18-jdk11","7.4.18","7.4.14","7.4.13-ubuntu-jdk11","7.4.13-jdk11","7.4.13","7.4.12-ubuntu-jdk11","7.4.12-jdk11","7.4.12","7.4.11-ubuntu-jdk11","7.4.11-jdk11","7.4.11","7.4.10-ubuntu-jdk11","7.4.10-jdk11","7.4.10","7.4.1","7.4.0-ubuntu-jdk11","7.4.0-jdk11","7.4.0","7.4","7.10-ubuntu-jdk11","7.10-jdk11","7.10.2-ubuntu-jdk11","7.10.2-jdk11","7.10.2","7.10.1-ubuntu-jdk11","7.10.1-jdk11","7.10.1","7.10.0-ubuntu-jdk11","7.10.0-jdk11","7.10.0","7.10","8.0.0-rc1-ubuntu-jdk11","8.0.0-rc1-jdk17","8.0.0-rc1-jdk11","8.0.0-rc1","8.0.0-rc1-ubuntu-jdk17","8.0.0-beta1-ubuntu-jdk17","8.0.0-beta1","8.0.0-beta1-ubuntu-jdk11","8.0.0-beta1-jdk17","8.0.0-beta1-jdk11","8.0.0-m90-ubuntu-jdk17","8.0.0-m90-ubuntu-jdk11","8.0.0-m90-jdk11","8.0.0-m90-jdk17","8.0.0-m90","8.0.0-m89-ubuntu-jdk17","8.0.0-m89-jdk17","8.0.0-m89-jdk11","8.0.0-m89","8.0.0-m89-ubuntu-jdk11","8.0.0-m86","8.0.0-m86-ubuntu-jdk17","8.0.0-m86-jdk11","8.0.0-m86-ubuntu-jdk11","8.0.0-m86-jdk17","8.0.0-m82-ubuntu-jdk11","8.0.0-m82-jdk11","8.0.0-m82","8.0.0-m82-ubuntu-jdk17","8.0.0-m82-jdk17","8.0.0-m76-ubuntu-jdk17","8.0.0-m76-ubuntu-jdk11","8.0.0-m76-jdk17","8.0.0-m76","8.0.0-m76-jdk11","8.0.0-m69","8.0.0-m69-ubuntu-jdk11","8.0.0-m69-jdk17","8.0.0-m69-ubuntu-jdk17","8.0.0-m69-jdk11","8.0.0-m60","8.0.0-m60-jdk17","8.0.0-m60-jdk11","8.0.0-m60-ubuntu-jdk17","8.0.0-m60-ubuntu-jdk11","7.20.0-rc1-ubuntu-jdk11","7.20.0-rc1-jdk11","7.20.0-rc1","8.0.0-struts-m48-ubuntu-jdk17","8.0.0-struts-m48-ubuntu-jdk11","8.0.0-struts-m48-jdk11","8.0.0-struts-m48","8.0.0-struts-m48-jdk17","7.20.0-beta4-jdk11","7.20.0-beta4-ubuntu-jdk11","7.20.0-beta4","8.0.0-m45-ubuntu-jdk11","8.0.0-m45-jdk17","8.0.0-m45-jdk11","8.0.0-m45-ubuntu-jdk17","8.0.0-m45","8.0.0-struts-m39-ubuntu-jdk11","8.0.0-struts-m39-ubuntu-jdk17","8.0.0-struts-m39-jdk11","8.0.0-struts-m39-jdk17","8.0.0-struts-m39","8.0.0-struts-m027-ubuntu-jdk11","8.0.0-struts-m027-ubuntu-jdk17","8.0.0-struts-m027","8.0.0-struts-m027-jdk17","8.0.0-struts-m027-jdk11","7.20.0-beta2-ubuntu-jdk11","7.20.0-beta2-jdk11","7.20.0-beta2-jdk17","7.20.0-beta2","7.20.0-beta2-ubuntu-jdk17","8.0.0-m026-ubuntu-jdk11","8.0.0-m026-ubuntu-jdk17","8.0.0-m026","8.0.0-m026-jdk17","8.0.0-m026-jdk11","8.0.0-struts-m020-ubuntu-jdk17","8.0.0-m023-jdk17","8.0.0-m023-ubuntu-jdk17","8.0.0-struts-m020-jdk17","7.20.0-m104-jdk17","7.20.0-m104-ubuntu-jdk17","8.0.0-m023-jdk11","7.20.0-m104","8.0.0-m023-ubuntu-jdk11","7.20.0-m104-ubuntu-jdk11","8.0.0-m023","7.20.0-m104-jdk11","8.0.0-m021-ubuntu-jdk11","8.0.0-m021","8.0.0-m021-jdk11","7.20.0-m99","7.20.0-m99-ubuntu-jdk11","7.20.0-m99-jdk11","8.0.0-struts-m020-jdk11","8.0.0-struts-m020-ubuntu-jdk11","8.0.0-struts-m020","8.0.0-m020-jdk11","8.0.0-m020","8.0.0-m020-ubuntu-jdk11","7.20.0-m78-ubuntu-jdk11","7.20.0-m78","7.20.0-m78-jdk11","7.19.0-rc3-jdk11","7.19.0-rc3-ubuntu-jdk11","7.19.0-rc3","8.0.0-m018-ubuntu-jdk11","8.0.0-m018-jdk11","8.0.0-m018","8.0.0-m017-ubuntu-jdk11","8.0.0-m017-jdk11","8.0.0-m017","7.19.0-rc2-ubuntu-jdk11","7.19.0-rc2-jdk11","7.19.0-rc2","8.0.0-m015-ubuntu-jdk11","8.0.0-m015","8.0.0-m015-jdk11","7.19.0-rc1-jdk11","7.19.0-rc1","7.19.0-rc1-ubuntu-jdk11","7.19.0-beta1-jdk11","7.19.0-beta1-ubuntu-jdk11","7.19.0-beta1","8.0.0-m014","8.0.0-m014-ubuntu-jdk11","8.0.0-m014-jdk11","7.17-ubuntu-18.04-adoptopenjdk11","7.17-ubuntu","7.17-adoptopenjdk11","7.17.5-ubuntu-18.04-adoptopenjdk11","7.17.5-ubuntu","7.17.5-adoptopenjdk11","eap-ubuntu-18.04-adoptopenjdk11","eap-ubuntu","8.0.0-m012-ubuntu-18.04-adoptopenjdk11","eap-adoptopenjdk11","8.0.0-m012-ubuntu","8.0.0-m012-adoptopenjdk11","8.0.0-m012","8.0.0-m012-jdk11","7.19.0-m02-jdk11","7.19.0-m02-ubuntu-18.04-adoptopenjdk11","7.19.0-m02-adoptopenjdk11","7.19.0-m02-ubuntu","7.19.0-m02","ubuntu-18.04-adoptopenjdk11","ubuntu","adoptopenjdk11","7-ubuntu-18.04-adoptopenjdk11","7-ubuntu","7-adoptopenjdk11","7.9.1-ubuntu-18.04-adoptopenjdk11","7.9.1-ubuntu","7.9.1-adoptopenjdk11","7.9.0-ubuntu-18.04-adoptopenjdk11","7.9.0-ubuntu","7.9.0-adoptopenjdk11","7.8-ubuntu-18.04-adoptopenjdk11","7.8-ubuntu","7.8-adoptopenjdk11","7.8.3-ubuntu-18.04-adoptopenjdk11","7.8.3-ubuntu","7.8.3-adoptopenjdk11","7.8.1-ubuntu-18.04-adoptopenjdk11","7.8.1-ubuntu","7.8.1-adoptopenjdk11","7.8.0-ubuntu-18.04-adoptopenjdk11","7.8.0-ubuntu","7.8.0-adoptopenjdk11","7.7-ubuntu-18.04-adoptopenjdk11","7.7-ubuntu","7.7-adoptopenjdk11","7.7.4-ubuntu-18.04-adoptopenjdk11","7.7.4-ubuntu","7.7.4-adoptopenjdk11","7.7.3-ubuntu-18.04-adoptopenjdk11","7.7.3-ubuntu","7.7.3-adoptopenjdk11","7.7.2-ubuntu-18.04-adoptopenjdk11","7.7.2-ubuntu","7.7.2-adoptopenjdk11","7.6-ubuntu-18.04-adoptopenjdk11","7.6-ubuntu","7.6-adoptopenjdk11","7.6.3-ubuntu-18.04-adoptopenjdk11","7.6.3-ubuntu","7.6.3-adoptopenjdk11","7.6.0-ubuntu-18.04-adoptopenjdk11","7.6.0-ubuntu","7.6.0-adoptopenjdk11","7.5-ubuntu-18.04-adoptopenjdk11","7.5-ubuntu","7.5-adoptopenjdk11","7.5.2-ubuntu-18.04-adoptopenjdk11","7.5.2-ubuntu","7.5.2-adoptopenjdk11","7.5.1-ubuntu-18.04-adoptopenjdk11","7.5.1-ubuntu","7.5.1-adoptopenjdk11","7.5.0-ubuntu-18.04-adoptopenjdk11","7.5.0-ubuntu","7.5.0-adoptopenjdk11","7.4-ubuntu-18.04-adoptopenjdk11","7.4-ubuntu","7.4-adoptopenjdk11","7.4.9-ubuntu-18.04-adoptopenjdk11","7.4.9-ubuntu","7.4.9-adoptopenjdk11","7.4.8-ubuntu-18.04-adoptopenjdk11","7.4.8-ubuntu","7.4.8-adoptopenjdk11","7.4.7-ubuntu-18.04-adoptopenjdk11","7.4.7-ubuntu","7.4.7-adoptopenjdk11","7.4.6-ubuntu-18.04-adoptopenjdk11","7.4.6-ubuntu","7.4.6-adoptopenjdk11","7.4.3-ubuntu-18.04-adoptopenjdk11","7.4.3-ubuntu","7.4.3-adoptopenjdk11","7.4.1-ubuntu","7.4.1-adoptopenjdk11","7.4.17-ubuntu-18.04-adoptopenjdk11","7.4.17-ubuntu","7.4.17-adoptopenjdk11","7.4.16-ubuntu-18.04-adoptopenjdk11","7.4.16-ubuntu","7.4.16-adoptopenjdk11","7.4.15-ubuntu-18.04-adoptopenjdk11","7.4.15-ubuntu","7.4.15-adoptopenjdk11","7.4.14-ubuntu-18.04-adoptopenjdk11","7.4.14-ubuntu","7.4.14-adoptopenjdk11","7.4.13-ubuntu-18.04-adoptopenjdk11","7.4.13-ubuntu","7.4.13-adoptopenjdk11","7.4.12-ubuntu-18.04-adoptopenjdk11","7.4.12-ubuntu","7.4.12-adoptopenjdk11","7.4.0-ubuntu-18.04-adoptopenjdk11","7.4.0-ubuntu","7.4.0-adoptopenjdk11","7.18-ubuntu-18.04-adoptopenjdk11","7.18-ubuntu","7.18-adoptopenjdk11","7.18.1-ubuntu-18.04-adoptopenjdk11","7.18.1-ubuntu","7.18.1-adoptopenjdk11","7.18.0-ubuntu-18.04-adoptopenjdk11","7.18.0-ubuntu","7.18.0-adoptopenjdk11","7.17.4-ubuntu-18.04-adoptopenjdk11","7.17.4-ubuntu","7.17.4-adoptopenjdk11","7.17.3-ubuntu-18.04-adoptopenjdk11","7.17.3-ubuntu","7.17.3-adoptopenjdk11","7.17.2-ubuntu-18.04-adoptopenjdk11","7.17.2-ubuntu","7.17.2-adoptopenjdk11","7.16-ubuntu-18.04-adoptopenjdk11","7.16-ubuntu","7.16-adoptopenjdk11","7.16.4-ubuntu-18.04-adoptopenjdk11","7.16.4-ubuntu","7.16.4-adoptopenjdk11","7.16.3-ubuntu-18.04-adoptopenjdk11","7.16.3-ubuntu","7.16.3-adoptopenjdk11","7.16.2-ubuntu-18.04-adoptopenjdk11","7.16.2-ubuntu","7.16.2-adoptopenjdk11","7.16.1-ubuntu-18.04-adoptopenjdk11","7.16.1-ubuntu","7.16.1-adoptopenjdk11","7.16.0-ubuntu-18.04-adoptopenjdk11","7.16.0-ubuntu","7.16.0-adoptopenjdk11","7.15-ubuntu-18.04-adoptopenjdk11","7.15-ubuntu","7.15-adoptopenjdk11","7.15.2-ubuntu-18.04-adoptopenjdk11","7.15.2-ubuntu","7.15.2-adoptopenjdk11","7.15.1-ubuntu-18.04-adoptopenjdk11","7.15.1-ubuntu","7.15.1-adoptopenjdk11","7.14-ubuntu-18.04-adoptopenjdk11","7.14.3-ubuntu-18.04-adoptopenjdk11","7.14.3-ubuntu","7.14.0-ubuntu","7.14.0-adoptopenjdk11","7.13-ubuntu-18.04-adoptopenjdk11","7.13-ubuntu","7.13-adoptopenjdk11","7.13.7-ubuntu-18.04-adoptopenjdk11","7.13.7-ubuntu","7.13.7-adoptopenjdk11","7.13.6-ubuntu-18.04-adoptopenjdk11","7.13.6-ubuntu","7.13.6-adoptopenjdk11","7.13.5-ubuntu-18.04-adoptopenjdk11","7.13.5-ubuntu","7.13.5-adoptopenjdk11","7.13.4-ubuntu-18.04-adoptopenjdk11","7.13.4-ubuntu","7.13.4-adoptopenjdk11","7.12.5-ubuntu-18.04-adoptopenjdk11","7.12.5-ubuntu","7.12.4-ubuntu-18.04-adoptopenjdk11","7.12.4-ubuntu","7.12.4-adoptopenjdk11","7.12.3-ubuntu-18.04-adoptopenjdk11","7.12.3-ubuntu","7.12.3-adoptopenjdk11","7.12.2-ubuntu-18.04-adoptopenjdk11","7.12.2-ubuntu","7.12.2-adoptopenjdk11","7.12.1-ubuntu-18.04-adoptopenjdk11","7.12.1-ubuntu","7.12.1-adoptopenjdk11","7.11.1-ubuntu-18.04-adoptopenjdk11","7.11.1-ubuntu","7.11.1-adoptopenjdk11","7.11.0-ubuntu-18.04-adoptopenjdk11","7.11.0-ubuntu","7.11.0-adoptopenjdk11","7.10-ubuntu-18.04-adoptopenjdk11","7.10-ubuntu","7.10-adoptopenjdk11","7.10.2-ubuntu-18.04-adoptopenjdk11","7.10.2-ubuntu","7.10.2-adoptopenjdk11","7.10.1-ubuntu-18.04-adoptopenjdk11","7.10.1-ubuntu","7.10.1-adoptopenjdk11","7.10.0-ubuntu-18.04-adoptopenjdk11","7.10.0-ubuntu","7.10.0-adoptopenjdk11","7.14-adoptopenjdk11","7.9-ubuntu-18.04-adoptopenjdk11","7.9-ubuntu","7.9-adoptopenjdk11","7.14.3-adoptopenjdk11","7.9.3-ubuntu-18.04-adoptopenjdk11","7.9.3-ubuntu","7.9.3-adoptopenjdk11","7.14.2-ubuntu-18.04-adoptopenjdk11","7.6.2-ubuntu-18.04-adoptopenjdk11","7.6.2-ubuntu","7.14.2-ubuntu","7.6.2-adoptopenjdk11","7.6.1-ubuntu-18.04-adoptopenjdk11","7.6.1-ubuntu","7.14.2-adoptopenjdk11","7.6.1-adoptopenjdk11","7.14.1-ubuntu-18.04-adoptopenjdk11","7.4.5-ubuntu-18.04-adoptopenjdk11","7.4.5-ubuntu","7.14.1-ubuntu","7.4.5-adoptopenjdk11","7.4.4-ubuntu-18.04-adoptopenjdk11","7.14.1-adoptopenjdk11","7.4.4-ubuntu","7.4.4-adoptopenjdk11","7.14.0-ubuntu-18.04-adoptopenjdk11","7.4.1-ubuntu-18.04-adoptopenjdk11","7.13.3-ubuntu-18.04-adoptopenjdk11","7.4.11-ubuntu-18.04-adoptopenjdk11","7.13.3-ubuntu","7.4.11-ubuntu","7.13.3-adoptopenjdk11","7.4.11-adoptopenjdk11","7.13.2-ubuntu-18.04-adoptopenjdk11","7.4.10-ubuntu-18.04-adoptopenjdk11","7.13.2-ubuntu","7.4.10-ubuntu","7.13.2-adoptopenjdk11","7.4.10-adoptopenjdk11","7.13.1-ubuntu-18.04-adoptopenjdk11","7.17.1-ubuntu-18.04-adoptopenjdk11","7.13.1-ubuntu","7.17.1-ubuntu","7.13.1-adoptopenjdk11","7.17.1-adoptopenjdk11","7.13.0-ubuntu-18.04-adoptopenjdk11","7.13.0-ubuntu","7.17.0-ubuntu-18.04-adoptopenjdk11","7.17.0-ubuntu","7.13.0-adoptopenjdk11","7.17.0-adoptopenjdk11","7.12-ubuntu-18.04-adoptopenjdk11","7.12-ubuntu","7.12-adoptopenjdk11","7.15.0-ubuntu-18.04-adoptopenjdk11","7.12.5-adoptopenjdk11","7.15.0-ubuntu","7.12.0-ubuntu-18.04-adoptopenjdk11","7.15.0-adoptopenjdk11","7.12.0-ubuntu","7.11-ubuntu-18.04-adoptopenjdk11","7.14-ubuntu","7.11-ubuntu","7.11.6-ubuntu-18.04-adoptopenjdk11","7.11.6-ubuntu","7.11.6-adoptopenjdk11","7.11.3-ubuntu-18.04-adoptopenjdk11","7.11.3-ubuntu","7.11.3-adoptopenjdk11","7.11.2-ubuntu-18.04-adoptopenjdk11","7.11.2-ubuntu","7.11.2-adoptopenjdk11","7.12.0-adoptopenjdk11","7.11-adoptopenjdk11","8.0.0-m011-jdk11","8.0.0-m011","8.0.0-m011-ubuntu-18.04-adoptopenjdk11","8.0.0-m011-ubuntu","7.19.0-m01-ubuntu-18.04-adoptopenjdk11","7.19.0-m01-jdk11","7.19.0-m01-adoptopenjdk11","8.0.0-m011-adoptopenjdk11","7.19.0-m01","7.19.0-m01-ubuntu","7.18.0-rc1-ubuntu-18.04-adoptopenjdk11","7.18.0-rc1-ubuntu","7.18.0-rc1-jdk11","7.18.0-rc1-adoptopenjdk11","7.18.0-rc1","7.18.0-beta1-ubuntu-18.04-adoptopenjdk11","7.18.0-beta1-ubuntu","7.18.0-beta1-jdk11","7.18.0-beta1-adoptopenjdk11","7.18.0-beta1","7.18.0-m68-jdk11","7.18.0-m68","7.18.0-m68-ubuntu-18.04-adoptopenjdk11","7.18.0-m68-ubuntu","7.18.0-m68-adoptopenjdk11","7.18.0-m61-ubuntu","7.18.0-m61-adoptopenjdk11","7.18.0-m61","7.18.0-m61-ubuntu-18.04-adoptopenjdk11","7.18.0-m61-jdk11","7.18.0-m53-ubuntu-18.04-adoptopenjdk11","7.18.0-m53-ubuntu","7.18.0-m53","7.18.0-m53-jdk11","7.18.0-m53-adoptopenjdk11","7.18.0-m46-ubuntu","7.18.0-m46-adoptopenjdk11","7.18.0-m46","7.18.0-m46-ubuntu-18.04-adoptopenjdk11","7.18.0-m46-jdk11","7.18.0-m40-ubuntu-18.04-adoptopenjdk11","7.18.0-m40-ubuntu","7.18.0-m40-jdk11","7.18.0-m40-adoptopenjdk11","7.18.0-m40","temurin-7.17.0","7.17.0-rc1-ubuntu-18.04-adoptopenjdk11","7.17.0-rc1-ubuntu","7.17.0-rc1-jdk11","7.17.0-rc1-adoptopenjdk11","7.17.0-rc1","7.4.6-ubuntu-jdk-11","7.4.5-ubuntu-jdk-11","7.4.4-ubuntu-jdk-11","7.4.3-ubuntu-jdk-11","7.4.1-ubuntu-jdk-11","7.4.0-ubuntu-jdk-11","7.3.2","7.3.2-jdk11","7.3.2-adoptopenjdk11","7.3.2-ubuntu-18.04-adoptopenjdk11","7.3.2-ubuntu","7.3.3-ubuntu-18.04-adoptopenjdk11","7.3.3-adoptopenjdk11","7.3.3-jdk11","7.3.3","7.3.3-ubuntu","7.3.4-jdk11","7.3.4-ubuntu-18.04-adoptopenjdk11","7.3.4-adoptopenjdk11","7.3.4-ubuntu","7.3.4","7.3.5-ubuntu","7.3-adoptopenjdk11","7.3-jdk11","7.3.5-ubuntu-18.04-adoptopenjdk11","7.3.5","7.3","7.3.5-adoptopenjdk11","7.3-ubuntu-18.04-adoptopenjdk11","7.3.5-jdk11","7.3-ubuntu","7.3.1-jdk11","7.3.1-ubuntu-18.04-adoptopenjdk11","7.3.1-adoptopenjdk11","7.3.1","7.3.1-ubuntu","7.17.0-beta1-jdk11","7.17.0-beta1-adoptopenjdk11","7.17.0-beta1","7.17.0-beta1-ubuntu-18.04-adoptopenjdk11","7.17.0-beta1-ubuntu","7.17.0-m61-adoptopenjdk11","7.17.0-m61","7.17.0-m61-ubuntu","7.17.0-m61-jdk11","7.17.0-m61-ubuntu-18.04-adoptopenjdk11","7.17.0-m59-ubuntu","7.17.0-m59-adoptopenjdk11","7.17.0-m59","7.17.0-m59-jdk11","7.17.0-m59-ubuntu-18.04-adoptopenjdk11","7.17.0-m47-jdk11","7.17.0-m47","7.17.0-m47-ubuntu","7.17.0-m47-adoptopenjdk11","7.17.0-m47-ubuntu-18.04-adoptopenjdk11","7.16.0-rc1","7.16.0-rc1-ubuntu-18.04-adoptopenjdk11","7.16.0-rc1-ubuntu","7.16.0-rc1-adoptopenjdk11","7.16.0-rc1-jdk11","7.2.0-adoptopenjdk11","7.2.0-ubuntu-18.04-adoptopenjdk11","7.2.0","7.2.0-jdk11","7.2.0-ubuntu","7.2.1","7.2.1-jdk11","7.2.1-ubuntu","7.2.1-adoptopenjdk11","7.2.1-ubuntu-18.04-adoptopenjdk11","7.2.2","7.2.2-adoptopenjdk11","7.2-jdk11","7.2-adoptopenjdk11","7.2-ubuntu","7.2","7.2.2-jdk11","7.2.2-ubuntu","7.2.2-ubuntu-18.04-adoptopenjdk11","7.2-ubuntu-18.04-adoptopenjdk11","7.16.0-beta2","7.16.0-beta2-adoptopenjdk11","7.16.0-beta2-ubuntu-18.04-adoptopenjdk11","7.16.0-beta2-ubuntu","7.16.0-beta2-jdk11","7.16.0-m49-ubuntu-18.04-adoptopenjdk11","7.16.0-m49-jdk11","7.16.0-m49-adoptopenjdk11","7.16.0-m49","7.16.0-m49-ubuntu","7.16.0-m39","7.16.0-m39-adoptopenjdk11","7.16.0-m39-ubuntu-18.04-adoptopenjdk11","7.16.0-m39-ubuntu","7.16.0-m39-jdk11","7.16.0-m30-ubuntu","7.16.0-m30","7.16.0-m30-adoptopenjdk11","7.16.0-m30-jdk11","7.16.0-m30-ubuntu-18.04-adoptopenjdk11","7.15.0-rc1-adoptopenjdk11","7.15.0-rc1-ubuntu-18.04-adoptopenjdk11","7.15.0-rc1-ubuntu","7.15.0-rc1","7.15.0-rc1-jdk11","7.15.0-beta3-jdk11","7.15.0-beta3-adoptopenjdk11","7.15.0-beta3","7.15.0-beta3-ubuntu-18.04-adoptopenjdk11","7.15.0-beta3-ubuntu","7.15.0-beta2-adoptopenjdk11","7.15.0-beta2-jdk11","7.15.0-beta2-ubuntu","7.15.0-beta2-ubuntu-18.04-adoptopenjdk11","7.15.0-beta2","7.15.0-beta1-ubuntu-18.04-adoptopenjdk11","7.15.0-beta1","7.15.0-beta1-jdk11","7.15.0-beta1-ubuntu","7.15.0-beta1-adoptopenjdk11","7.15.0-m35-jdk11","7.15.0-m35-adoptopenjdk11","7.15.0-m35-ubuntu-18.04-adoptopenjdk11","7.15.0-m35-ubuntu","7.15.0-m35","7.15.0-m27","7.15.0-m27-ubuntu","7.15.0-m27-ubuntu-18.04-adoptopenjdk11","7.15.0-m27-jdk11","7.15.0-m27-adoptopenjdk11","7.15.0-m20-ubuntu","7.15.0-m20","7.15.0-m20-ubuntu-18.04-adoptopenjdk11","7.15.0-m20-jdk11","7.15.0-m20-adoptopenjdk11","7.14.0-rc1-jdk11","7.14.0-rc1","7.14.0-rc1-ubuntu","7.14.0-rc1-ubuntu-18.04-adoptopenjdk11","7.14.0-rc1-adoptopenjdk11","7.14.0-beta1-ubuntu","7.14.0-beta1-jdk11","7.14.0-beta1","7.14.0-beta1-ubuntu-18.04-adoptopenjdk11","7.14.0-beta1-adoptopenjdk11","7.14.0-m176-jdk11","7.14.0-m176-ubuntu-18.04-adoptopenjdk11","7.14.0-m176","7.14.0-m176-ubuntu","7.14.0-m176-adoptopenjdk11","7.13.0-rc1-jdk11","7.13.0-rc1","7.13.0-rc1-adoptopenjdk11","7.13.0-rc1-ubuntu","7.13.0-rc1-ubuntu-18.04-adoptopenjdk11","7.13.0-beta-jdk11","7.13.0-beta","7.13.0-beta-ubuntu","7.13.0-beta-ubuntu-18.04-adoptopenjdk11","7.13.0-beta-adoptopenjdk11","7.13.0-m17","7.13.0-m17-adoptopenjdk11","7.13.0-m17-jdk11","7.13.0-m17-ubuntu-18.04-adoptopenjdk11","7.13.0-m17-ubuntu","7.13.0-m16-adoptopenjdk11","7.13.0-m16","7.13.0-m16-ubuntu","7.13.0-m16-ubuntu-18.04-adoptopenjdk11","7.13.0-m16-jdk11","6.13.20-ubuntu","6.5.2-jdk8","6.14.0-jdk8","6.15.7-jdk8","6.3.2-jdk8","6.6.8-adoptopenjdk8","6.11.1","6.6.15-ubuntu","6.1.1","6.13.10","6.8.3-ubuntu-18.04-adoptopenjdk8","6.5.2-ubuntu-18.04-adoptopenjdk8","6.13.20-adoptopenjdk8","6.14.0-ubuntu","6.15.7-ubuntu","6.6.8-ubuntu-18.04-adoptopenjdk8","6.3.2-ubuntu-18.04-adoptopenjdk8","6.11.1-ubuntu","6.6.15-jdk8","6.1.1-jdk8","6.13.10-adoptopenjdk8","6.8.3-adoptopenjdk8","6.5.2-adoptopenjdk8","6.13.20-ubuntu-18.04-adoptopenjdk8","6.14.0-ubuntu-18.04-adoptopenjdk8","6.15.7-adoptopenjdk8","6.6.8-ubuntu","6.3.2-adoptopenjdk8","6.6.15-ubuntu-18.04-adoptopenjdk8","6.8.3","6.13.3-ubuntu-18.04-adoptopenjdk8","6.13.3-adoptopenjdk8","6.6.0","6.13.3-ubuntu","6.6.0-ubuntu-18.04-adoptopenjdk8","6.13.3-jdk8","6.6.0-adoptopenjdk8","6.13.3","6.6.0-jdk8","6.6.0-ubuntu","6.13.4-ubuntu","6.13.4-jdk8","6.6.2","6.6.1-jdk8","6.13.4","6.6.2-adoptopenjdk8","6.6.1-ubuntu-18.04-adoptopenjdk8","6.13.4-ubuntu-18.04-adoptopenjdk8","6.6.2-ubuntu-18.04-adoptopenjdk8","6.6.1-ubuntu","6.13.4-adoptopenjdk8","6.6.2-ubuntu","6.6.1","6.6.2-jdk8","6.6.1-adoptopenjdk8","6.13.5-ubuntu-18.04-adoptopenjdk8","6.13.5-jdk8","6.6.3-ubuntu-18.04-adoptopenjdk8","6.6.10-jdk8","6.13.5","6.6.3-jdk8","6.6.10","6.13.5-adoptopenjdk8","6.6.3-adoptopenjdk8","6.6.10-ubuntu-18.04-adoptopenjdk8","6.13.5-ubuntu","6.6.3-ubuntu","6.6.10-ubuntu","6.6.3","6.6.10-adoptopenjdk8","6.13.6-ubuntu-18.04-adoptopenjdk8","6.13.6-ubuntu","6.6.4-jdk8","6.6.11","6.13.6-jdk8","6.6.4-adoptopenjdk8","6.6.11-adoptopenjdk8","6.13.6-adoptopenjdk8","6.6.11-jdk8","6.6.4-ubuntu-18.04-adoptopenjdk8","6.13.6","6.6.11-ubuntu","6.6.4","6.6.11-ubuntu-18.04-adoptopenjdk8","6.6.4-ubuntu","6.13.7-ubuntu-18.04-adoptopenjdk8","6.15.2-ubuntu","6.8.0-ubuntu","6.13.7-adoptopenjdk8","6.15.2-adoptopenjdk8","6.6.5-ubuntu-18.04-adoptopenjdk8","6.8.0-adoptopenjdk8","6.6.12-ubuntu-18.04-adoptopenjdk8","6.13.7-ubuntu","6.15.2","6.6.5-ubuntu","6.8.0-ubuntu-18.04-adoptopenjdk8","6.6.12-jdk8","6.13.7","6.15.2-jdk8","6.6.5","6.8.0-jdk8","6.6.12","6.13.7-jdk8","6.15.2-ubuntu-18.04-adoptopenjdk8","6.6.5-jdk8","6.8.0","6.6.12-ubuntu","6.6.5-adoptopenjdk8","6.6.12-adoptopenjdk8","6.13.0-jdk8","6.13.8-ubuntu","6.8.1","6.15.4-ubuntu","6.5.0-ubuntu","6.13.0-adoptopenjdk8","6.13.8","6.8.1-ubuntu-18.04-adoptopenjdk8","6.15.4-adoptopenjdk8","6.6.6-ubuntu","6.6.13-adoptopenjdk8","6.5.0-jdk8","6.13.0-ubuntu","6.13.8-jdk8","6.15.4","6.6.6-ubuntu-18.04-adoptopenjdk8","6.8.1-ubuntu","6.6.13","6.5.0-adoptopenjdk8","6.13.0","6.13.8-adoptopenjdk8","6.6.13-jdk8","6.8.1-jdk8","6.15.4-jdk8","6.6.6-jdk8","6.5.0","6.13.0-ubuntu-18.04-adoptopenjdk8","6.13.8-ubuntu-18.04-adoptopenjdk8","6.6.13-ubuntu-18.04-adoptopenjdk8","6.6.6","6.15.4-ubuntu-18.04-adoptopenjdk8","6.8.1-adoptopenjdk8","6.5.0-ubuntu-18.04-adoptopenjdk8","6.6.13-ubuntu","6.6.6-adoptopenjdk8","6.13.1-ubuntu","6.15.6-ubuntu-18.04-adoptopenjdk8","6.11.0-adoptopenjdk8","6.13.9-jdk8","6.1.0-ubuntu-18.04-adoptopenjdk8","6.5.1-ubuntu-18.04-adoptopenjdk8","6.3.1-jdk8","6.8.2-jdk8","6.13.1-jdk8","6.6.7-ubuntu","6.15.6-ubuntu","6.11.0-ubuntu-18.04-adoptopenjdk8","6.6.14-adoptopenjdk8","6.1.0-adoptopenjdk8","6.13.9","6.5.1-adoptopenjdk8","6.3.1","6.8.2-adoptopenjdk8","6.13.1-adoptopenjdk8","6.6.7-ubuntu-18.04-adoptopenjdk8","6.15.6-jdk8","6.6.14-ubuntu","6.11.0-ubuntu","6.13.9-ubuntu","6.1.0-ubuntu","6.5.1-ubuntu","6.3.1-ubuntu","6.8.2-ubuntu-18.04-adoptopenjdk8","6.13.1-ubuntu-18.04-adoptopenjdk8","6.6.7-adoptopenjdk8","6.15.6-adoptopenjdk8","6.6.14-jdk8","6.13.9-adoptopenjdk8","6.11.0-jdk8","6.1.0-jdk8","6.3.1-ubuntu-18.04-adoptopenjdk8","6.5.1","6.8.2","6.13.1","6.6.7-jdk8","6.15.6","6.6.14-ubuntu-18.04-adoptopenjdk8","6.13.9-ubuntu-18.04-adoptopenjdk8","6.1.0","6.11.0","6.3.1-adoptopenjdk8","6.5.1-jdk8","6.8.2-ubuntu","6.6.7","6.6.14","6.11.1-adoptopenjdk8","6.1.1-adoptopenjdk8","6.5.2-ubuntu","6.8.3-jdk8","6.13.10-ubuntu","6.6.8-jdk8","6.15.7-ubuntu-18.04-adoptopenjdk8","6.14.0-adoptopenjdk8","6.1.1-ubuntu-18.04-adoptopenjdk8","6.11.1-jdk8","6.8.3-ubuntu","6.6.15","6.15.7","6.14.0","6.1.1-ubuntu","6.3.2","6.5.2","6.13.10-ubuntu-18.04-adoptopenjdk8","6.6.8","6.3.2-ubuntu","6.13.10-jdk8","6.11.1-ubuntu-18.04-adoptopenjdk8","6.6.15-adoptopenjdk8","6.13.11-ubuntu-18.04-adoptopenjdk8","6.13.11-ubuntu","6.13.11-jdk8","6.13.11","6.13.11-adoptopenjdk8","6.13.12","6.13.12-jdk8","6.13.12-ubuntu-18.04-adoptopenjdk8","6.13.12-ubuntu","6.13.12-adoptopenjdk8","6.13.13-jdk8","6.13.13-ubuntu","6.13.13","6.13.13-ubuntu-18.04-adoptopenjdk8","6.13.13-adoptopenjdk8","6.13.15-ubuntu","6.13.15-adoptopenjdk8","6.13.15","6.13.15-jdk8","6.13.15-ubuntu-18.04-adoptopenjdk8","6.13.17","6.13.17-adoptopenjdk8","6.13.17-jdk8","6.13.17-ubuntu-18.04-adoptopenjdk8","6.13.17-ubuntu","6.13.18","6.13.18-ubuntu","6.13.18-jdk8","6.13.18-ubuntu-18.04-adoptopenjdk8","6.13.18-adoptopenjdk8","6.13.19-jdk8","6.13.19-ubuntu-18.04-adoptopenjdk8","6.13.19","6.13.19-adoptopenjdk8","6.13.19-ubuntu","6.13.2-adoptopenjdk8","6.13.2-jdk8","6.13.2","6.13.2-ubuntu","6.13.2-ubuntu-18.04-adoptopenjdk8","6.13.20-jdk8","6.13.20","7.13.0-m15-adoptopenjdk11","7.13.0-m15-ubuntu","7.13.0-m15-jdk11","7.13.0-m15","7.13.0-m14-jdk11","7.13.0-m14-ubuntu-18.04-adoptopenjdk11","7.13.0-m14-adoptopenjdk11","7.13.0-m14-ubuntu","7.13.0-m14","7.1.0-ubuntu","7.1.0-jdk11","7.1.0-ubuntu-18.04-adoptopenjdk11","7.1.0","7.1.0-adoptopenjdk11","7.1.1-ubuntu","7.1.1-adoptopenjdk11","7.1.1-ubuntu-18.04-adoptopenjdk11","7.1.1-jdk11","7.1.1","7.13.0-m13-adoptopenjdk11","7.13.0-m13","7.13.0-m13-ubuntu-18.04-adoptopenjdk11","7.13.0-m13-ubuntu","7.13.0-m13-jdk11","7.1.2-ubuntu-18.04-adoptopenjdk11","6.4.2-jdk8","6.7.3-adoptopenjdk8","6.12.3-jdk8","6.15.10","6.10.1","6.0.6-jdk8","6.4.2-adoptopenjdk8","6.7-jdk8","6.15.10-adoptopenjdk8","6.2.3","6.0.6-ubuntu-18.04-adoptopenjdk8","6.7-adoptopenjdk8","7.0.2-ubuntu","6-jdk8","6.2.3-jdk8","6.0.6-adoptopenjdk8","7.0.2-jdk8","6.7","6.12.4-jdk8","6","6.2.3-ubuntu","6.10.2-ubuntu","6.0.6","6.4-adoptopenjdk8","7.0.2","6.7-ubuntu-18.04-adoptopenjdk8","6.12-ubuntu","6.15-ubuntu","6.2.3-ubuntu-18.04-adoptopenjdk8","6.10.2-adoptopenjdk8","6.0.6-ubuntu","6.4","7.0.2-ubuntu-18.04-adoptopenjdk8","6.12.4-ubuntu","6.7.3-ubuntu","6.2.3-adoptopenjdk8","6.10.2","6.4-ubuntu","7.0.2-adoptopenjdk8","6.12-adoptopenjdk8","6.10.2-ubuntu-18.04-adoptopenjdk8","6.4.3","6.12.4-adoptopenjdk8","6.0-jdk8","6.4.3-ubuntu-18.04-adoptopenjdk8","6.10.2-jdk8","6.12.4","6.2.4-jdk8","6.4.3-jdk8","6.0-ubuntu","6.12.4-ubuntu-18.04-adoptopenjdk8","7.0.3-adoptopenjdk8","6.2.4","6.0","6.4.3-adoptopenjdk8","6.12","7.0.3-jdk8","6.2.4-ubuntu","6.0.7-jdk8","6.10-adoptopenjdk8","6.4-ubuntu-18.04-adoptopenjdk8","6.12-jdk8","7.0.3-ubuntu-18.04-adoptopenjdk8","6.10.3","6.0.7-ubuntu-18.04-adoptopenjdk8","6.2-jdk8","6.4-jdk8","6.12-ubuntu-18.04-adoptopenjdk8","6.0-adoptopenjdk8","6.2","6.10.3-ubuntu","7.0.3","6.4.3-ubuntu","6.0-ubuntu-18.04-adoptopenjdk8","6.2-ubuntu-18.04-adoptopenjdk8","6.10.3-jdk8","7.0.3-ubuntu","6.0.7-adoptopenjdk8","6.2-adoptopenjdk8","6.10.3-ubuntu-18.04-adoptopenjdk8","6.0.7","6.10-ubuntu-18.04-adoptopenjdk8","6.2.4-ubuntu-18.04-adoptopenjdk8","6.0.7-ubuntu","6.10","6.2.4-adoptopenjdk8","7.0.4-jdk8","6.10.3-adoptopenjdk8","6.2-ubuntu","7.0.4","6.10-ubuntu","7.0.4-ubuntu-18.04-adoptopenjdk8","6.10-jdk8","7.0.4-adoptopenjdk8","7.0.4-ubuntu","7.0.5-adoptopenjdk8","7.0-adoptopenjdk8","7.0.5-ubuntu","7.0-ubuntu","7.0.5-jdk8","7.0.5-ubuntu-18.04-adoptopenjdk8","7.0","7.0-ubuntu-18.04-adoptopenjdk8","7.0-jdk8","7.0.5","7.13.0-m12-jdk11","7.13.0-m12-ubuntu-18.04-adoptopenjdk11","7.13.0-m12","7.13.0-m12-ubuntu","7.13.0-m12-adoptopenjdk11","6.14.1-adoptopenjdk8","6.14.1-ubuntu-18.04-adoptopenjdk8","6.14.1-ubuntu","6.8-ubuntu","6.14.1","6.8-adoptopenjdk8","6.14.1-jdk8","6.8.5-jdk8","6.8.5","6.8.5-ubuntu","6.8-ubuntu-18.04-adoptopenjdk8","6.1.2-adoptopenjdk8","6.14.2-ubuntu","6.8-jdk8","6.1.2-ubuntu","6.14.2-adoptopenjdk8","6.8","6.1.2","6.14.2","6.8.5-ubuntu-18.04-adoptopenjdk8","6.1.2-ubuntu-18.04-adoptopenjdk8","6.14.2-jdk8","6.8.5-adoptopenjdk8","6.1.2-jdk8","6.14.2-ubuntu-18.04-adoptopenjdk8","6.3.3-adoptopenjdk8","6.1.3-ubuntu","6.14-adoptopenjdk8","6.3.3-jdk8","6.1.3-ubuntu-18.04-adoptopenjdk8","6.3.3-ubuntu-18.04-adoptopenjdk8","6.14","6.1.3-jdk8","6.3.3-ubuntu","6.14-ubuntu-18.04-adoptopenjdk8","6.1.3","6.9.0-ubuntu-18.04-adoptopenjdk8","6.3.3","6.14.3-ubuntu-18.04-adoptopenjdk8","6.1.3-adoptopenjdk8","6.9.0-adoptopenjdk8","6.0.1-adoptopenjdk8","6.14-ubuntu","6.9.0-jdk8","6.0.1-ubuntu","6.14-jdk8","6.9.0","6.0.1","6.14.3-ubuntu","6.9.0-ubuntu","6.3.4-jdk8","6.0.1-ubuntu-18.04-adoptopenjdk8","6.1.4-ubuntu-18.04-adoptopenjdk8","6.14.3","6.3-adoptopenjdk8","6.6.16-ubuntu-18.04-adoptopenjdk8","6.6.9-jdk8","6.1-jdk8","6.0.1-jdk8","6.14.3-adoptopenjdk8","6.3.4","6.6.9-ubuntu-18.04-adoptopenjdk8","6.1.4","6.6.16-ubuntu","6.14.3-jdk8","6.3-ubuntu-18.04-adoptopenjdk8","6.6.9","6.1-adoptopenjdk8","6.6.16","6.9.1-adoptopenjdk8","6.6.9-adoptopenjdk8","6.1.4-ubuntu","6.3-jdk8","6.6.16-adoptopenjdk8","6.9.1-ubuntu","6.0.2-adoptopenjdk8","6.6.9-ubuntu","6.3.4-ubuntu","6.6.16-jdk8","6.1-ubuntu","6.0.2","6.9.1-ubuntu-18.04-adoptopenjdk8","6.3","6.0.2-ubuntu","6.1.4-jdk8","6.9.1-jdk8","6.3-ubuntu","6.0.2-jdk8","6.1-ubuntu-18.04-adoptopenjdk8","6.9.1","6.3.4-ubuntu-18.04-adoptopenjdk8","6.7.0-adoptopenjdk8","6.0.2-ubuntu-18.04-adoptopenjdk8","6.1.4-adoptopenjdk8","6.6","6.15.1-jdk8","6.3.4-adoptopenjdk8","6.7.0","6.1","6.6.17-ubuntu","6.15.1-adoptopenjdk8","6.7.0-ubuntu","6.6-jdk8","6.9.3-jdk8","6.15.1-ubuntu","6.7.0-jdk8","6.0.3-ubuntu-18.04-adoptopenjdk8","6.6.17","6.9.3","6.15.1-ubuntu-18.04-adoptopenjdk8","6.7.0-ubuntu-18.04-adoptopenjdk8","6.0.3","6.6.17-ubuntu-18.04-adoptopenjdk8","6.9-adoptopenjdk8","6.15.1","6.0.3-adoptopenjdk8","6.9-jdk8","6.6-ubuntu","6.0.3-jdk8","6.6.17-jdk8","6.9.3-adoptopenjdk8","6.0.3-ubuntu","6.4.0-adoptopenjdk8","6.7.1-jdk8","6.10.0-ubuntu-18.04-adoptopenjdk8","6.6-adoptopenjdk8","6.9-ubuntu-18.04-adoptopenjdk8","6.4.0","6.7.1-adoptopenjdk8","6.10.0-jdk8","6.6.17-adoptopenjdk8","6.9-ubuntu","6.4.0-ubuntu-18.04-adoptopenjdk8","6.7.1-ubuntu","6.15.10-ubuntu","6.10.0-adoptopenjdk8","6.9.3-ubuntu-18.04-adoptopenjdk8","6.6-ubuntu-18.04-adoptopenjdk8","6.4.0-ubuntu","6.7.1","6.15","6.10.0-ubuntu","6.9.3-ubuntu","6.0.4-jdk8","6.4.0-jdk8","6.7.1-ubuntu-18.04-adoptopenjdk8","6-adoptopenjdk8","6.10.0","6.9","6.0.4-adoptopenjdk8","6.0.4-ubuntu","6.15-jdk8","6.0.4-ubuntu-18.04-adoptopenjdk8","6.15.10-ubuntu-18.04-adoptopenjdk8","6.4.1-ubuntu","6.0.4","7.1-ubuntu-18.04-adoptopenjdk11","6.7.2-ubuntu-18.04-adoptopenjdk8","6.4.1-ubuntu-18.04-adoptopenjdk8","6-ubuntu-18.04-adoptopenjdk8","6.7.2-ubuntu","7.1.2","6.10.1-ubuntu-18.04-adoptopenjdk8","6.4.1-adoptopenjdk8","6.15.10-jdk8","6.7.2","6.10.1-ubuntu","7.1","6.4.1-jdk8","6.15-ubuntu-18.04-adoptopenjdk8","6.7.2-jdk8","6.10.1-adoptopenjdk8","6.0.5","7.1.2-ubuntu","7.0.1-jdk8","6.4.1","6.7.2-adoptopenjdk8","6.10.1-jdk8","6.0.5-adoptopenjdk8","7.1-ubuntu","7.0.1","6.0.5-ubuntu-18.04-adoptopenjdk8","7.1.2-jdk11","7.0.1-adoptopenjdk8","6-ubuntu","6.0.5-jdk8","7.0.1-ubuntu","6.15-adoptopenjdk8","6.4.2-ubuntu-18.04-adoptopenjdk8","6.0.5-ubuntu","7.1.2-adoptopenjdk11","7.0.1-ubuntu-18.04-adoptopenjdk8","6.7.3-ubuntu-18.04-adoptopenjdk8","7.1-adoptopenjdk11","7.1-jdk11","6.4.2","6.4.2-ubuntu","6.7.3","6.7.3-jdk8","6.7-ubuntu","6.13","6.13.21-adoptopenjdk8","6.13.21-ubuntu-18.04-adoptopenjdk8","6.13-ubuntu","6.13.21-jdk8","6.13-adoptopenjdk8","6.13-ubuntu-18.04-adoptopenjdk8","6.13.21-ubuntu","6.13-jdk8","6.13.21","6.12.2-jdk8","6.12.3-ubuntu-18.04-adoptopenjdk8","6.12.3-adoptopenjdk8","6.12.3","6.12.3-ubuntu","7.13.0-m09-jdk11","7.13.0-m09","7.13.0-m09-adoptopenjdk11","7.13.0-m09-ubuntu-18.04-adoptopenjdk11","7.13.0-m09-ubuntu","6.11.2-ubuntu","6.11-adoptopenjdk8","6.11.2-adoptopenjdk8","6.11.2-ubuntu-18.04-adoptopenjdk8","6.11.2","6.11-ubuntu-18.04-adoptopenjdk8","6.11.2-jdk8","6.11-jdk8","6.11","6.11-ubuntu","6.15.8-ubuntu-18.04-adoptopenjdk8","6.15.8-ubuntu","6.15.8-adoptopenjdk8","6.15.8","6.15.8-jdk8","6.12.0-jdk8","6.5.3-ubuntu","6.12.0","6.5.3-jdk8","6.5","6.15.9","6.5-ubuntu","6.15.9-ubuntu","6.5-jdk8","6.15.9-jdk8","6.5.3-adoptopenjdk8","6.15.9-adoptopenjdk8","6.5.3-ubuntu-18.04-adoptopenjdk8","6.15.9-ubuntu-18.04-adoptopenjdk8","6.5-ubuntu-18.04-adoptopenjdk8","6.5-adoptopenjdk8","6.12.0-ubuntu","6.5.3","6.12.0-ubuntu-18.04-adoptopenjdk8","6.2.0","6.12.0-adoptopenjdk8","6.2.0-jdk8","6.2.0-adoptopenjdk8","6.2.0-ubuntu-18.04-adoptopenjdk8","6.12.1-ubuntu-18.04-adoptopenjdk8","6.2.0-ubuntu","6.12.1","6.12.1-ubuntu","6.12.1-adoptopenjdk8","6.2.1-jdk8","6.12.1-jdk8","6.2.1-adoptopenjdk8","6.2.1-ubuntu","6.2.1","6.2.1-ubuntu-18.04-adoptopenjdk8","6.12.2-ubuntu","6.12.2","6.12.2-adoptopenjdk8","6.2.2-ubuntu","6.12.2-ubuntu-18.04-adoptopenjdk8","6.2.2-ubuntu-18.04-adoptopenjdk8","6.2.2-adoptopenjdk8","6.2.2","6.2.2-jdk8","7.13.0-m08","7.13.0-m08-adoptopenjdk11","7.13.0-m08-ubuntu-18.04-adoptopenjdk11","7.13.0-m08-jdk11","7.13.0-m08-ubuntu","6.15.2-alpine-adoptopenjdk8","6.15.2-alpine","6.13.0-alpine-adoptopenjdk8","6.13.0-alpine","6.15.4-alpine","6.15.4-alpine-adoptopenjdk8","6.13.1-alpine","6.13.1-alpine-adoptopenjdk8","6.15.6-alpine","6.15.6-alpine-adoptopenjdk8","6.13.10-alpine","6.13.10-alpine-adoptopenjdk8","6.15.7-alpine","6.15.7-alpine-adoptopenjdk8","6.13.11-alpine","6.0.1-alpine","6.15.8-alpine-adoptopenjdk8","6.13.11-alpine-adoptopenjdk8","6.0.1-alpine-adoptopenjdk8","6.15.8-alpine","6.6.9-alpine","6.13.12-alpine-adoptopenjdk8","6.13.3-alpine","6.0.2-alpine","6.12.0-alpine-adoptopenjdk8","6.15.9-alpine-adoptopenjdk8","6.13.12-alpine","6.7.0-alpine-adoptopenjdk8","6.6.0-alpine","6.13.3-alpine-adoptopenjdk8","6.0.2-alpine-adoptopenjdk8","6.12.0-alpine","6.15.9-alpine","6.6.0-alpine-adoptopenjdk8","6.7.0-alpine","6.13.13-alpine","6.0.3-alpine-adoptopenjdk8","6.13.4-alpine-adoptopenjdk8","6.2.0-alpine","6.12.1-alpine-adoptopenjdk8","6.6.2-alpine-adoptopenjdk8","6.6.1-alpine","6.7.1-alpine","6.13.13-alpine-adoptopenjdk8","6.0.3-alpine","6.2.0-alpine-adoptopenjdk8","6.12.1-alpine","6.13.4-alpine","6.6.2-alpine","6.6.1-alpine-adoptopenjdk8","6.7.1-alpine-adoptopenjdk8","6.10.0-alpine-adoptopenjdk8","6.13.15-alpine","6.0.4-alpine","6.13.5-alpine","6.2.1-alpine","6.6.3-alpine","6.12.2-alpine-adoptopenjdk8","6.15.1-alpine-adoptopenjdk8","6.7.2-alpine-adoptopenjdk8","6.6.10-alpine-adoptopenjdk8","6.10.0-alpine","6.13.15-alpine-adoptopenjdk8","6.0.4-alpine-adoptopenjdk8","6.13.5-alpine-adoptopenjdk8","6.2.1-alpine-adoptopenjdk8","6.6.3-alpine-adoptopenjdk8","6.12.2-alpine","6.15.1-alpine","6.7.2-alpine","6.6.10-alpine","7.13.0-m07-jdk11","7.13.0-m07-ubuntu-18.04-adoptopenjdk11","7.13.0-m07-ubuntu","7.13.0-m07-adoptopenjdk11","7.13.0-m07","6.6.4-alpine","6.6.4-alpine-adoptopenjdk8","6.6.5-alpine-adoptopenjdk8","6.6.5-alpine","6.6.6-alpine","6.6.6-alpine-adoptopenjdk8","6.6.7-alpine-adoptopenjdk8","6.6.7-alpine","6.6.8-alpine","6.6.8-alpine-adoptopenjdk8","6.6.9-alpine-adoptopenjdk8","6.4.0-alpine","6.4.0-alpine-adoptopenjdk8","6.4.1-alpine","6.4.1-alpine-adoptopenjdk8","7.13.0-m06-ubuntu-18.04-adoptopenjdk11","7.13.0-m06-adoptopenjdk11","7.13.0-m06","7.13.0-m06-ubuntu","7.13.0-m06-jdk11","6.7-alpine-adoptopenjdk8","6.7.3-alpine-adoptopenjdk8","6.7.3-alpine","6.10.1-alpine","6.7-alpine","6.2.2-alpine","6.10.1-alpine-adoptopenjdk8","6.2.2-alpine-adoptopenjdk8","6.4.2-alpine","6-alpine-adoptopenjdk8","6.4.2-alpine-adoptopenjdk8","6.10.2-alpine","6.15.10-alpine","6.13.17-alpine","6.2.3-alpine-adoptopenjdk8","6.13.6-alpine-adoptopenjdk8","6.12.3-alpine","6.10.2-alpine-adoptopenjdk8","6.15-alpine","6.13.17-alpine-adoptopenjdk8","6.8.0-alpine","6.13.6-alpine","6.2.3-alpine","6.4.3-alpine","6.12.3-alpine-adoptopenjdk8","6.8.0-alpine-adoptopenjdk8","6.15-alpine-adoptopenjdk8","6.6.11-alpine-adoptopenjdk8","6.4-alpine-adoptopenjdk8","6-alpine","6.6.11-alpine","6.10.3-alpine","6.4-alpine","6.13.18-alpine-adoptopenjdk8","6.15.10-alpine-adoptopenjdk8","6.12-alpine","6.2-alpine","6.13.7-alpine-adoptopenjdk8","6.10-alpine","6.4.3-alpine-adoptopenjdk8","6.8.1-alpine-adoptopenjdk8","6.13.18-alpine","6.12-alpine-adoptopenjdk8","6.13.7-alpine","6.10.3-alpine-adoptopenjdk8","6.2.4-alpine","6.8.1-alpine","6.6.12-alpine-adoptopenjdk8","6.12.4-alpine-adoptopenjdk8","6.10-alpine-adoptopenjdk8","6.2-alpine-adoptopenjdk8","6.6.12-alpine","6.12.4-alpine","6.13.19-alpine-adoptopenjdk8","6.2.4-alpine-adoptopenjdk8","6.13.8-alpine","6.8.2-alpine-adoptopenjdk8","6.13.19-alpine","6.13.8-alpine-adoptopenjdk8","6.5.0-alpine-adoptopenjdk8","6.8.2-alpine","6.6.13-alpine","6.5.0-alpine","6.6.13-alpine-adoptopenjdk8","6.13.2-alpine","6.11.0-alpine-adoptopenjdk8","6.13.9-alpine-adoptopenjdk8","6.8.3-alpine-adoptopenjdk8","6.13.2-alpine-adoptopenjdk8","6.11.0-alpine","6.3.1-alpine","6.13.9-alpine","6.5.1-alpine","6.8.3-alpine","6.6.14-alpine","6.3.1-alpine-adoptopenjdk8","6.6.14-alpine-adoptopenjdk8","6.5.1-alpine-adoptopenjdk8","6.13.20-alpine","6.11.1-alpine","6.14.0-alpine","6.8-alpine-adoptopenjdk8","6.11.1-alpine-adoptopenjdk8","6.13.20-alpine-adoptopenjdk8","6.3.2-alpine-adoptopenjdk8","6.14.0-alpine-adoptopenjdk8","6.6.15-alpine","6.5.2-alpine-adoptopenjdk8","6.8.5-alpine","6.3.2-alpine","6.6.15-alpine-adoptopenjdk8","6.8.5-alpine-adoptopenjdk8","6.5.2-alpine","6.13.21-alpine-adoptopenjdk8","6.11.2-alpine","6.8-alpine","6.14.1-alpine-adoptopenjdk8","6.13.21-alpine","6.3.3-alpine-adoptopenjdk8","6.11-alpine","6.6.16-alpine","6.14.1-alpine","6.5-alpine","6.13-alpine","6.3.3-alpine","6.11.2-alpine-adoptopenjdk8","6.6.16-alpine-adoptopenjdk8","6.5-alpine-adoptopenjdk8","6.13-alpine-adoptopenjdk8","6.11-alpine-adoptopenjdk8","6.5.3-alpine","6.14.2-alpine","6.3-alpine","6.5.3-alpine-adoptopenjdk8","6.6-alpine-adoptopenjdk8","6.9.0-alpine-adoptopenjdk8","6.14.2-alpine-adoptopenjdk8","6.3-alpine-adoptopenjdk8","6.6.17-alpine","6.9.0-alpine","6.3.4-alpine-adoptopenjdk8","6.6-alpine","6.6.17-alpine-adoptopenjdk8","6.3.4-alpine","6.14.3-alpine","6.9.1-alpine","6.14-alpine-adoptopenjdk8","6.9.1-alpine-adoptopenjdk8","6.1-alpine","6.14.3-alpine-adoptopenjdk8","6.14-alpine","6.9-alpine","6.9.3-alpine-adoptopenjdk8","6.9.3-alpine","6.9-alpine-adoptopenjdk8","6.1.3-alpine","6.1.4-alpine","6.1.4-alpine-adoptopenjdk8","6.1-alpine-adoptopenjdk8","6.1.3-alpine-adoptopenjdk8","6.1.2-alpine-adoptopenjdk8","6.1.2-alpine","6.0.5-alpine-adoptopenjdk8","6.0.5-alpine","6.0.6-alpine-adoptopenjdk8","6.0.6-alpine","7.13.0-m04-jdk11","7.13.0-m04","7.13.0-m04-ubuntu-18.04-adoptopenjdk11","7.13.0-m04-ubuntu","7.13.0-m04-adoptopenjdk11","7.13.0-m03-ubuntu-18.04-adoptopenjdk11","7.13.0-m03","7.13.0-m03-ubuntu","7.13.0-m03-jdk11","7.13.0-m03-adoptopenjdk11","7.13.0-m02-jdk11","7.13.0-m02-adoptopenjdk11","7.13.0-m02-ubuntu","7.13.0-m02","7.13.0-m02-ubuntu-18.04-adoptopenjdk11","6.0.7-alpine","6.0-alpine","6.0-alpine-adoptopenjdk8","6.0.7-alpine-adoptopenjdk8","6.1.0-alpine","6.1.0-alpine-adoptopenjdk8","6.1.1-alpine","6.1.1-alpine-adoptopenjdk8"],"bamboo":["latest","9.4-ubuntu","9.4-jdk11","9.4.3-ubuntu","9.4","9.4.3","9.4.3-jdk11","ubuntu","jdk11","9-ubuntu","9-jdk11","9.5-ubuntu","9.5-jdk11","9.5.1-ubuntu","9.5.1-jdk11","9.5.1","9.5","9","9.2-ubuntu","9.2-jdk11","9.2","9.2.11-jdk11","9.2.11-ubuntu","9.2.11","9.3.3-ubuntu","9.3.3-jdk11","9.3.3","9.2.4-ubuntu","9.2.4-jdk11","9.3.2-ubuntu","9.3.2-jdk11","9.0-ubuntu","9.2.4","9.3.2","9.0-jdk11","9.0.4-ubuntu","9.0.4-jdk11","9.0.4","9.0","8.2.6-ubuntu","8.2.6","8.2.6-jdk11","8.1.11-ubuntu","8.1.11-jdk11","8.1.11","8.0.0-jdk11","8.0.0-ubuntu","8.1.8-ubuntu","9.3.4-ubuntu","8.1.8-jdk11","9.3.4-jdk11","8.1.8","8.0.0","8.0.4-ubuntu","9.3.4","8.0.4-jdk11","9.2.5-ubuntu","9.2.5-jdk11","8.0.4","9.2.5","9.1.0-ubuntu","9.1.0-jdk11","9.1.0","8.2.7-ubuntu","8.2.7-jdk11","8.2.7","8.1-ubuntu","8.1-jdk11","8.1.12-ubuntu","8.1.12-jdk11","8.1","8.1.12","8.0.5-ubuntu","8.0.5-jdk11","8.0.1-ubuntu","9.1.1-jdk11","8.1.9-ubuntu","8.0.1-jdk11","8.0.1","8.1.9-jdk11","9.1.1","8.1.9","8.2.8-ubuntu","8.2.8-jdk11","8.0.5","8.2.8","8.1.2-ubuntu","8.1.2-jdk11","8.1.2","8.0.10-ubuntu","9.3.5-ubuntu","8.0.10-jdk11","9.3.5-jdk11","8.0.10","9.3.5","9.2.6-jdk11","9.2.6-ubuntu","9.2.6","9.1.1-ubuntu","8.2.0-ubuntu","8.2.0-jdk11","9.1.2-ubuntu","8.2.0","9.1.2-jdk11","8.0.6-ubuntu","8.0.6-jdk11","9.1.2","8.0.6","8-ubuntu","8-jdk11","8.2-ubuntu","8.2-jdk11","8.2.9-ubuntu","8.2.9-jdk11","8.2.9","8.2","8.1.3-ubuntu","8.1.3-jdk11","8.1.3","8.0.11-jdk11","8.0.11-ubuntu","8.0.11","9.3-ubuntu","8","9.3-jdk11","9.3.6-ubuntu","9.3.6-jdk11","9.3.6","9.3","9.2.7-ubuntu","9.2.7-jdk11","9.2.7","8.2.1-ubuntu","8.2.1-jdk11","8.2.1","8.0.7-ubuntu","9.4.0-jdk11","8.0.7-jdk11","9.2.8-ubuntu","8.0.7","9.2.8-jdk11","9.2.8","9.1-ubuntu","9.1-jdk11","9.1.3-ubuntu","9.1.3-jdk11","9.1.3","9.1","9.0.0-jdk11","9.0.0-ubuntu","9.0.0","8.1.4-ubuntu","8.1.4-jdk11","8.1.4","8.0.8-ubuntu","8.0.8-jdk11","8.0.8","8.0.12-ubuntu","8.0.12","8.0.12-jdk11","8.2.2-ubuntu","8.2.2-jdk11","8.1.5-jdk11","8.2.2","8.1.5-ubuntu","8.1.5","8.0-ubuntu","8.0-jdk11","8.0.13-jdk11","8.0.13-ubuntu","8.0.13","9.4.1-ubuntu","8.0","9.4.1-jdk11","9.4.1","9.2.9-ubuntu","9.2.9","9.2.9-jdk11","9.2.1-ubuntu","9.2.1-jdk11","9.2.1","9.0.1-ubuntu","9.0.1-jdk11","9.0.1","8.2.3-ubuntu","8.2.3-jdk11","8.2.3","9.4.2-jdk11","8.0.9-ubuntu","8.0.9-jdk11","9.0.2-ubuntu","8.0.9","9.0.2-jdk11","9.0.2","8.1.6-ubuntu","8.1.6-jdk11","8.1.6","8.0.2-ubuntu","8.0.2-jdk11","8.0.2","7-ubuntu","7-jdk8","7.2-ubuntu","7.2-jdk8","7.2.7-jdk8","7.2.7-ubuntu","7.2.7","7.2.5-ubuntu","7.2.5-jdk8","7.2.5","7.2.3-ubuntu","7.2.3-jdk8","7.2.3","7.2.10-ubuntu","7.2.10-jdk8","7.2.10","7.2.0-ubuntu","7.2.0-jdk8","7.2.0","7.2","7.1.3-ubuntu","7.1.3-jdk8","7.1.3","7","9.3.0-ubuntu","9.3.0-jdk11","9.3.0","9.2.10-ubuntu","9.2.10-jdk11","9.2.10","8.2.4-ubuntu","8.2.4-jdk11","8.2.4","8.1.1-ubuntu","8.1.1-jdk11","8.1.1","8.1.7-ubuntu","8.1.7-jdk11","8.1.7","8.0.3-ubuntu","8.0.3-jdk11","8.0.3","jdk17","9-jdk17","9.5-jdk17","9.5.0-ubuntu","9.5.0-jdk17","9.5.0-jdk11","9.5.0","9.4-jdk17","9.4.2-ubuntu","9.4.0-ubuntu","9.4.2-jdk17","9.4.2","9.4.0","9.4.0-jdk17","9.3.1-ubuntu","9.3.1-jdk11","9.3.1","9.2.3-ubuntu","9.2.3-jdk11","9.2.3","9.0.3-ubuntu","9.0.3-jdk11","9.0.3","8.2.5-ubuntu","8.2.5-jdk11","8.2.5","8.1.10-ubuntu","8.1.10-jdk11","8.1.10","7.2.9-ubuntu","7.2.9","7.2.9-jdk8","7.2.6-ubuntu","7.2.6-jdk8","7.2.6","7.2.4-ubuntu","7.2.4-jdk8","7.2.4","7.2.2-ubuntu","7.2.2-jdk8","7.2.2","7.2.1-ubuntu","7.2.1-jdk8","7.2.1","7.1-ubuntu","7.1-jdk8","7.1.4-ubuntu","7.1.4-jdk8","7.1.4","7.1.2-ubuntu","7.1.2-jdk8","7.1.2","7.1.1-ubuntu","7.1.1-jdk8","7.1.1","7.1","9.4.1-jdk17","eap-jdk8","9.0.0-rc1-ubuntu","9.0.0-rc1-jdk11","9.0.0-rc1","eap-jdk11","eap-ubuntu","9.0.0-rc1-jdk8","eap","8.0.0-rc4","8.0.0-rc1","7.0.6","7.0","7.1.0-rc1","7.0.4","6.10","6.10.6","7.0.3","6.10.5","7.0.2","7.0.1","7.0.0-rc2","7.0.0-rc1","6.10.4","6.10.3","6.10.2","6.9.2","6.9","6.8.3","6.8","6.9.1","6.8.2","6.9.0","6.9.0-rc1","6.7","6.7.3","6.8.1","6.8.0","6.7.2","6.7.1","6.6.3","6.6","6.7.0-rc2","6.6.2","6.6.1","6.6.0"],"bitbucket":["8.2.1-ubuntu-jdk11","8.2.1-jdk11","8.2.1","8.16.2-ubuntu-jdk17","8.16.2-jdk17","8.16.2","8.1.1-ubuntu-jdk11","8.1.1-jdk11","8.1.1","8.8-ubuntu-jdk17","8.8-jdk17","8.8.7-ubuntu-jdk17","8.8.7-jdk17","8.8.7","8.8","8.3.2-ubuntu-jdk11","7.21.5-ubuntu-jdk11","8.3.2-jdk11","7.21.5-jdk11","8.3.2","8.2.2-ubuntu-jdk11","7.21.5","8.2.2-jdk11","8.2.2","8.1.2-ubuntu-jdk11","8.1.2-jdk11","8.1.2","7.21.2-ubuntu-jdk11","7.21.2-jdk11","7.21.2","8.3.3-ubuntu-jdk11","8.3.3-jdk11","8.3.3","8.16-ubuntu-jdk17","8.16-jdk17","8.16.3-ubuntu-jdk17","8.16.3-jdk17","8.16.3","8.16","8.14.2-ubuntu-jdk17","8.15.1-ubuntu-jdk17","8.15.1-jdk17","8.14.2-jdk17","8.14.3-ubuntu-jdk17","8.15.1","8.14.2","8.14.3-jdk17","8.9.4-ubuntu-jdk17","8.9.4-jdk17","8.14.4-ubuntu-jdk17","8.14.3","8.9.4","8.14.4-jdk17","8.14.5-ubuntu-jdk17","8.14.4","8.8.1-ubuntu-jdk17","8.8.1-jdk17","8.14-ubuntu-jdk17","8.8.1","8.14-jdk17","8.14.5-jdk17","8.11.3-ubuntu-jdk17","8.11.3-jdk17","8.15.0-ubuntu-jdk17","8.14.5","8.15.0-jdk17","8.11.3","8.14","8.0.1-ubuntu-jdk11","8.15.0","8.0.1-jdk11","8.0.1","8.7.0-ubuntu-jdk11","8.7.0-jdk11","8.7.0","8.10.5-ubuntu-jdk17","8.10.5-jdk17","8.10.5","7.21.14-ubuntu-jdk11","7.21.14-jdk11","8.9.0-ubuntu-jdk17","7.21.14","8.9.0-jdk17","8.9.0","8.5-ubuntu-jdk11","8.5-jdk11","8.5.4-ubuntu-jdk11","8.5.4-jdk11","8.5.4","8.5","8.4.3-ubuntu-jdk11","8.4.3-jdk11","8.4.3","7.21.6-ubuntu-jdk11","7.21.6-jdk11","7.21.6","7.21.20-ubuntu-jdk11","7.21.20-jdk11","7.21.20","7.21.1-ubuntu-jdk11","7.21.1-jdk11","7.21.1","8.3-ubuntu-jdk11","8.3-jdk11","8.3.4-ubuntu-jdk11","8.3.4-jdk11","8.3.4","8.3","8.2.3-ubuntu-jdk11","8.2.3-jdk11","8.2.3","8.15.2-ubuntu-jdk17","8.15.2-jdk17","8.15.2","8.1.3-ubuntu-jdk11","8.1.3-jdk11","8.1.3","8.9.5-ubuntu-jdk17","8.9.5-jdk17","8.10.0-ubuntu-jdk17","8.10.0-jdk17","8.10.0","8.0.2-ubuntu-jdk11","8.0.2-jdk11","8.0.2","7.20.0-ubuntu-jdk11","7.20.0-jdk11","7.20.0","8.9.5","8.8.2-ubuntu-jdk17","8.8.2-jdk17","8.8.2","8.17.0-ubuntu-jdk17","8.17.0-jdk17","8.17.0","8.11.4-ubuntu-jdk17","8.11.4-jdk17","8.11.4","8.10-ubuntu-jdk17","8.10-jdk17","8.10.6-ubuntu-jdk17","8.10.6-jdk17","8.10.6","8.10","8.9.1-ubuntu-jdk17","8.9.1-jdk17","8.7.1-ubuntu-jdk11","8.9.1","8.7.1-jdk11","8.7.1","8.4-ubuntu-jdk11","8.4-jdk11","8.4.4-ubuntu-jdk11","8.4.4-jdk11","8.4.4","8.4","8.12.1-ubuntu-jdk17","8.12.1-jdk17","8.12.1","7.21.7-ubuntu-jdk11","7.21.7-jdk11","7.21.7","7.21.21-ubuntu-jdk11","7.21.21-jdk11","7.21.21","7.21.15-ubuntu-jdk11","7.21.15-jdk11","7.21.15","7.21.10-jdk11","7.21.10-ubuntu-jdk11","7.21.10","8.2-ubuntu-jdk11","8.2-jdk11","8.2.4-ubuntu-jdk11","8.2.4-jdk11","8.2.4","8.2","8.15.3-ubuntu-jdk17","8.15.3-jdk17","8.15.3","8.12-ubuntu-jdk17","8.12-jdk17","8.12.6-ubuntu-jdk17","8.12.6-jdk17","8.12.6","8.12","8.6.0-ubuntu-jdk11","8.6.0","8.6.0-jdk11","8.1.4-ubuntu-jdk11","8.1.4-jdk11","8.1.4","8.13.4-ubuntu-jdk17","8.13.4-jdk17","8.13.4","8.0.3-ubuntu-jdk11","8.0.3-jdk11","8.0.3","7.20.1-ubuntu-jdk11","8.9.6-ubuntu-jdk17","7.20.1-jdk11","8.9.6-jdk17","7.20.1","8.9.6","8.17-ubuntu-jdk17","8.17-jdk17","8.17.1-ubuntu-jdk17","8.17.1-jdk17","8.17.1","8.17","8.11.5-ubuntu-jdk17","8.11.5-jdk17","8.11.5","8.10.1-ubuntu-jdk17","8.10.1-jdk17","8.10.1","8.8.3-ubuntu-jdk17","8.8.3-jdk17","8.8.3","8.7.2-ubuntu-jdk11","8.7.2-jdk11","8.7.2","7-ubuntu-jdk11","7-jdk11","7.21-ubuntu-jdk11","7.21-jdk11","7.21.8-ubuntu-jdk11","7.21.8-jdk11","7.21.8","7.21.22-ubuntu-jdk11","7.21.22-jdk11","7.21.22","7.21","7","8.9-ubuntu-jdk17","8.9-jdk17","8.9.10-ubuntu-jdk17","8.9.10-jdk17","8.9.10","8.9","8.12.2-ubuntu-jdk17","8.12.2-jdk17","8.12.2","7.21.16-ubuntu-jdk11","7.21.16-jdk11","7.21.16","8.6.1-ubuntu-jdk11","8.6.1-jdk11","8.6.1","8.4.0-ubuntu-jdk11","8.4.0-jdk11","8.4.0","8.15-ubuntu-jdk17","8.15-jdk17","8.15.4-ubuntu-jdk17","8.15.4-jdk17","8.15.4","8.15","8.13.5-ubuntu-jdk17","8.13.5-jdk17","8.13.5","8.0.4-ubuntu-jdk11","8.0.4-jdk11","8.0.4","7.21.11-ubuntu-jdk11","7.21.11-jdk11","7.21.11","7.20.2-ubuntu-jdk11","7.20.2-jdk11","7.20.2","8.9.7-ubuntu-jdk17","8.9.7-jdk17","8.9.7","8.5.0-ubuntu-jdk11","8.5.0-jdk11","8.5.0","8.1-ubuntu-jdk11","8.1-jdk11","8.1.5-ubuntu-jdk11","8.1.5-jdk11","8.1.5","8.11.0-ubuntu-jdk17","8.11.0-jdk17","8.11.0","8.1","8.8.4-ubuntu-jdk17","8.8.4-jdk17","8.8.4","8.7.3-ubuntu-jdk11","8.13.0-ubuntu-jdk17","8.13.0-jdk17","8.13.0","8.11-ubuntu-jdk17","8.11-jdk17","8.11.6-ubuntu-jdk17","8.11.6-jdk17","8.11.6","8.11","8.10.2-ubuntu-jdk17","8.10.2-jdk17","latest","8.10.2","jdk17","8-ubuntu-jdk17","8-jdk17","8.7.3-jdk11","8.7.3","8.18.0-ubuntu-jdk17","8.12.3-ubuntu-jdk17","8.12.3-jdk17","8.12.3","8","7.21.9-ubuntu-jdk11","7.21.9-jdk11","7.21.9","ubuntu-jdk17","7.21.17-ubuntu-jdk11","8.6.2-ubuntu-jdk11","8.6.2-jdk11","8.6.2","8.4.1-ubuntu-jdk11","8.4.1-jdk11","8.4.1","8.3.0-ubuntu-jdk11","8.3.0-jdk11","8.3.0","8.18-ubuntu-jdk17","8.18-jdk17","8.18.0-jdk17","8.18.0","8.18","8.13-ubuntu-jdk17","8.13-jdk17","8.13.6-ubuntu-jdk17","8.13.6-jdk17","8.13.6","8.13","8.0-ubuntu-jdk11","8.0-jdk11","8.0.5-ubuntu-jdk11","8.0.5-jdk11","8.0.5","8.0","7.21.17-jdk11","7.21.17","7.20-ubuntu-jdk11","7.20-jdk11","7.20.3-ubuntu-jdk11","7.20.3-jdk11","7.20.3","7.20","8.9.8-ubuntu-jdk17","8.9.8-jdk17","8.9.8","8.5.1-ubuntu-jdk11","8.5.1-jdk11","8.5.1","8.11.1-ubuntu-jdk17","8.11.1-jdk17","8.11.1","7.21.3-ubuntu-jdk11","7.21.3-jdk11","7.21.3","7.21.12-ubuntu-jdk11","7.21.12-jdk11","7.21.12","8.9.2-ubuntu-jdk17","8.9.2-jdk17","8.9.2","8.8.5-ubuntu-jdk17","8.8.5-jdk17","8.8.5","8.7.4-ubuntu-jdk11","8.7.4-jdk11","8.7.4","8.13.1-ubuntu-jdk17","8.13.1-jdk17","8.13.1","8.10.3-ubuntu-jdk17","8.6.3-ubuntu-jdk11","8.10.3-jdk17","8.6.3-jdk11","8.10.3","8.6.3","8.16.0-ubuntu-jdk17","8.16.0-jdk17","8.16.0","8.12.4-ubuntu-jdk17","8.12.4-jdk17","8.12.4","8.0.0","7.21.18-ubuntu-jdk11","7.21.18","8.2.0-ubuntu-jdk11","8.2.0-jdk11","8.2.0","8.12.0-ubuntu-jdk17","8.12.0-jdk17","8.12.0","8.0.0-ubuntu-jdk11","8.0.0-jdk11","7.21.4-ubuntu-jdk11","7.21.4-jdk11","7.21.4","7.21.18-jdk11","8.9.9","8.5.2-ubuntu-jdk11","8.5.2-jdk11","8.5.2","8.4.2-ubuntu-jdk11","8.4.2-jdk11","8.4.2","8.3.1-ubuntu-jdk11","8.3.1-jdk11","8.3.1","8.11.2-ubuntu-jdk17","8.11.2-jdk17","8.11.2","8.9.9-ubuntu-jdk17","8.9.9-jdk17","8.14.0-ubuntu-jdk17","8.14.0-jdk17","8.14.0","8.13.2-ubuntu-jdk17","8.13.2-jdk17","8.13.2","7.21.13-ubuntu-jdk11","7.21.13-jdk11","7.21.13","8.7-ubuntu-jdk11","8.7-jdk11","8.7.5-ubuntu-jdk11","8.7.5-jdk11","8.7.5","8.7","8.6-ubuntu-jdk11","8.6-jdk11","8.6.4-ubuntu-jdk11","8.6.4-jdk11","8.6.4","8.6","7.21.0-ubuntu-jdk11","7.21.0-jdk11","7.21.0","8.9.3-ubuntu-jdk17","8.9.3-jdk17","8.9.3","8.8.6-ubuntu-jdk17","8.8.6-jdk17","8.8.6","8.8.0-ubuntu-jdk17","8.8.0-jdk17","8.8.0","8.16.1-ubuntu-jdk17","8.16.1-jdk17","8.16.1","8.12.5-ubuntu-jdk17","8.12.5-jdk17","8.12.5","8.1.0-ubuntu-jdk11","8.1.0-jdk11","8.10.4-ubuntu-jdk17","8.10.4-jdk17","8.10.4","8.1.0","7.21.19-ubuntu-jdk11","7.21.19-jdk11","7.21.19","8.5.3-ubuntu-jdk11","8.5.3-jdk11","8.5.3","8.14.1-ubuntu-jdk17","8.14.1-jdk17","8.14.1","8.13.3-ubuntu-jdk17","8.13.3-jdk17","8.13.3","8.15-ubuntu-jdk11","8.15-jdk11","8.15.3-ubuntu-jdk11","8.15.3-jdk11","8.8.0-ubuntu-jdk11","8.8.0-jdk11","8.16.0-ubuntu-jdk11","8.16.0-jdk11","8.8.1-jdk11","8.12.3-ubuntu-jdk11","8.12.3-jdk11","8.8.1-ubuntu-jdk11","8.16.1-ubuntu-jdk11","8.10.0-ubuntu-jdk11","8.10.0-jdk11","8.16.1-jdk11","7.6.9-ubuntu-jdk11","7.6.9-jdk11","8.12.4-ubuntu-jdk11","8.12.4-jdk11","7.6.9","8.8.2-ubuntu-jdk11","8.8.2-jdk11","7.19-ubuntu-jdk11","7.19-jdk11","7.19.5-ubuntu-jdk11","7.19.5-jdk11","7.19.5","7.19","7.7.0-ubuntu-jdk11","8.16-ubuntu-jdk11","8.16-jdk11","8.16.2-ubuntu-jdk11","7.7.0","8.16.2-jdk11","8.10.1-ubuntu-jdk11","8.10.1-jdk11","7.7.0-jdk11","8.12.5-ubuntu-jdk11","8.8.3-ubuntu-jdk11","8.12.5-jdk11","8.8.3-jdk11","7.7-ubuntu-jdk11","7.7-jdk11","7.7.1-ubuntu-jdk11","7.7.1-jdk11","7.7.1","7.7","7.10.0-ubuntu-jdk11","7.10.0-jdk11","7.10.0","8.10.2-ubuntu-jdk11","8.10.2-jdk11","ubuntu-jdk11","jdk11","8-jdk11","8-ubuntu-jdk11","8.17-ubuntu-jdk11","8.17-jdk11","8.17.0-ubuntu-jdk11","8.17.0-jdk11","8.12-ubuntu-jdk11","8.12-jdk11","8.12.6-ubuntu-jdk11","8.12.6-jdk11","7.16.2-ubuntu-jdk11","7.16.2-jdk11","7.16.2","8.8.4-ubuntu-jdk11","8.8.4-jdk11","7.10-ubuntu-jdk11","7.10-jdk11","7.10.1-ubuntu-jdk11","7.10.1-jdk11","7.10.1","7.10","7.17.3-ubuntu-jdk11","7.17.3-jdk11","8.10.3-ubuntu-jdk11","7.17.3","8.10.3-jdk11","7.8.0-ubuntu-jdk11","7.8.0-jdk11","7.8.0","7.16-jdk11","7.16-ubuntu-jdk11","7.16.3-jdk11","7.16.3-ubuntu-jdk11","7.16.3","7.16","8.13.0-ubuntu-jdk11","8.13.0-jdk11","8.8.5-ubuntu-jdk11","8.8.5-jdk11","7.11.1","7.6.13-ubuntu-jdk11","7.6.13-jdk11","7.17.4-ubuntu-jdk11","7.6.13","7.17.4-jdk11","7.17.4","7.11.1-ubuntu-jdk11","7.11.1-jdk11","7.8-ubuntu-jdk11","8.10.4-ubuntu-jdk11","7.8-jdk11","7.8.1-jdk11","7.8.1-ubuntu-jdk11","7.8.1","7.8","8.10.4-jdk11","7.17.0-ubuntu-jdk11","7.17.0-jdk11","7.17.0","8.13.1-ubuntu-jdk11","8.13.1-jdk11","7.6.14-ubuntu-jdk11","7.6.14-jdk11","7.6.14","7.11-ubuntu-jdk11","7.11-jdk11","7.11.2-ubuntu-jdk11","7.11.2-jdk11","8.8.6-ubuntu-jdk11","8.8.6-jdk11","7.11.2","7.11","7.17.5-ubuntu-jdk11","7.17.5-jdk11","7.17.5","7.9.0-ubuntu-jdk11","7.9.0-jdk11","7.9.0","8.10.5-ubuntu-jdk11","8.10.5-jdk11","7.6.15-ubuntu-jdk11","7.6.15-jdk11","7.6.15","7.17.1-ubuntu-jdk11","7.17.1-jdk11","7.17.1","8.13.2-ubuntu-jdk11","8.13.2-jdk11","7.17.6-ubuntu-jdk11","7.17.6-jdk11","7.17.6","7.12.0-jdk11","7.12.0","8.8-ubuntu-jdk11","8.8-jdk11","7.12.0-ubuntu-jdk11","8.8.7-ubuntu-jdk11","8.8.7-jdk11","7.9-ubuntu-jdk11","7.9-jdk11","7.9.1-ubuntu-jdk11","7.9.1-jdk11","7.9.1","7.9","8.10-ubuntu-jdk11","8.10-jdk11","8.10.6-ubuntu-jdk11","8.10.6-jdk11","7.6.16-ubuntu-jdk11","7.6.16-jdk11","7.6.16","7.17.10-ubuntu-jdk11","7.17.10-jdk11","7.17.10","7.17.7-ubuntu-jdk11","7.17.7-jdk11","7.17.7","7.12-ubuntu-jdk11","7.12-jdk11","7.12.1-ubuntu-jdk11","7.12.1-jdk11","7.12","8.13.3-ubuntu-jdk11","8.13.3-jdk11","7.12.1","7.6.17-ubuntu-jdk11","7.6.17-jdk11","7.6.17","7.17.11-ubuntu-jdk11","7.17.11-jdk11","7.17.11","7.17.8-ubuntu-jdk11","7.17.8-jdk11","7.17.8","8.9.0-ubuntu-jdk11","8.9.0-jdk11","8.13.4-ubuntu-jdk11","8.13.4-jdk11","8.11.0-ubuntu-jdk11","8.11.0-jdk11","7.6.18-ubuntu-jdk11","7.6.18-jdk11","7.6.18","7.13.0-ubuntu-jdk11","7.13.0-jdk11","7.13.0","7.17.12-ubuntu-jdk11","7.17.12-jdk11","7.17.12","7.17.9-ubuntu-jdk11","7.17.9-jdk11","7.17.9","8.13-ubuntu-jdk11","8.13-jdk11","8.13.5-ubuntu-jdk11","8.13.5-jdk11","7.6.19-ubuntu-jdk11","7.6.19-jdk11","7.6.19","7.13-ubuntu-jdk11","7.13-jdk11","7.13.1-ubuntu-jdk11","7.13.1-jdk11","7.13.1","7.13","8.9.1-ubuntu-jdk11","8.9.1-jdk11","8.11.1-ubuntu-jdk11","8.11.1-jdk11","7.17.13-ubuntu-jdk11","7.17.13-jdk11","7.17.13","7.18.0-ubuntu-jdk11","7.18.0-jdk11","7.18.0","7.6.2-ubuntu-jdk11","7.6.2-jdk11","8.9.2-ubuntu-jdk11","7.6.2","8.9.2-jdk11","8.14.0-ubuntu-jdk11","8.14.0-jdk11","8.11.2-ubuntu-jdk11","8.11.2-jdk11","7.17.14-ubuntu-jdk11","7.17.14-jdk11","7.17.14","7.14.0-ubuntu-jdk11","7.14.0-jdk11","7.14.0","7.6.20-ubuntu-jdk11","7.6.20-jdk11","7.6.20","7.18.1-ubuntu-jdk11","7.18.1-jdk11","7.18.1","7.14.1-ubuntu-jdk11","7.14.1","7.18.2-ubuntu-jdk11","7.17.15-ubuntu-jdk11","7.17.15-jdk11","7.17.15","7.14.1-jdk11","8.9.3-ubuntu-jdk11","8.9.3-jdk11","8.14.1-ubuntu-jdk11","8.14.1-jdk11","8.11.3-ubuntu-jdk11","8.11.3-jdk11","7.6.21-ubuntu-jdk11","7.6.21-jdk11","7.6.21","7.18.2-jdk11","7.18.2","7.14-ubuntu-jdk11","7.14.2-ubuntu-jdk11","7.14.2-jdk11","7.6.22-ubuntu-jdk11","7.6.22-jdk11","7.6.22","7.5.0-ubuntu-jdk11","7.5.0-jdk11","7.5.0","7.18.3","7.17.16-ubuntu-jdk11","7.17.16-jdk11","7.17.16","7.14-jdk11","7.14.2","7.14","8.14.2-ubuntu-jdk11","8.14.2-jdk11","7.18.3-ubuntu-jdk11","7.18.3-jdk11","8.9.4-ubuntu-jdk11","8.9.4-jdk11","8.11.4-ubuntu-jdk11","8.11.4-jdk11","7.6-ubuntu-jdk11","7.6-jdk11","7.6.23-ubuntu-jdk11","7.6.23-jdk11","7.6.23","7.6","7.5.1-ubuntu-jdk11","7.5.1-jdk11","7.5.1","7.17.17-ubuntu-jdk11","7.17.17-jdk11","7.17.17","7.18-ubuntu-jdk11","7.18-jdk11","7.18.4-ubuntu-jdk11","7.18.4-jdk11","7.18.4","7.18","8.14.3-ubuntu-jdk11","7.15.0-jdk11","8.14.3-jdk11","8.11.5-ubuntu-jdk11","8.11.5-jdk11","7.15.0-ubuntu-jdk11","7.15.0","8.9.5-ubuntu-jdk11","8.9.5-jdk11","7.5-ubuntu-jdk11","7.5-jdk11","7.5.2-ubuntu-jdk11","7.5.2-jdk11","7.5.2","7.5","7.17.18-ubuntu-jdk11","7.17.18-jdk11","7.17.18","7.15.1-ubuntu-jdk11","7.15.1-jdk11","7.15.1","8.14-jdk11","8.14.4-jdk11","8.9.6-ubuntu-jdk11","7.6.3-ubuntu-jdk11","7.6.3-jdk11","8.9.6-jdk11","7.6.3","8.14-ubuntu-jdk11","8.14.4-ubuntu-jdk11","8.11-ubuntu-jdk11","8.11-jdk11","8.11.6-ubuntu-jdk11","8.11.6-jdk11","7.19.0-ubuntu-jdk11","7.19.0-jdk11","7.19.0","7.17.19-ubuntu-jdk11","7.17.19-jdk11","7.17.19","7.15.2-ubuntu-jdk11","7.15.2-jdk11","7.15.2","7.6.4-ubuntu-jdk11","7.6.4-jdk11","7.6.4","7.6.0-ubuntu-jdk11","7.6.0-jdk11","7.6.0","8.9.7-ubuntu-jdk11","8.9.7-jdk11","7.19.1-ubuntu-jdk11","7.19.1-jdk11","7.19.1","7.17.2-ubuntu-jdk11","7.17.2","7.17.2-jdk11","7.15-ubuntu-jdk11","7.15-jdk11","7.15.3-ubuntu-jdk11","7.15.3-jdk11","7.15.3","7.15","8.15.0-ubuntu-jdk11","8.15.0-jdk11","8.12.0-ubuntu-jdk11","8.12.0-jdk11","7.6.5-ubuntu-jdk11","7.6.5-jdk11","7.6.5","7.6.1-ubuntu-jdk11","7.6.1-jdk11","7.6.1","8.9.8-ubuntu-jdk11","8.9.8-jdk11","7.19.2-ubuntu-jdk11","7.19.2-jdk11","7.19.2","7.17.20-ubuntu-jdk11","7.17.20-jdk11","7.17.20","7.6.6-ubuntu-jdk11","7.6.6-jdk11","7.6.6","7.6.10-ubuntu-jdk11","7.6.10-jdk11","7.6.10","8.15.1-ubuntu-jdk11","8.15.1-jdk11","8.12.1-ubuntu-jdk11","8.12.1-jdk11","7.19.3-ubuntu-jdk11","7.19.3-jdk11","7.19.3","7.16.0-ubuntu-jdk11","7.16.0-jdk11","8.9-ubuntu-jdk11","7.16.0","8.9-jdk11","8.9.9-ubuntu-jdk11","8.9.9-jdk11","7.6.7-ubuntu-jdk11","7.6.7-jdk11","7.6.7","7.6.11-ubuntu-jdk11","7.6.11-jdk11","7.6.11","7.17-ubuntu-jdk11","7.17-jdk11","7.17.21-ubuntu-jdk11","7.17.21-jdk11","7.17.21","7.17","8.15.2-ubuntu-jdk11","8.15.2-jdk11","8.12.2-ubuntu-jdk11","8.12.2-jdk11","7.19.4-ubuntu-jdk11","7.19.4-jdk11","7.19.4","7.16.1-ubuntu-jdk11","7.16.1-jdk11","7.16.1","7.6.8-ubuntu-jdk11","7.6.8-jdk11","7.6.8","7.6.12-ubuntu-jdk11","7.6.12-jdk11","7.6.12","8.14.0-eap01","8.14.0-eap01-ubuntu-jdk11","8.14.0-eap01-jdk11","7.4-ubuntu-jdk11","7.4-jdk11","7.4.1-ubuntu-jdk11","7.4.1-jdk11","7.4.1","7.4.0-ubuntu-jdk11","7.4.0-jdk11","7.4.0","7.3-ubuntu-jdk11","7.3-jdk11","7.3.2-ubuntu-jdk11","7.3.2-jdk11","7.3.2","7.3","7.4.2-ubuntu-jdk11","7.4.2-jdk11","7.4.2","7.4","7.3.1-ubuntu-jdk11","7.3.1-jdk11","7.3.1","7.3.0-ubuntu-jdk11","7.3.0-jdk11","7.3.0","7.2.5-ubuntu-jdk11","7.2.5-jdk11","7.2.5","7.2.4-ubuntu-jdk11","7.2.4-jdk11","7.2.4","7.2.3-ubuntu-jdk11","7.2.3-jdk11","7.2.3","7.2.2-ubuntu-jdk11","7.2.2-jdk11","7.2.2","7.2.1-ubuntu-jdk11","7.2.1-jdk11","7.2.1","7.2.0-ubuntu-jdk11","7.2.0-jdk11","7.2.0","7.1-ubuntu-jdk11","7.1-jdk11","7.1.4-ubuntu-jdk11","7.1.4-jdk11","7.1.4","7.1.3-ubuntu-jdk11","7.1.3-jdk11","7.1.3","7.1.2-ubuntu-jdk11","7.1.2-jdk11","7.1.2","7.1.1-ubuntu-jdk11","7.1.1-jdk11","7.1.1","7.1.0-ubuntu-jdk11","7.1.0-jdk11","7.1.0","7.1","7.2-ubuntu-jdk11","7.2-jdk11","7.2.6-ubuntu-jdk11","7.2.6-jdk11","7.2.6","7.2","eap-ubuntu-jdk11","eap-jdk11","eap","8.0.0-eap05-ubuntu-jdk11","8.0.0-eap05-jdk11","8.0.0-eap05","7.0-jdk11","7.0-ubuntu-jdk11","7.0.5-ubuntu-jdk11","7.0.5-jdk11","7.0.5","7.0","6.10.17-ubuntu-jdk11","6.10.17","6.10.17-jdk11","6.10","6","6-ubuntu-jdk11","6-jdk11","6.10-ubuntu-jdk11","6.10-jdk11","6.8.2-jdk11","6.10.11-jdk11","6.8.2","6.10.11","6.8.2-ubuntu-jdk11","6.10.11-ubuntu-jdk11","7.0.0-jdk11","7.0.0-ubuntu-jdk11","7.0.0","6.8.3","6.7.1","6.7.1-ubuntu-jdk11","6.10.12-jdk11","6.8.3-ubuntu-jdk11","6.7.1-jdk11","6.10.12","6.8.3-jdk11","6.10.12-ubuntu-jdk11","7.0.1-ubuntu-jdk11","7.0.1-jdk11","7.0.1","6.7.2-jdk11","6.10.3","6.8.4-jdk11","6.7.2","6.10.3-jdk11","6.8","6.10.13-jdk11","6.7.2-ubuntu-jdk11","6.10.3-ubuntu-jdk11","6.8-ubuntu-jdk11","6.10.13-ubuntu-jdk11","6.8-jdk11","6.10.13","6.8.4-ubuntu-jdk11","7.0.2-ubuntu-jdk11","6.8.4","7.0.2","6.7.3-ubuntu-jdk11","7.0.2-jdk11","6.10.4-jdk11","6.7.3","6.10.4-ubuntu-jdk11","6.7.3-jdk11","6.10.14-ubuntu-jdk11","6.10.4","6.10.14","6.10.14-jdk11","7.0.3-jdk11","6.9.0-ubuntu-jdk11","7.0.3","6.7.4-ubuntu-jdk11","6.10.5-jdk11","6.9.0","7.0.3-ubuntu-jdk11","6.7.4","6.10.5-ubuntu-jdk11","6.9.0-jdk11","6.7.4-jdk11","6.10.15-jdk11","6.10.5","6.10.15","6.10.15-ubuntu-jdk11","7.0.4-jdk11","6.9.1-jdk11","6.7-jdk11","7.0.4-ubuntu-jdk11","6.10.7-jdk11","6.9.1","6.7","7.0.4","6.10.7","6.9.1-ubuntu-jdk11","6.7-ubuntu-jdk11","6.10.7-ubuntu-jdk11","6.7.5","6.10.16-jdk11","6.7.5-jdk11","6.7.5-ubuntu-jdk11","6.10.16","6.9.2-ubuntu-jdk11","6.10.16-ubuntu-jdk11","6.10.8","6.9.2-jdk11","6.10.8-ubuntu-jdk11","6.9.2","6.10.8-jdk11","6.9.3-ubuntu-jdk11","6.10.9","6.8.0-jdk11","6.9-jdk11","6.10.9-jdk11","6.8.0-ubuntu-jdk11","6.9.3","6.10.9-ubuntu-jdk11","6.8.0","6.9.3-jdk11","6.9","6.9-ubuntu-jdk11","6.10.2","6.7.0-jdk11","6.8.1-jdk11","6.10.2-jdk11","6.7.0-ubuntu-jdk11","6.8.1","6.10.2-ubuntu-jdk11","6.7.0","6.8.1-ubuntu-jdk11","6.10.0-jdk11","6.10.0-ubuntu-jdk11","6.10.0","6.10.1-jdk11","6.10.1-ubuntu-jdk11","6.10.1","6.10.10-jdk11","6.10.10","6.10.10-ubuntu-jdk11","7.14-ubuntu","7.14-jdk8","7.14.1-ubuntu","7.14-ubuntu-jdk8","7.14.1-jdk8","7.14.1-ubuntu-jdk8","7.11.2-jdk8","7.11-ubuntu","7.11.2-ubuntu","7.11-ubuntu-jdk8","7.11-jdk8","7.11.2-ubuntu-jdk8","7.15.0-jdk8","7.1.2-ubuntu","7.15.0-ubuntu","7.1.2-jdk8","7.15.0-ubuntu-jdk8","7.1.2-ubuntu-jdk8","7.6.4-ubuntu","6.7-jdk8","7.2.2-jdk8","7.6.4-jdk8","7.12.0-jdk8","6.7.5-ubuntu","7.5.1-ubuntu","7.6.4-ubuntu-jdk8","6.7.5-ubuntu-jdk8","7.2.2-ubuntu-jdk8","6.9.0-ubuntu","7.15-ubuntu-jdk8","7.12.0-ubuntu","7.5.1-jdk8","7.1.3-jdk8","7.2.2-ubuntu","6.9.0-jdk8","6.7-ubuntu","7.15.1-ubuntu","7.12.0-ubuntu-jdk8","7.5.1-ubuntu-jdk8","6.9.0-ubuntu-jdk8","6.7.5-jdk8","7.15-ubuntu","7.0.2-ubuntu-jdk8","7.1.3-ubuntu-jdk8","6.7-ubuntu-jdk8","7.15-jdk8","7.0.2-jdk8","7.1.3-ubuntu","7.0.2-ubuntu","7.15.1-jdk8","7.15.1-ubuntu-jdk8","7.2.3-jdk8","7.12.1-ubuntu","7.5.2-ubuntu-jdk8","7.6.5-jdk8","7.2.3-ubuntu","6.9.1-ubuntu","7.5-ubuntu-jdk8","7.6.5-ubuntu-jdk8","7.12.1-jdk8","6.9.1-ubuntu-jdk8","7.1.4-jdk8","7.5-jdk8","7.2.3-ubuntu-jdk8","7.12-jdk8","7.1.4-ubuntu","7.5.2-ubuntu","7.6.5-ubuntu","6.9.1-jdk8","7.0.3-ubuntu-jdk8","7.12-ubuntu-jdk8","6.10.2-jdk8","6.8.0-jdk8","7.12-ubuntu","7.1-ubuntu","7.5.2-jdk8","6.8.0-ubuntu-jdk8","6.10.9-ubuntu-jdk8","7.0.3-jdk8","6.10.2-ubuntu","7.12.1-ubuntu-jdk8","7.1-ubuntu-jdk8","7.5-ubuntu","6.10.9-jdk8","7.0.3-ubuntu","6.10.2-ubuntu-jdk8","7.1.4-ubuntu-jdk8","6.8.0-ubuntu","7.2.4-ubuntu","7.16.0-ubuntu","6.10.9-ubuntu","7.6.6-ubuntu","7.16-jdk8","6.9.2-jdk8","7.1-jdk8","7.16.0-jdk8","7.2.4-ubuntu-jdk8","7.6.6-ubuntu-jdk8","6.9.2-ubuntu-jdk8","7.2.4-jdk8","7.0.4-jdk8","7.6.6-jdk8","6.10.3-ubuntu","6.9.2-ubuntu","7.16.0-ubuntu-jdk8","6.8.1-ubuntu","6.10.3-ubuntu-jdk8","6.7.0-jdk8","7.0.4-ubuntu","6.8.1-ubuntu-jdk8","6.7.0-ubuntu-jdk8","7-ubuntu-jdk8","7.0.4-ubuntu-jdk8","6.10.3-jdk8","6.8.1-jdk8","7-jdk8","7.13.0-jdk8","6.7.0-ubuntu","ubuntu","7.2.5-jdk8","7.6.0-jdk8","7.13.0-ubuntu-jdk8","7.6.7-jdk8","6.9-ubuntu","7.6.0-ubuntu","7.2.5-ubuntu-jdk8","6.10.11-jdk8","7.13.0-ubuntu","7.10.0-ubuntu-jdk8","6.9-jdk8","jdk8","7.10.0-jdk8","6.9.3-ubuntu-jdk8","7.6.7-ubuntu","7.0.5-ubuntu","6.10.4-ubuntu-jdk8","6.8.2-ubuntu-jdk8","ubuntu-jdk8","7.2.5-ubuntu","7.6.0-ubuntu-jdk8","7.10.0-ubuntu","6.9.3-ubuntu","7.6.7-ubuntu-jdk8","6.10.4-ubuntu","6.8.2-ubuntu","7.0-ubuntu","7.16-ubuntu","6.10.4-jdk8","7.0-ubuntu-jdk8","6.7.1-ubuntu-jdk8","6.8.2-jdk8","7.0-jdk8","6.7.1-jdk8","7-ubuntu","6.9-ubuntu-jdk8","6.7.1-ubuntu","6.10.12-jdk8","7.16-ubuntu-jdk8","6.9.3-jdk8","7.13.1-ubuntu-jdk8","6.10.12-ubuntu","7.2.6-jdk8","7.6.1-jdk8","7.10-ubuntu-jdk8","7.6.8-jdk8","7.0.5-jdk8","6.10.12-ubuntu-jdk8","7.2-ubuntu-jdk8","7.10-ubuntu","7.6.8-ubuntu","7.13.1-ubuntu","7.0.5-ubuntu-jdk8","7.2-ubuntu","7.6.1-ubuntu","7.10.1-ubuntu-jdk8","6.8.3-jdk8","7.13-jdk8","6.10.5-ubuntu","7.2.6-ubuntu","7.6.1-ubuntu-jdk8","7.10.1-ubuntu","6.8.3-ubuntu-jdk8","6.7.2-ubuntu-jdk8","7.6.8-ubuntu-jdk8","7.13-ubuntu","7.2-jdk8","6.8.3-ubuntu","6.7.2-ubuntu","7.4.2-jdk8","6.10.5-jdk8","7.13-ubuntu-jdk8","7.2.6-ubuntu-jdk8","7.10-jdk8","7.4-ubuntu-jdk8","6.10.5-ubuntu-jdk8","7.13.1-jdk8","6-ubuntu","6.7.2-jdk8","7.10.1-jdk8","7.4-jdk8","7.4.2-ubuntu","7.6.2-jdk8","7.6-jdk8","7.0.0-ubuntu-jdk8","7.2.0-ubuntu","7.6.2-ubuntu-jdk8","7.0.0-ubuntu","6.10-ubuntu-jdk8","7.6.2-ubuntu","7.1.0-ubuntu","7.2.0-jdk8","6.10.7-jdk8","7.6.9-jdk8","7.0.0-jdk8","6.7.3-jdk8","6.10-ubuntu","7.1.0-ubuntu-jdk8","7.6-ubuntu-jdk8","7.2.0-ubuntu-jdk8","6.7.3-ubuntu-jdk8","6.10-jdk8","6.8-ubuntu-jdk8","7.1.0-jdk8","6.10.7-ubuntu","7.6-ubuntu","6.7.3-ubuntu","6-jdk8","6.8.4-jdk8","6.10.7-ubuntu-jdk8","6.10.13-jdk8","6.8-ubuntu","7.9.1-ubuntu-jdk8","7.6.9-ubuntu","6.10.13-ubuntu-jdk8","6.8.4-ubuntu","7.6.9-ubuntu-jdk8","6.10.13-ubuntu","6.8-jdk8","7.3.0-ubuntu-jdk8","7.6.3-jdk8","7.2.1-ubuntu","7.0.1-ubuntu-jdk8","7.5.0-ubuntu","6-ubuntu-jdk8","6.8.4-ubuntu-jdk8","7.14.0-ubuntu","7.11.1-ubuntu-jdk8","7.6.3-ubuntu-jdk8","7.3.0-ubuntu","7.0.1-jdk8","7.2.1-ubuntu-jdk8","7.5.0-jdk8","6.7.4-ubuntu","7.14.0-jdk8","7.11.1-jdk8","7.6.3-ubuntu","7.0.1-ubuntu","7.5.0-ubuntu-jdk8","7.1.1-ubuntu-jdk8","6.10.8-jdk8","7.14.0-ubuntu-jdk8","7.11.1-ubuntu","7.3.0-jdk8","7.2.1-jdk8","7.1.1-ubuntu","6.7.4-jdk8","6.10.8-ubuntu-jdk8","7.1.1-jdk8","6.7.4-ubuntu-jdk8","6.10.8-ubuntu","7.7.0-ubuntu-jdk8","7.7.0-ubuntu","7.7.0-jdk8","7.7.1-jdk8","7.7.1-ubuntu","7.7-jdk8","7.7-ubuntu","7.7-ubuntu-jdk8","6.10.0-ubuntu","7.7.1-ubuntu-jdk8","6.10.0-jdk8","6.10.0-ubuntu-jdk8","6.10.1-jdk8","6.10.1-ubuntu","7.8.0-ubuntu","6.10.1-ubuntu-jdk8","7.8.0-ubuntu-jdk8","7.8.0-jdk8","6.10.10-ubuntu","6.10.10-ubuntu-jdk8","7.8-ubuntu","6.10.10-jdk8","7.8.1-ubuntu","7.8.1-ubuntu-jdk8","7.8-ubuntu-jdk8","7.8-jdk8","6.10.11-ubuntu-jdk8","7.8.1-jdk8","6.10.11-ubuntu","7.9.0-jdk8","7.9.0-ubuntu","7.9.0-ubuntu-jdk8","7.9.1-jdk8","7.9-ubuntu-jdk8","7.9.1-ubuntu","7.9-ubuntu","7.9-jdk8","7.3.1-ubuntu-jdk8","7.3.1-ubuntu","7.3.1-jdk8","7.3.2-jdk8","7.3.2-ubuntu-jdk8","7.3-ubuntu","7.3-ubuntu-jdk8","7.3-jdk8","7.3.2-ubuntu","7.4.0-ubuntu-jdk8","7.4.0-ubuntu","7.4.0-jdk8","7.4.1-ubuntu-jdk8","7.4.1-ubuntu","7.4.1-jdk8","7.4.2-ubuntu-jdk8","7.4-ubuntu","6.5.0-ubuntu","6.5.0","6.5.0-jdk8","6.5.0-ubuntu-jdk8","6.5.1","6.6.3","6.5.1-ubuntu","6.6.3-ubuntu","6.5.1-ubuntu-jdk8","6.6.3-ubuntu-jdk8","6.5.1-jdk8","6.6.3-jdk8","6.5.2-jdk8","6.6.4-ubuntu","6.5.2-ubuntu-jdk8","6.6-ubuntu-jdk8","6.5.2-ubuntu","6.6","6.5.2","6.6.4-jdk8","6.6.4","6.6.4-ubuntu-jdk8","6.6-jdk8","6.5.1-ubuntu-jdk11","6.6.3-jdk11","6.6-ubuntu","6.5.1-jdk11","6.5-ubuntu","6.6.3-ubuntu-jdk11","6.5.3-jdk8","6.5.3-ubuntu-jdk8","6.5-ubuntu-jdk8","6.5","6.6-ubuntu-jdk11","6.5.2-jdk11","6.5.3-ubuntu","6.5.2-ubuntu-jdk11","6.6.4-jdk11","6.5.3","6.6.4-ubuntu-jdk11","6.5-jdk8","6.6-jdk11","6.5-jdk11","6.5.3-ubuntu-jdk11","6.5-ubuntu-jdk11","6.5.3-jdk11","6.6.0-ubuntu","6.6.0-jdk8","6.6.0-ubuntu-jdk8","6.6.0","6.6.0-jdk11","6.6.0-ubuntu-jdk11","6.6.1-jdk8","6.6.1","6.6.1-ubuntu","6.6.1-ubuntu-jdk8","6.6.1-jdk11","6.6.1-ubuntu-jdk11","6.6.2","6.6.2-ubuntu-jdk8","6.5.0-jdk11","6.6.2-ubuntu-jdk11","6.6.2-jdk8","6.5.0-ubuntu-jdk11","6.6.2-jdk11","6.6.2-ubuntu","6.4.0-ubuntu","6.4.0","6.4.0-ubuntu-jdk8","6.4.0-jdk8","6.0-ubuntu-jdk8","6.4.1-jdk8","6.0.11","6.4.1-ubuntu-jdk8","6.0-jdk8","6.4.1","6.0-ubuntu","6.4.1-ubuntu","6.0.11-ubuntu-jdk8","6.0","6.0.11-jdk8","6.0.11-ubuntu","6.4.2-jdk8","6.4.2-ubuntu-jdk8","6.4.2-ubuntu","6.4.2","6.0.2","6.4.3-jdk8","6.0.2-ubuntu","6.4.3-ubuntu","6.0.2-jdk8","6.4.3","6.0.2-ubuntu-jdk8","6.4.3-ubuntu-jdk8","6.0.3-ubuntu-jdk8","6.4.4-ubuntu","6.0.3","6.4.4-jdk8","6.0.3-jdk8","6.4-jdk8","6.0.3-ubuntu","6.4-ubuntu","6.4","6.4.4-ubuntu-jdk8","6.4-ubuntu-jdk8","6.4.4","6.0.4-jdk8","6.0.4","6.0.4-ubuntu","6.0.4-ubuntu-jdk8","6.0.5-jdk8","6.0.5-ubuntu-jdk8","6.0.5-ubuntu","6.0.5","6.0.6-jdk8","6.0.6-ubuntu-jdk8","6.0.6","6.0.6-ubuntu","6.0.7-ubuntu-jdk8","6.0.7","6.0.7-ubuntu","6.0.7-jdk8","6.0.9-ubuntu","6.0.9","6.0.9-jdk8","6.0.9-ubuntu-jdk8","6.1.0-ubuntu-jdk8","6.1.0-ubuntu","6.1.0","6.1.0-jdk8","6.2.6","6.2.6-ubuntu-jdk8","6.2.6-jdk8","6.2.6-ubuntu","6.2","6.2.7-ubuntu-jdk8","6.2-ubuntu","6.2.7-jdk8","6.2-ubuntu-jdk8","6.2-jdk8","6.2.7-ubuntu","6.2.7","6.3.0-jdk8","6.3.0-ubuntu-jdk8","6.3.0","6.3.0-ubuntu","6.3.1","6.3.1-jdk8","6.3.1-ubuntu-jdk8","6.3.1-ubuntu","6.3.2-ubuntu","6.3.2-jdk8","6.3.2-ubuntu-jdk8","6.3.2","6.3.3","6.3.3-jdk8","6.3.3-ubuntu-jdk8","6.3.3-ubuntu","6.3.4-ubuntu-jdk8","6.3.4-ubuntu","6.3.4","6.3.4-jdk8","6.3.5","6.3.5-ubuntu-jdk8","6.3.5-jdk8","6.3.5-ubuntu","6.3-ubuntu","6.3.6-ubuntu","6.3.6-ubuntu-jdk8","6.3.6","6.3","6.3.6-jdk8","6.3-ubuntu-jdk8","6.3-jdk8","6.1.1-ubuntu","6.1.1","6.1.1-jdk8","6.1.1-ubuntu-jdk8","6.1.2-jdk8","6.1.2","6.1.2-ubuntu","6.1.2-ubuntu-jdk8","6.1.3-ubuntu-jdk8","6.1.3","6.4.0-ubuntu-jdk11","6.2.6-jdk11","6.1.3-jdk8","6.4.0-jdk11","6.2.6-ubuntu-jdk11","6.1.3-ubuntu","6.0-jdk11","6.1.1-ubuntu-jdk11","6.4.1-jdk11","6.0.11-jdk11","6.1.4","6.2.7-ubuntu-jdk11","6.4.1-ubuntu-jdk11","6.0-ubuntu-jdk11","6.1.4-ubuntu-jdk8","6.1.1-jdk11","6.2-ubuntu-jdk11","6.0.11-ubuntu-jdk11","6.1.4-jdk8","6.2-jdk11","6.1.4-ubuntu","6.2.7-jdk11","6.4.2-ubuntu-jdk11","6.1.2-jdk11","6.4.2-jdk11","6.1.2-ubuntu-jdk11","6.2.0-ubuntu-jdk8","6.1.5-jdk8","6.2.0-jdk8","6.1.5-ubuntu","6.2.0-ubuntu","6.1.5","6.2.0","6.0.2-jdk11","6.3.0-ubuntu-jdk11","6.1.5-ubuntu-jdk8","6.4.3-jdk11","6.1.3-ubuntu-jdk11","6.0.2-ubuntu-jdk11","6.3.0-jdk11","6.1.3-jdk11","6.4.3-ubuntu-jdk11","6.2.1","6.1.6-ubuntu-jdk8","6.2.1-ubuntu-jdk8","6.0.3-ubuntu-jdk11","6.1.6-ubuntu","6.3.1-jdk11","6.2.1-ubuntu","6.1.4-ubuntu-jdk11","6.0.3-jdk11","6.1.6","6.3.1-ubuntu-jdk11","6.4.4-ubuntu-jdk11","6.2.1-jdk8","6.1.4-jdk11","6.1.6-jdk8","6.4.4-jdk11","6.4-ubuntu-jdk11","6.4-jdk11","6.0.4-ubuntu-jdk11","6.3.2-ubuntu-jdk11","6.2.0-jdk11","6.1.5-jdk11","6.2.2-ubuntu","6.0.4-jdk11","6.1.7","6.3.2-jdk11","6.2.0-ubuntu-jdk11","6.1.5-ubuntu-jdk11","6.2.2-ubuntu-jdk8","6.1.7-ubuntu-jdk8","6.2.2","6.1.7-ubuntu","6.2.2-jdk8","6.1.7-jdk8","6.0.5-jdk11","6.3.3-ubuntu-jdk11","6.2.1-jdk11","6.1.6-ubuntu-jdk11","6.0.5-ubuntu-jdk11","6.3.3-jdk11","6.2.1-ubuntu-jdk11","6.1.6-jdk11","6.2.3-ubuntu","6.1.8-jdk8","6.0.0-ubuntu","6.2.3-ubuntu-jdk8","6.1.8-ubuntu-jdk8","6.0.0-ubuntu-jdk8","6.2.3","6.1.8-ubuntu","6.0.0-jdk8","6.0.6-ubuntu-jdk11","6.3.4-ubuntu-jdk11","6.2.2-ubuntu-jdk11","6.1.8","6.2.3-jdk8","6.0.0","6.1.7-jdk11","6.0.6-jdk11","6.3.4-jdk11","6.2.2-jdk11","6.1.7-ubuntu-jdk11","6.2.3-ubuntu-jdk11","6.3.5-ubuntu-jdk11","6.0.7-ubuntu-jdk11","6.0.0-ubuntu-jdk11","6.1.8-jdk11","6.2.3-jdk11","6.0.7-jdk11","6.3.5-jdk11","6.0.0-jdk11","6.1.8-ubuntu-jdk11","6.1.9-ubuntu","6.2.4-jdk8","6.0.1-ubuntu-jdk8","6.1-ubuntu-jdk8","6.2.4-ubuntu-jdk8","6.0.1-jdk8","6.1.9-jdk8","6.2.4","6.0.1-ubuntu","6.1-ubuntu","6.2.4-ubuntu","6.0.1","6.0.9-jdk11","6.2.4-ubuntu-jdk11","6.1-jdk8","6.3-jdk11","6.0.1-ubuntu-jdk11","6.1.9-ubuntu-jdk11","6.0.9-ubuntu-jdk11","6.1.9-ubuntu-jdk8","6.2.4-jdk11","6.3.6-jdk11","6.0.1-jdk11","6.1-jdk11","6.1","6.3-ubuntu-jdk11","6.1-ubuntu-jdk11","6.1.9","6.3.6-ubuntu-jdk11","6.1.9-jdk11","6.2.5","6.0.10-ubuntu-jdk8","6.2.5-ubuntu","6.0.10-jdk8","6.1.0-ubuntu-jdk11","6.2.5-jdk11","6.2.5-jdk8","6.0.10-jdk11","6.0.10-ubuntu","6.2.5-ubuntu-jdk8","6.2.5-ubuntu-jdk11","6.1.0-jdk11","6.0.10-ubuntu-jdk11","6.0.10","7-eap","5.16.11-alpine","5-alpine","5.16.11","5.16","5","5.16-alpine","5.16.10","5.16.10-alpine","5.16.9","5.16.9-alpine","5.16.8","5.16.8-alpine","5.16.7-alpine","5.16.7","5.16.6-alpine","5.16.6","5.16.5-alpine","5.16.5","5.9.2-alpine","5.9.2","5.9-alpine","5.9","5.9.1-alpine","5.9.1","5.9.0-alpine","5.9.0","5.8.3-alpine","5.8.3","5.8.2-alpine","5.8.2","5.8.1-alpine","5.8.1","5.8.0-alpine","5.8.0","5.7.4-alpine","5.7.4","5.7-alpine","5.7","5.7.3-alpine","5.7.3","5.7.2-alpine","5.7.2","5.7.1-alpine","5.7.1","5.7.0-alpine","5.7.0","5.6.6-alpine","5.6.6","5.6-alpine","5.6","5.6.5-alpine","5.6.5","5.6.4-alpine","5.6.4","5.6.3-alpine","5.6.3","5.6.2-alpine","5.6.2","5.6.1-alpine","5.6.1","5.6.0-alpine","5.6.0","5.5.9-alpine","5.5.9","5.5-alpine","5.5","5.5.8-alpine","5.5.8","5.5.7-alpine","5.5.7","5.5.6-alpine","5.5.6","5.5.5-alpine","5.5.5","5.5.4-alpine","5.5.4","5.5.3-alpine","5.5.3","5.5.2-alpine","5.5.2","5.5.1-alpine","5.5.1","5.5.0-alpine","5.5.0","5.4.9-alpine","5.4.9","5.4-alpine","5.4","5.4.8-alpine","5.4.8","5.4.7-alpine","5.4.7","5.4.6-alpine","5.4.6","5.4.4-alpine","5.4.4","5.4.3-alpine","5.4.3","5.4.2-alpine","5.4.2","5.4.1-alpine","5.4.1","5.4.0-alpine","5.4.0","5.3.7-alpine","5.3.7","5.3-alpine","5.3","5.3.6-alpine","5.3.6","5.3.5-alpine","5.3.5","5.3.4-alpine","5.3.4","5.3.3-alpine","5.3.3","5.3.2-alpine","5.3.2","5.3.1-alpine","5.3.1","5.3.0-alpine","5.3.0","5.2.8-alpine","5.2.8","5.2-alpine","5.2","5.2.7-alpine","5.2.7","5.2.6-alpine","5.2.6","5.2.5-alpine","5.2.5","5.2.4-alpine","5.2.4","5.2.3-alpine","5.2.3","5.2.2-alpine","5.2.2","5.2.1-alpine","5.2.1","5.2.0-alpine","5.2.0","5.16.4-alpine","5.16.4","5.16.3-alpine","5.16.3","5.16.2-alpine","5.16.2","5.16.1-alpine","5.16.1","5.16.0-alpine","5.16.0","5.15.3-alpine","5.15.3","5.15-alpine","5.15","5.15.2-alpine","5.15.2","5.15.1-alpine","5.15.1","5.15.0-alpine","5.15.0","5.14.4-alpine","5.14.4","5.14-alpine","5.14","5.14.3-alpine","5.14.3","5.14.2-alpine","5.14.2","5.14.1-alpine","5.14.1","5.14.0-alpine","5.14.0","5.13.6-alpine","5.13.6","5.13-alpine","5.13","5.13.5-alpine","5.13.5","5.13.4-alpine","5.13.4","5.13.3-alpine","5.13.3","5.13.1-alpine","5.13.1","5.13.0-alpine","5.13.0","5.12.4-alpine","5.12.4","5.12-alpine","5.12","5.12.3-alpine","5.12.3","5.12.2-alpine","5.12.2","5.12.1-alpine","5.12.1","5.12.0-alpine","5.12.0","5.11.4-alpine","5.11.4","5.11-alpine","5.11","5.11.3-alpine","5.11.3","5.11.2-alpine","5.11.2","5.11.1-alpine","5.11.1","5.10.4-alpine","5.10.4","5.10-alpine","5.10","5.10.3-alpine","5.10.3","5.10.2-alpine","5.10.2","5.10.1-alpine","5.10.1","5.10.0-alpine","5.10.0","5.1.9-alpine","5.1.9","5.1-alpine","5.1","5.1.8-alpine","5.1.8","5.1.7-alpine","5.1.7","5.1.6-alpine","5.1.6","5.1.5-alpine","5.1.5","5.1.4-alpine","5.1.4","5.1.3-alpine","5.1.3","5.1.2-alpine","5.1.2","5.1.1-alpine","5.1.1","5.1.0-alpine","5.1.0","5.0.9-alpine","5.0.9","5.0.8-alpine","5.0.8","5.0.7-alpine","5.0.7","5.0.6-alpine","5.0.6","5.0.5-alpine","5.0.5","5.0.4-alpine","5.0.4","5.0.2-alpine","5.0.2","5.0.10-alpine","5.0.10","5.0-alpine","5.0","5.0.1-alpine","5.0.1","5.0.0-alpine","5.0.0","4.14.5-alpine","4.14.5","4.1.6-alpine","4.1.6","4.1-alpine","4.1","4.11.2-alpine","4.11.2","4.11-alpine","4.11","5.8.4","5.8","4.14","4.13","4.12","4.10","4.9","4.8","4.6","4.7","4.4","4.5","4.3","4.2","4.0"],"mysql":["latest","oraclelinux8","oracle","innovation-oraclelinux8","innovation-oracle","innovation","8.3.0-oraclelinux8","8.3.0-oracle","8.3.0","8.3-oraclelinux8","8.3-oracle","8.3","8.0.36-oraclelinux8","8.0.36-oracle","8.0.36","8.0-oraclelinux8","8.0-oracle","8.0","8-oraclelinux8","8-oracle","8","8.0.36-debian","8.0.36-bookworm","8.0-debian","8.0-bookworm","8.0.36-bullseye","8.0-bullseye","8.2.0-oraclelinux8","8.2.0-oracle","8.2.0","8.2-oraclelinux8","8.2-oracle","8.2","8.0.35-oraclelinux8","8.0.35-oracle","8.0.35","8.0.35-debian","8.0.35-bullseye","5.7.44-oraclelinux7","5.7-oraclelinux7","5-oraclelinux7","5.7.44-oracle","5.7.44","5.7-oracle","5.7","5-oracle","5","8.1.0-oracle","8.1.0","8.1-oracle","8.1","8.0.34-oracle","8.0.34","5.7.43-oracle","5.7.43","8.0.34-debian","debian","8-debian","5.7.42-debian","5.7-debian","5-debian","5.7.42-oracle","5.7.42","8.0.33-oracle","8.0.33","8.0.33-debian","8.0.32-debian","5.7.41-debian","8.0.32-oracle","8.0.32","5.7.41-oracle","5.7.41","8.0.31-debian","5.7.40-debian","8.0.31-oracle","8.0.31","5.7.40-oracle","5.7.40","8.0.30-oracle","8.0.30","5.7.39-oracle","5.7.39","8.0.30-debian","5.7.39-debian","8.0.29-oracle","8.0.29","5.7.38-oracle","5.7.38","8.0.29-debian","5.7.38-debian","8.0.28-debian","8.0.28","5.7.37-debian","5.7.37","8.0.28-oracle","5.7.37-oracle","8.0.27","5.7.36","5.6.51","5.6","8.0.26","5.7.35","8.0.25","5.7.34","8.0.24","8.0.23","5.7.33","8.0.22","5.7.32","5.6.50","8.0.21","5.7.31","5.6.49","8.0.20","5.7.30","5.6.48","8.0.19","5.7.29","5.6.47","8.0.18","5.7.28","5.6.46","8.0.17","5.7.27","5.6.45","8.0.16","5.7.26","5.6.44","5.5.62","5.5","5.6.43","5.7.25","8.0.15","8.0.14","5.6.42","5.7.24","8.0.13","5.5.61","5.6.41","5.7.23","8.0.12","5.5.60","5.6.40","5.7.22","8.0.11","5.5.59","5.6.39","5.7.21","8.0.4","8.0.4-rc","8.0.3","5.5.58","5.6.38","5.7.20","5.5.57","5.6.37","5.7.19","8.0.2","5.5.56","5.6.36","5.7.18","8.0.1","5.5.55","5.6.35","5.7.17","8.0.0","5.5.54","5.5.53","5.7.16","5.6.34","5.5.52","5.6.33","5.7.15","5.5.51","5.6.32","5.7.14","5.5.50","5.6.31","5.7.13","5.7.12","5.6.30","5.5.49","5.7.11","5.6.29","5.5.48","5.7.10","5.6.28","5.5.47","5.6.27","5.7.9","5.5.46","5.5.40","5.7.4","5.6.17","5.5.41","5.7.5-m15","5.6.21","5.6.20","5.6.22","5.7.4-m14","5.7.5","5.7.8","5.7.8-rc","5.6.26","5.5.45","5.7.7","5.7.7-rc","5.6.25","5.5.44","5.6.24","5.5.43","5.7.6","5.7.6-m16","5.6.23","5.5.42"],"postgresql":["latest","bullseye","bookworm","16.2-bullseye","16.2-bookworm","16.2","16-bullseye","16-bookworm","16","15.6-bullseye","15.6-bookworm","15.6","15-bullseye","15-bookworm","15","14.11-bullseye","14.11-bookworm","14.11","14-bullseye","14-bookworm","14","13.14-bullseye","13.14-bookworm","13.14","13-bullseye","13-bookworm","13","12.18-bullseye","12.18-bookworm","12.18","12-bullseye","12-bookworm","12","alpine3.19","alpine3.18","alpine","16.2-alpine3.19","16.2-alpine3.18","16.2-alpine","16-alpine3.19","16-alpine3.18","16-alpine","15.6-alpine3.19","15.6-alpine3.18","15.6-alpine","15-alpine3.19","15-alpine3.18","15-alpine","14.11-alpine3.19","14.11-alpine3.18","14.11-alpine","14-alpine3.19","14-alpine3.18","14-alpine","13.14-alpine3.19","13.14-alpine3.18","13.14-alpine","13-alpine3.19","13-alpine3.18","13-alpine","12.18-alpine3.19","12.18-alpine3.18","12.18-alpine","12-alpine3.19","12-alpine3.18","12-alpine","16.1-bullseye","15.5-bullseye","15.5-bookworm","15.5","14.10-bullseye","14.10-bookworm","14.10","13.13-bullseye","13.13-bookworm","13.13","12.17-bullseye","12.17-bookworm","12.17","16.1-bookworm","16.1","16.1-alpine3.19","16.1-alpine3.18","16.1-alpine","15.5-alpine3.19","15.5-alpine3.18","15.5-alpine","14.10-alpine3.19","14.10-alpine3.18","14.10-alpine","13.13-alpine3.19","13.13-alpine3.18","13.13-alpine","12.17-alpine3.19","12.17-alpine3.18","12.17-alpine","11.22-bullseye","11.22-bookworm","11-bullseye","11-bookworm","11.22-alpine3.19","11.22-alpine","11-alpine3.19","11-alpine","12.17-alpine3.17","12-alpine3.17","11.22-alpine3.18","11.22-alpine3.17","11-alpine3.18","11-alpine3.17","alpine3.17","16.1-alpine3.17","16-alpine3.17","15.5-alpine3.17","15-alpine3.17","13.13-alpine3.17","13-alpine3.17","14.10-alpine3.17","14-alpine3.17","12.16-bullseye","11.21-bullseye","11.21-bookworm","13.12-bullseye","13.12-bookworm","13.12","12.16-bookworm","12.16","14.9-bullseye","14.9-bookworm","14.9","16.0-bullseye","15.4-bullseye","15.4-bookworm","15.4","16.0-bookworm","16.0","16.0-alpine3.18","16.0-alpine3.17","16.0-alpine","15.4-alpine3.18","15.4-alpine3.17","15.4-alpine","14.9-alpine3.18","14.9-alpine3.17","14.9-alpine","13.12-alpine3.18","13.12-alpine3.17","13.12-alpine","12.16-alpine3.18","12.16-alpine3.17","12.16-alpine","11.21-alpine3.18","11.21-alpine3.17","11.21-alpine","16rc1-bullseye","16rc1-bookworm","16rc1","16rc1-alpine3.18","16rc1-alpine3.17","16rc1-alpine","16beta3-bullseye","16beta3-bookworm","16beta3","16beta3-alpine3.18","16beta3-alpine3.17","16beta3-alpine","16beta2-alpine3.18","16beta2-alpine3.17","16beta2-alpine","15.3-alpine3.18","15.3-alpine3.17","15.3-alpine","14.8-alpine3.18","14.8-alpine3.17","14.8-alpine","13.11-alpine3.18","13.11-alpine3.17","13.11-alpine","12.15-alpine3.18","12.15-alpine3.17","12.15-alpine","11.20-alpine3.18","11.20-alpine3.17","11.20-alpine","16beta2-bullseye","16beta2-bookworm","16beta2","15.3-bullseye","15.3-bookworm","15.3","14.8-bullseye","14.8-bookworm","14.8","13.11-bullseye","13.11-bookworm","13.11","12.15-bullseye","12.15-bookworm","12.15","11.20-bullseye","11.20-bookworm","16beta1-bookworm","16beta1-alpine3.18","16beta1-alpine3.17","16beta1-alpine","16beta1","16beta1-bullseye","15.2-bullseye","15.2-alpine3.17","15.2-alpine","15.2","14.7-bullseye","14.7-alpine3.17","14.7-alpine","14.7","13.10-bullseye","13.10-alpine3.17","13.10-alpine","13.10","12.14-bullseye","12.14-alpine3.17","12.14-alpine","12.14","11.19-bullseye","11.19-alpine3.17","11.19-alpine","15.1-bullseye","15.1","14.6-bullseye","14.6","13.9-bullseye","13.9","12.13-bullseye","12.13","11.18-bullseye","15.1-alpine3.17","15.1-alpine","14.6-alpine3.17","14.6-alpine","13.9-alpine3.17","13.9-alpine","12.13-alpine3.17","12.13-alpine","11.18-alpine3.17","11.18-alpine","alpine3.16","15.1-alpine3.16","15-alpine3.16","14.6-alpine3.16","14-alpine3.16","13.9-alpine3.16","13-alpine3.16","12.13-alpine3.16","12-alpine3.16","11.18-alpine3.16","11-alpine3.16","10.23-alpine3.16","10.23-alpine","10-alpine3.16","10-alpine","10.23-bullseye","10-bullseye","15.0-bullseye","14.5-bullseye","14.5","13.8-bullseye","13.8","12.12-bullseye","12.12","11.17-bullseye","10.22-bullseye","15.0","15.0-alpine3.16","15.0-alpine","14.5-alpine3.16","14.5-alpine","13.8-alpine3.16","13.8-alpine","12.12-alpine3.16","12.12-alpine","11.17-alpine3.16","11.17-alpine","10.22-alpine3.16","10.22-alpine","15rc2-alpine3.16","15rc2-alpine","15rc2-bullseye","15rc2","15rc1-alpine3.16","15rc1-alpine","15rc1-bullseye","15rc1","15beta4-bullseye","15beta4","15beta4-alpine3.16","15beta4-alpine","15beta3-bullseye","15beta3","15beta3-alpine3.16","15beta3-alpine","15beta2-alpine3.16","15beta2-alpine","14.4-alpine3.16","14.4-alpine","13.7-alpine3.16","13.7-alpine","12.11-alpine3.16","12.11-alpine","11.16-alpine3.16","11.16-alpine","10.21-alpine3.16","10.21-alpine","12.11-bullseye","12.11","15beta2-bullseye","15beta2","14.4-bullseye","14.4","13.7-bullseye","13.7","11.16-bullseye","10.21-bullseye","15beta1-bullseye","15beta1","11.16-stretch","11.16","11-stretch","11","10.21-stretch","10.21","10-stretch","10","15beta1-alpine3.16","15beta1-alpine","14.3-alpine3.16","14.3-alpine","14.3-bullseye","14.3","15beta1-alpine3.15","alpine3.15","14.3-alpine3.15","14-alpine3.15","13.7-alpine3.15","13-alpine3.15","12.11-alpine3.15","12-alpine3.15","11.16-alpine3.15","11-alpine3.15","10.21-alpine3.15","10-alpine3.15","14.2-bullseye","14.2","13.6-bullseye","13.6","12.10-bullseye","12.10","11.15-bullseye","10.20-bullseye","11.15-stretch","11.15","10.20-stretch","10.20","14.2-alpine3.15","14.2-alpine","13.6-alpine3.15","13.6-alpine","12.10-alpine3.15","12.10-alpine","11.15-alpine3.15","11.15-alpine","10.20-alpine3.15","10.20-alpine","9.6.24-bullseye","9.6-bullseye","9-bullseye","9.6.24-stretch","9.6.24","9.6-stretch","9.6","9-stretch","9","14.1-bullseye","14.1","13.5-bullseye","13.5","12.9-bullseye","12.9","11.14-stretch","11.14-bullseye","11.14","10.19-stretch","10.19-bullseye","10.19","9.6.24-alpine3.15","9.6.24-alpine","9.6-alpine3.15","9.6-alpine","9-alpine3.15","9-alpine","14.1-alpine3.15","14.1-alpine","13.5-alpine3.15","13.5-alpine","12.9-alpine3.15","12.9-alpine","11.14-alpine3.15","11.14-alpine","10.19-alpine3.15","10.19-alpine","alpine3.14","9.6.24-alpine3.14","9.6-alpine3.14","9-alpine3.14","14.1-alpine3.14","14-alpine3.14","13.5-alpine3.14","13-alpine3.14","12.9-alpine3.14","12-alpine3.14","11.14-alpine3.14","11-alpine3.14","10.19-alpine3.14","10-alpine3.14","9.6.23-stretch","9.6.23-bullseye","9.6.23-alpine3.14","9.6.23-alpine","9.6.23","14.0-bullseye","14.0-alpine3.14","14.0-alpine","14.0","13.4-bullseye","13.4-alpine3.14","13.4-alpine","13.4","12.8-bullseye","12.8-alpine3.14","12.8-alpine","12.8","11.13-stretch","11.13-bullseye","11.13-alpine3.14","11.13-alpine","11.13","10.18-stretch","10.18-bullseye","10.18-alpine3.14","10.18-alpine","10.18","14rc1-bullseye","14rc1","14rc1-alpine3.14","14rc1-alpine","buster","9.6.23-buster","9.6-buster","9-buster","14beta3-buster","14beta3","13.4-buster","13-buster","12.8-buster","12-buster","11.13-buster","11-buster","10.18-buster","10-buster","14beta3-alpine3.14","14beta3-alpine","9.6.22-alpine3.14","9.6.22-alpine","14beta2-alpine3.14","14beta2-alpine","13.3-alpine3.14","13.3-alpine","12.7-alpine3.14","12.7-alpine","11.12-alpine3.14","11.12-alpine","10.17-alpine3.14","10.17-alpine","9.6.22-stretch","9.6.22-buster","9.6.22","14beta2-buster","14beta2","13.3-buster","13.3","12.7-buster","12.7","11.12-stretch","11.12-buster","11.12","10.17-stretch","10.17-buster","10.17","alpine3.13","9-alpine3.13","13.3-alpine3.13","13-alpine3.13","12.7-alpine3.13","12-alpine3.13","11.12-alpine3.13","11-alpine3.13","10.17-alpine3.13","10-alpine3.13","9.6.22-alpine3.13","9.6-alpine3.13","9.6.21","9.5.25","9.5","13.2","12.6","11.11","10.16","9.6.21-alpine","9.5.25-alpine","9.5-alpine","13.2-alpine","12.6-alpine","11.11-alpine","10.16-alpine","9.6.20","9.5.24","13.1","12.5","11.10","10.15","9.6.20-alpine","9.5.24-alpine","13.1-alpine","12.5-alpine","11.10-alpine","10.15-alpine","9.6.19-alpine","9.5.23-alpine","13.0-alpine","12.4-alpine","11.9-alpine","10.14-alpine","9.6.19","9.5.23","13.0","12.4","11.9","10.14","13-rc1-alpine","13-rc1","13-beta3","13-beta3-alpine","9.6.18","9.5.22","13-beta2","12.3","11.8","10.13","9.6.18-alpine","9.5.22-alpine","13-beta2-alpine","12.3-alpine","11.8-alpine","10.13-alpine","13-beta1-alpine","13-beta1","9.6.17","9.5.21","11.7","10.12","9.6.17-alpine","9.5.21-alpine","12.2-alpine","11.7-alpine","10.12-alpine","12.2","9.4.26-alpine","9.4.26","9.4-alpine","9.4","9.6.16","9.5.20","9.4.25","12.1","11.6","10.11","9.6.16-alpine","9.5.20-alpine","9.4.25-alpine","12.1-alpine","11.6-alpine","10.11-alpine","9.6.15-alpine","9.5.19-alpine","9.4.24-alpine","12.0-alpine","11.5-alpine","10.10-alpine","9.6.15","9.5.19","9.4.24","12.0","11.5","10.10","12-rc1-alpine","12-rc1","12-beta4-alpine","12-beta4","12-beta3","12-beta3-alpine","9.6.14-alpine","9.5.18-alpine","9.4.23-alpine","12-beta2-alpine","11.4-alpine","10.9-alpine","9.6.14","9.5.18","9.4.23","11.4","10.9","12-beta2","9.6.13-alpine","9.5.17-alpine","9.4.22-alpine","12-beta1-alpine","11.3-alpine","10.8-alpine","9.6.13","9.5.17","9.4.22","12-beta1","11.3","10.8","11.2","9.4.21","9.5.16","9.6.12","10.7","9.4.21-alpine","9.5.16-alpine","9.6.12-alpine","10.7-alpine","11.2-alpine","9.4.20","9.5.15","9.6.11","10.6","11.1","9.4.20-alpine","9.5.15-alpine","9.6.11-alpine","10.6-alpine","11.1-alpine","9.3","9.3.25","9.3-alpine","9.3.25-alpine","10.5-alpine","10.5","11.0","11.0-alpine","9.3.24","9.4.19","9.5.14","9.6.10","11-rc1","11-rc1-alpine","11-beta4-alpine","9.4.19-alpine","11-beta4","9.3.24-alpine","9.5.14-alpine","9.6.10-alpine","11-beta3-alpine","11-beta3","9.3.23","9.5.13","9.5.13-alpine","9.4.18","9.6.9","10.4-alpine","10.4","11-beta2-alpine","11-beta2","9.4.18-alpine","9.3.23-alpine","9.6.9-alpine","11-beta1","11-beta1-alpine","9.6.8","10.3","9.3.22-alpine","9.3.22","9.4.17-alpine","9.4.17","9.5.12-alpine","9.5.12","9.6.8-alpine","10.3-alpine","9.3.21","9.4.16","9.5.11","9.6.7","10.2-alpine","10.2","9.5.11-alpine","9.5.10","9.3.21-alpine","9.4.16-alpine","9.6.7-alpine","9.3.20-alpine","9.4.15-alpine","9.5.10-alpine","9.6.6-alpine","10.1-alpine","9.3.20","9.4.15","9.6.6","10.1","9.3.19-alpine","9.3.19","9.4.14-alpine","9.4.14","9.5.9-alpine","9.5.9","9.6.5-alpine","9.6.5","10.0-alpine","10.0","9.2","9.2.23","10-rc1","9.2-alpine","9.2.23-alpine","10-rc1-alpine","10-beta4","10-beta4-alpine","9.2.22-alpine","9.2.22","9.3.18-alpine","9.3.18","9.4.13-alpine","9.4.13","9.5.8-alpine","9.5.8","9.6.4-alpine","9.6.4","10-beta3-alpine","10-beta3","9.2.21-alpine","9.2.21","9.3.17-alpine","9.3.17","9.4.12-alpine","9.4.12","9.5.7-alpine","9.5.7","9.6.3-alpine","9.6.3","10-beta2-alpine","10-beta2","10-beta1-alpine","10-beta1","9.2.20-alpine","9.2.20","9.3.16-alpine","9.3.16","9.4.11-alpine","9.4.11","9.5.6-alpine","9.5.6","9.6.2-alpine","9.6.2","9.2.19-alpine","9.2.19","9.3.15-alpine","9.3.15","9.4.10-alpine","9.4.10","9.5.5-alpine","9.5.5","9.6.1-alpine","9.6.1","9.1.24","9.1","9.5.4","9.4.9","9.3.14","9.2.18","9.1.23","9.6.0","9.6-rc1","9.6-beta4","9.4.8","9.1.22","9.6-beta3","9.5.3","9.3.13","9.2.17","9.6-beta2","9.6-beta1","9.5.2","9.4.7","9.3.12","9.2.16","9.1.21","9.5.1","9.4.6","9.3.11","9.2.15","9.1.20","9.5.0","9.4.5","9.3.10","9.2.14","9.1.19","9.0","9.0.22","9.5-rc1","9.5-beta2","9.5-beta1","9.4-rc1","9.3.5","9.4-beta2","9.2.9","9.4.0","9.1.14","9.4-beta3","9.0.18","9.5-alpha2","9.4.4","9.3.9","9.2.13","9.1.18","9.5-alpha1","9.4.3","9.3.8","9.2.12","9.1.17","9.0.21","9.4.2","9.3.7","9.2.11","9.1.16","9.0.20","8","8.4","8.4.22","9.4.1","9.3.6","9.2.10","9.1.15","9.0.19"],"mssql":["2017-CU1-ubuntu","2017-CU10","2017-CU10-ubuntu","2017-CU11","2017-CU11-ubuntu","2017-CU12","2017-CU12-ubuntu","2017-CU13","2017-CU13-ubuntu","2017-CU14","2017-CU14-ubuntu","2017-CU15","2017-CU15-GDR","2017-CU15-GDR-ubuntu","2017-CU15-GDR1-ubuntu-16.04","2017-CU15-ubuntu","2017-CU16","2017-CU16-ubuntu","2017-CU17","2017-CU17-ubuntu","2017-CU18-ubuntu-16.04","2017-CU19-ubuntu-16.04","2017-CU2-ubuntu","2017-CU20","2017-CU20-ubuntu","2017-CU20-ubuntu-16.04","2017-CU21-ubuntu-16.04","2017-CU22-GDR1-ubuntu-16.04","2017-CU22-OD1-ubuntu-16.04","2017-CU22-ubuntu-16.04","2017-CU23-ubuntu-16.04","2017-CU24-ubuntu-16.04","2017-CU25-ubuntu-16.04","2017-CU26-ubuntu-16.04","2017-CU27-ubuntu-16.04","2017-CU28-ubuntu-16.04","2017-CU29-GDR1-ubuntu-16.04","2017-CU29-ubuntu-16.04","2017-CU3-ubuntu","2017-CU30-ubuntu-18.04","2017-CU31-GDR1-ubuntu-18.04","2017-CU31-GDR2-ubuntu-18.04","2017-CU31-ubuntu-18.04","2017-CU4-ubuntu","2017-CU5-ubuntu","2017-CU6-ubuntu","2017-CU7-ubuntu","2017-CU8-ubuntu","2017-CU9-ubuntu","2017-GA-ubuntu","2017-GDR-ubuntu","2017-GDR3","2017-GDR3-ubuntu","2017-GDR4","2017-GDR4-ubuntu","2017-cu16","2017-cu16-ubuntu","2017-cu17","2017-cu17-ubuntu","2017-cu19","2017-cu19-ubuntu","2017-latest","2017-latest-ubuntu","2019-CU1-ubuntu-16.04","2019-CU10-ubuntu-16.04","2019-CU10-ubuntu-18.04","2019-CU10-ubuntu-20.04","2019-CU11-ubuntu-16.04","2019-CU11-ubuntu-18.04","2019-CU11-ubuntu-20.04","2019-CU12-ubuntu-16.04","2019-CU12-ubuntu-18.04","2019-CU12-ubuntu-20.04","2019-CU13-ubuntu-16.04","2019-CU13-ubuntu-18.04","2019-CU13-ubuntu-20.04","2019-CU14-ubuntu-16.04","2019-CU14-ubuntu-18.04","2019-CU14-ubuntu-20.04","2019-CU15-ubuntu-16.04","2019-CU15-ubuntu-18.04","2019-CU15-ubuntu-20.04","2019-CU16-GDR1-ubuntu-16.04","2019-CU16-GDR1-ubuntu-18.04","2019-CU16-GDR1-ubuntu-20.04","2019-CU16-ubuntu-16.04","2019-CU16-ubuntu-18.04","2019-CU16-ubuntu-20.04","2019-CU17-ubuntu-18.04","2019-CU17-ubuntu-20.04","2019-CU18-GDR1-ubuntu-18.04","2019-CU18-GDR1-ubuntu-20.04","2019-CU18-ubuntu-18.04","2019-CU18-ubuntu-20.04","2019-CU19-ubuntu-18.04","2019-CU19-ubuntu-20.04","2019-CU2-ubuntu-16.04","2019-CU20-ubuntu-18.04","2019-CU20-ubuntu-20.04","2019-CU21-ubuntu-20.04","2019-CU22-GDR1-ubuntu-20.04","2019-CU22-ubuntu-20.04","2019-CU23-ubuntu-20.04","2019-CU24-ubuntu-20.04","2019-CU25-GDR1-ubuntu-20.04","2019-CU25-ubuntu-20.04","2019-CU26-ubuntu-20.04","2019-CU3-ubuntu-16.04","2019-CU3-ubuntu-18.04","2019-CU4-ubuntu-16.04","2019-CU4-ubuntu-18.04","2019-CU5-ubuntu-16.04","2019-CU5-ubuntu-18.04","2019-CU6-ubuntu-16.04","2019-CU6-ubuntu-18.04","2019-CU8-GDR1-ubuntu-16.04","2019-CU8-GDR1-ubuntu-18.04","2019-CU8-OD2-ubuntu-16.04","2019-CU8-OD2-ubuntu-18.04","2019-CU8-ubuntu-16.04","2019-CU8-ubuntu-18.04","2019-CU9-ubuntu-16.04","2019-CU9-ubuntu-18.04","2019-GA-ubuntu-16.04","2019-GDR1-ubuntu-16.04","2019-GDR2-ubuntu-16.04","2019-gdr2-ubuntu-16.04","2019-gdr3-ubuntu-16.04","2019-latest","2022-CTP2.0-ubuntu","2022-CTP2.0-ubuntu-20.04","2022-CU1-ubuntu-20.04","2022-CU10-GDR1-ubuntu-20.04","2022-CU10-GDR1-ubuntu-22.04","2022-CU10-ubuntu-20.04","2022-CU10-ubuntu-22.04","2022-CU11-ubuntu-20.04","2022-CU11-ubuntu-22.04","2022-CU12-GDR1-ubuntu-20.04","2022-CU12-GDR1-ubuntu-22.04","2022-CU12-ubuntu-20.04","2022-CU12-ubuntu-22.04","2022-CU3-ubuntu-20.04","2022-CU4-ubuntu-20.04","2022-CU5-ubuntu-20.04","2022-CU6-ubuntu-20.04","2022-CU7-ubuntu-20.04","2022-CU8-GDR1-ubuntu-20.04","2022-CU8-ubuntu-20.04","2022-CU9-ubuntu-20.04","2022-RC0-ubuntu-20.04","2022-RC1-ubuntu-20.04","2022-RTM-CU1-ubuntu-20.04","2022-RTM-CU2-ubuntu-20.04","2022-RTM-GDR1-ubuntu-20.04","2022-RTM-ubuntu-20.04","2022-latest","2022-preview-ubuntu-22.04","latest","latest-ubuntu"]} \ No newline at end of file diff --git a/generateVersionList.mjs b/generateVersionList.mjs index 6e06813..a92dfde 100644 --- a/generateVersionList.mjs +++ b/generateVersionList.mjs @@ -10,7 +10,7 @@ const repositories = [ { name: 'bamboo', repository: 'atlassian/bamboo-server' }, { name: 'bitbucket', repository: 'atlassian/bitbucket-server' }, { name: 'mysql', repository: 'library/mysql' }, - { name: 'postgres', repository: 'library/postgres' }, + { name: 'postgresql', repository: 'library/postgres' }, ]; const getListOfTagsPaginated = async (url) => { diff --git a/package.json b/package.json index 024a296..5906605 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,7 @@ "README", "package.json" ], - "bin": { - "dcdx": "./lib/index.js" - }, + "bin": "./lib/index.js", "engines": { "node": ">=18" }, @@ -29,7 +27,9 @@ "generate:versions": "node ./generateVersionList.mjs", "watch": "yarn run nodemon --watch src -e '.ts' --exec 'yarn build'", "start": "./lib/index.js", - "prepack": "yarn generate:versions && yarn build" + "prepack": "yarn generate:versions && yarn build", + "test": "yarn run vitest --coverage --disable-console-intercept --watch=false --silent=false", + "test:ui": "yarn run vitest --coverage --disable-console-intercept --silent=false --ui" }, "repository": { "type": "git", @@ -51,9 +51,10 @@ "@types/js-yaml": "4", "@types/node": "18.16.0", "@types/pg": "8", - "@types/yargs": "17.0.32", "@typescript-eslint/eslint-plugin": "7.6.0", "@typescript-eslint/parser": "7.6.0", + "@vitest/coverage-v8": "^1.6.0", + "@vitest/ui": "^1.6.0", "eslint": "9.0.0", "eslint-plugin-simple-import-sort": "12.0.0", "nodemon": "3.1.0", @@ -61,7 +62,9 @@ "rollup-plugin-executable": "1.6.3", "semantic-release": "23.0.8", "typescript": "5.4.4", - "typescript-eslint": "7.6.0" + "typescript-eslint": "7.6.0", + "vitest": "1.6.0", + "vitest-mock-process": "1.0.4" }, "dependencies": { "@xmldom/xmldom": "0.8.10", @@ -79,7 +82,7 @@ "simple-git": "3.24.0", "tedious": "18.1.0", "xpath": "0.0.34", - "yargs": "17.7.2" + "zod": "3.23.6" }, "release": { "branches": [ diff --git a/rollup.config.js b/rollup.config.js index e33121d..62d30a3 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,7 +2,6 @@ import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; import nodeResolve from '@rollup/plugin-node-resolve'; -import terser from '@rollup/plugin-terser'; import { readdirSync } from 'fs'; import path from 'path'; import executable from 'rollup-plugin-executable'; @@ -13,7 +12,7 @@ const getCommands = (dir) => { commands.forEach(item => { if (item.isDirectory()) { result.push(...getCommands(path.join(dir, item.name))); - } else { + } else if (item.name.endsWith('ts')) { result.push(path.join(dir, item.name)); } }); @@ -27,7 +26,8 @@ export default [ input: `./dist/src/${file}`, output: { file: `./lib/${file}`, - format: 'es' + format: 'es', + sourcemap: 'inline' }, external: [ /node_modules/ ], plugins: [ @@ -36,7 +36,6 @@ export default [ nodeResolve({ preferBuiltins: true }), - terser({ keep_classnames: true, keep_fnames: true }), executable() ], onwarn(warning, rollupWarn) { diff --git a/src/applications/bamboo.ts b/src/applications/bamboo.ts index d2914f7..2313cd2 100644 --- a/src/applications/bamboo.ts +++ b/src/applications/bamboo.ts @@ -3,21 +3,20 @@ import axios from 'axios'; import { timebomb } from '../helpers/licences'; import { toAbsolutePath } from '../helpers/toAbsolutePath'; -import { ApplicationOptions } from '../types/ApplicationOptions'; -import { DatabaseEngine } from '../types/DatabaseEngine'; +import { SupportedApplications, TApplicationOptions } from '../types/Application'; +import { DatabaseEngine } from '../types/Database'; import { Service } from '../types/DockerComposeV3'; -import { SupportedApplications } from '../types/SupportedApplications'; import { Base } from './base'; export class Bamboo extends Base { - name = SupportedApplications.BAMBOO; + name = SupportedApplications.Values.bamboo; database: DatabaseEngine; logFilePath = '/var/atlassian/application-data/bamboo/logs/atlassian-bamboo.log'; // ------------------------------------------------------------------------------------------ Constructor - constructor(options: ApplicationOptions) { + constructor(options: TApplicationOptions) { super(options); this.database = this.getDatabaseEngine(options.database); } @@ -26,14 +25,13 @@ export class Bamboo extends Base { protected getService(): Service { - const volumes = this.getVolumes(); const environment = this.getEnvironmentVariables(); return { build: { context: toAbsolutePath('../../assets'), dockerfile_inline: ` -FROM dcdx/${this.name}:${this.options.version} +FROM dcdx/${this.name}:${this.options.tag} COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/bamboo/lib/mysql-connector-j-8.3.0.jar COPY ./quickreload-5.0.4.jar /var/atlassian/application-data/bamboo/shared/plugins/quickreload-5.0.4.jar RUN echo "/opt/quickreload" > /var/atlassian/application-data/bamboo/quickreload.properties; \ @@ -46,8 +44,7 @@ RUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo` `${this.options.port || 80}:8085`, ...this.options.debug ? [ '5005:5005' ] : [], ], - environment: Object.keys(environment).length > 0 ? environment : undefined, - volumes: volumes.length > 0 ? volumes : undefined, + environment, networks: [ 'shared' ] } } @@ -64,7 +61,11 @@ RUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo` protected async isApplicationReady(): Promise { try { const response = await axios.get(`${this.baseUrl}/setup/setupGeneralConfiguration.action`, { validateStatus: () => true }).catch(() => null); - return response?.status === 200; + if (response?.status === 200) { + console.log(`The application ${this.name} is ready on ${this.baseUrl} 🎉`); + return true; + } + return false } catch (err) { return false; } @@ -75,20 +76,16 @@ RUN chown -R bamboo:bamboo /var/atlassian/application-data/bamboo` private getEnvironmentVariables() { return { ...this.options.contextPath ? { 'ATL_TOMCAT_CONTEXTPATH': this.options.contextPath } : '', + ...this.options.xms ? { 'JVM_MINIMUM_MEMORY': this.options.xms } : '', + ...this.options.xmx ? { 'JVM_MAXIMUM_MEMORY': this.options.xmx } : '', 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs().join(' '), 'ATL_BAMBOO_ENABLE_UNATTENDED_SETUP': 'true', - 'ATL_LICENSE': this.options.license || timebomb.bamboo, + 'ATL_LICENSE': timebomb.bamboo, '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}`, + 'ATL_DB_TYPE': `${this.database.options.name}`, } }; - private getVolumes() { - return [ - ...this.options.quickReload ? [ `${this.options.quickReload}:/opt/quickreload` ] : '' - ]; - } - } \ No newline at end of file diff --git a/src/applications/base.ts b/src/applications/base.ts index eded3e0..23ae6bc 100644 --- a/src/applications/base.ts +++ b/src/applications/base.ts @@ -1,7 +1,6 @@ import axios from 'axios'; import { spawn } from 'child_process'; -import { downAll, execCompose, ps, stop, upAll } from 'docker-compose/dist/v2.js'; -import EventEmitter from 'events'; +import { downAll, ps, stop, upAll } from 'docker-compose/dist/v2.js'; import { gracefulExit } from 'exit-hook'; import { existsSync, mkdirSync } from 'fs'; import { dump } from 'js-yaml'; @@ -10,29 +9,24 @@ import { join } from 'path'; import { cwd } from 'process'; import simpleGit from 'simple-git'; -import { MSSQL } from '../databases/mssql'; -import { MySQL } from '../databases/mysql'; -import { Postgres } from '../databases/postgres'; +import { getDatabaseEngine } from '../helpers/getDatabaseEngine'; +import { getZodDefaults } from '../helpers/getZodDefaults'; import { network } from '../helpers/network'; -import { ApplicationOptions } from '../types/ApplicationOptions'; -import { DatabaseEngine } from '../types/DatabaseEngine'; +import { Application,TApplicationOptions,TSupportedApplications } from '../types/Application'; +import { DatabaseEngine, DatabaseOptions, MSSQLOptions, MySQLOptions, PostgreSQLOptions, SupportedDatabaseEngines, TSupportedDatabaseEngines } from '../types/Database'; import { DockerComposeV3, Service } from '../types/DockerComposeV3'; -import { SupportedApplications } from '../types/SupportedApplications'; -import { SupportedDatabaseEngines } from '../types/SupportedDatabaseEngines'; const basedir = join(homedir(),'.dcdx'); -export abstract class Base extends EventEmitter { +export abstract class Base implements Application { - abstract get name(): SupportedApplications; + abstract get name(): TSupportedApplications; abstract get database(): DatabaseEngine; abstract get logFilePath(): string; // ------------------------------------------------------------------------------------------ Constructor - constructor(protected options: ApplicationOptions) { - super(); - } + constructor(protected options: TApplicationOptions) {} // ------------------------------------------------------------------------------------------ Properties @@ -46,20 +40,12 @@ export abstract class Base extends EventEmitter { // ------------------------------------------------------------------------------------------ Public Methods - getDatabaseEngine(name: SupportedDatabaseEngines): DatabaseEngine { - switch (name) { - case 'postgresql': return new Postgres(); - case 'mssql': return new MSSQL(); - case 'mysql': return new MySQL(); - } - } - - async start() { + async start(): Promise { if (this.options.clean) { await this.down(); } - await this.build(this.options.version); + await this.build(this.options.tag); await this.database.start(this.options.clean); await this.up(); } @@ -71,33 +57,18 @@ export abstract class Base extends EventEmitter { } else { const configAsString = dump(this.getDockerComposeConfig()); await stop({ - cwd: cwd(), + cwd: this.options.cwd || cwd(), configAsString, log: true }); } - this.emit(`${this.name}:stopped`); } - async reset() { + async reset(): Promise { await this.database.stop(true); await this.down(); } - async cp(filename: string) { - const service = await this.getServiceState(); - const isRunning = service && service.state.toLowerCase().startsWith('up'); - if (isRunning) { - const config = this.getDockerComposeConfig(); - const configAsString = dump(config); - await execCompose('cp', [ filename, `${this.name}:/opt/quickreload/` ], { - cwd: cwd(), - configAsString, - log: false - }); - } - } - // ------------------------------------------------------------------------------------------ Protected Methods protected abstract getService(): Service; @@ -106,12 +77,12 @@ export abstract class Base extends EventEmitter { const JVM_SUPPORT_RECOMMENDED_ARGS = []; JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dupm.plugin.upload.enabled=true'); - if (this.options.devMode) { + if (this.options.debug) { JVM_SUPPORT_RECOMMENDED_ARGS.push('-Djira.dev.mode=true'); JVM_SUPPORT_RECOMMENDED_ARGS.push('-Datlassian.dev.mode=true'); } - if (this.options.quickReload) { + if (this.options.watch) { JVM_SUPPORT_RECOMMENDED_ARGS.push('-Dquickreload.dirs=/opt/quickreload'); } @@ -122,7 +93,6 @@ export abstract class Base extends EventEmitter { protected async isApplicationReady(): Promise { try { const response = await axios.get<{ state: string }>(`${this.baseUrl}/status`, { validateStatus: () => true }).catch(() => null); - if (response) { if (response.status === 200) { const { data } = response; @@ -139,6 +109,14 @@ export abstract class Base extends EventEmitter { } } + protected getDatabaseEngine(name: TSupportedDatabaseEngines): DatabaseEngine { + return getDatabaseEngine(DatabaseOptions.parse({ + ...this.getDefaultOptions(name), + name, + cwd: this.options.cwd + })); + } + // ------------------------------------------------------------------------------------------ Private Methods private getDockerComposeConfig(): DockerComposeV3 { @@ -158,27 +136,27 @@ export abstract class Base extends EventEmitter { const configAsString = dump(config); await upAll({ - cwd: cwd(), + cwd: this.options.cwd || cwd(), configAsString, log: true, }); - this.emit(`${this.name}:up`); const isAvailable = await this.waitUntilReady(); if (!isAvailable) { console.log(`Failed to start ${this.name} ⛔`); } else { - this.emit(`${this.name}:ready`); await this.tailApplicationLogs(); } - gracefulExit(0); + // We are exiting with an error code + // This is to trigger a graceful shut down of the application + gracefulExit(1); } private async down() { const configAsString = dump(this.getDockerComposeConfig()); await downAll({ - cwd: cwd(), + cwd: this.options.cwd || cwd(), configAsString, commandOptions: [ '-v', '--remove-orphans', '--rmi', 'local' ], log: true @@ -259,7 +237,7 @@ export abstract class Base extends EventEmitter { const docker = spawn( 'docker', [ 'logs', '-f', '-n', '5000', service ], - { cwd: cwd(), stdio: 'inherit' } + { cwd: this.options.cwd || cwd(), stdio: 'inherit' } ); docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); }); @@ -270,10 +248,21 @@ export abstract class Base extends EventEmitter { const docker = spawn( 'docker', [ 'exec', '-i', service, `tail`, `-F`, `-n`, `5000`, this.logFilePath ], - { cwd: cwd(), stdio: 'inherit' } + { cwd: this.options.cwd || cwd(), stdio: 'inherit' } ); docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); }); } + private getDefaultOptions(name: TSupportedDatabaseEngines) { + switch (name) { + case SupportedDatabaseEngines.Values.postgresql: + return getZodDefaults(PostgreSQLOptions); + case SupportedDatabaseEngines.Values.mysql: + return getZodDefaults(MySQLOptions); + case SupportedDatabaseEngines.Values.mssql: + return getZodDefaults(MSSQLOptions); + } + } + } \ No newline at end of file diff --git a/src/applications/bitbucket.ts b/src/applications/bitbucket.ts index cadcccf..8b4a272 100644 --- a/src/applications/bitbucket.ts +++ b/src/applications/bitbucket.ts @@ -1,21 +1,20 @@ import { timebomb } from '../helpers/licences'; import { toAbsolutePath } from '../helpers/toAbsolutePath'; -import { ApplicationOptions } from '../types/ApplicationOptions'; -import { DatabaseEngine } from '../types/DatabaseEngine'; +import { SupportedApplications, TApplicationOptions } from '../types/Application'; +import { DatabaseEngine } from '../types/Database'; import { Service } from '../types/DockerComposeV3'; -import { SupportedApplications } from '../types/SupportedApplications'; import { Base } from './base'; export class Bitbucket extends Base { - name = SupportedApplications.BITBUCKET; + name = SupportedApplications.Values.bitbucket; database: DatabaseEngine; logFilePath = '/var/atlassian/application-data/bitbucket/log/atlassian-bitbucket.log'; // ------------------------------------------------------------------------------------------ Constructor - constructor(options: ApplicationOptions) { + constructor(options: TApplicationOptions) { super(options); this.database = this.getDatabaseEngine(options.database); } @@ -23,15 +22,13 @@ export class Bitbucket extends Base { // ------------------------------------------------------------------------------------------ Protected Methods protected getService(): Service { - - const volumes = this.getVolumes(); const environment = this.getEnvironmentVariables(); return { build: { context: toAbsolutePath('../../assets'), dockerfile_inline: ` -FROM dcdx/${this.name}:${this.options.version} +FROM dcdx/${this.name}:${this.options.tag} COPY ./quickreload-5.0.4.jar /var/atlassian/application-data/bitbucket/plugins/installed-plugins/quickreload-5.0.4.jar COPY ./mysql-connector-j-8.3.0.jar /var/atlassian/application-data/bitbucket/lib/mysql-connector-j-8.3.0.jar RUN echo "/opt/quickreload" > /var/atlassian/application-data/bitbucket/quickreload.properties; \ @@ -40,7 +37,7 @@ RUN echo "/opt/quickreload" > /var/atlassian/application-data/bitbucket/quickrel RUN mkdir -p /var/atlassian/application-data/bitbucket/shared; \ touch /var/atlassian/application-data/bitbucket/shared/bitbucket.properties; \ - echo "setup.license=${this.options.license || timebomb.bitbucket}" >> /var/atlassian/application-data/bitbucket/shared/bitbucket.properties; + echo "setup.license=${timebomb.bitbucket}" >> /var/atlassian/application-data/bitbucket/shared/bitbucket.properties; RUN chown -R bitbucket:bitbucket /var/atlassian/application-data/bitbucket` }, @@ -48,8 +45,7 @@ RUN chown -R bitbucket:bitbucket /var/atlassian/application-data/bitbucket` `${this.options.port || 80}:7990`, ...this.options.debug ? [ '5005:5005' ] : [], ], - environment: Object.keys(environment).length > 0 ? environment : undefined, - volumes: volumes.length > 0 ? volumes : undefined, + environment, networks: [ 'shared' ] } } @@ -68,18 +64,14 @@ RUN chown -R bitbucket:bitbucket /var/atlassian/application-data/bitbucket` private getEnvironmentVariables() { return { + ...this.options.xms ? { 'JVM_MINIMUM_MEMORY': this.options.xms } : '', + ...this.options.xmx ? { 'JVM_MAXIMUM_MEMORY': this.options.xmx } : '', 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs().join(' '), 'JDBC_URL': this.database.url, 'JDBC_USER': this.database.options.username, 'JDBC_PASSWORD': this.database.options.password, - 'JDBC_DRIVER': `${this.database.driver}`, + 'JDBC_DRIVER': `${this.database.options.driver}`, } }; - private getVolumes() { - return [ - ...this.options.quickReload ? [ `${this.options.quickReload}:/opt/quickreload` ] : '' - ]; - } - } \ No newline at end of file diff --git a/src/applications/confluence.ts b/src/applications/confluence.ts index 36520b8..8787f6d 100644 --- a/src/applications/confluence.ts +++ b/src/applications/confluence.ts @@ -1,21 +1,20 @@ import { timebomb } from '../helpers/licences'; import { toAbsolutePath } from '../helpers/toAbsolutePath'; -import { ApplicationOptions } from '../types/ApplicationOptions'; -import { DatabaseEngine } from '../types/DatabaseEngine'; +import { SupportedApplications,TApplicationOptions } from '../types/Application'; +import { DatabaseEngine } from '../types/Database'; import { Service } from '../types/DockerComposeV3'; -import { SupportedApplications } from '../types/SupportedApplications'; import { Base } from './base'; export class Confluence extends Base { - name = SupportedApplications.CONFLUENCE; + name = SupportedApplications.Values.confluence; database: DatabaseEngine; logFilePath = '/var/atlassian/application-data/confluence/logs/atlassian-confluence.log'; // ------------------------------------------------------------------------------------------ Constructor - constructor(options: ApplicationOptions) { + constructor(options: TApplicationOptions) { super(options); this.database = this.getDatabaseEngine(options.database); } @@ -24,14 +23,13 @@ export class Confluence extends Base { protected getService(): Service { - const volumes = this.getVolumes(); const environment = this.getEnvironmentVariables(); return { build: { context: toAbsolutePath('../../assets'), dockerfile_inline: ` -FROM dcdx/${this.name}:${this.options.version} +FROM dcdx/${this.name}:${this.options.tag} COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/confluence/confluence/WEB-INF/lib/mysql-connector-j-8.3.0.jar COPY ./quickreload-5.0.4.jar /opt/atlassian/confluence/confluence/WEB-INF/atlassian-bundled-plugins/quickreload-5.0.4.jar RUN echo "/opt/quickreload" > /var/atlassian/application-data/confluence/quickreload.properties; \ @@ -44,8 +42,7 @@ RUN chown -R confluence:confluence /opt/atlassian/confluence` `${this.options.port || 80}:8090`, ...this.options.debug ? [ '5005:5005' ] : [], ], - environment: Object.keys(environment).length > 0 ? environment : undefined, - volumes: volumes.length > 0 ? volumes : undefined, + environment, networks: [ 'shared' ] } } @@ -68,19 +65,15 @@ RUN chown -R confluence:confluence /opt/atlassian/confluence` private getEnvironmentVariables() { return { ...this.options.contextPath ? { 'ATL_TOMCAT_CONTEXTPATH': this.options.contextPath } : '', + ...this.options.xms ? { 'JVM_MINIMUM_MEMORY': this.options.xms } : '', + ...this.options.xmx ? { 'JVM_MAXIMUM_MEMORY': this.options.xmx } : '', 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs().join(' '), - 'ATL_LICENSE_KEY': this.options.license || timebomb.confluence, + 'ATL_LICENSE_KEY': timebomb.confluence, '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}`, + 'ATL_DB_TYPE': `${this.database.options.name}`, } }; - private getVolumes() { - return [ - ...this.options.quickReload ? [ `${this.options.quickReload}:/opt/quickreload` ] : '' - ]; - } - } \ No newline at end of file diff --git a/src/applications/jira.ts b/src/applications/jira.ts index 14d7b07..73ca567 100644 --- a/src/applications/jira.ts +++ b/src/applications/jira.ts @@ -1,21 +1,20 @@ import { timebomb } from '../helpers/licences'; import { toAbsolutePath } from '../helpers/toAbsolutePath'; -import { ApplicationOptions } from '../types/ApplicationOptions'; -import { DatabaseEngine } from '../types/DatabaseEngine'; +import { SupportedApplications,TApplicationOptions } from '../types/Application'; +import { DatabaseEngine } from '../types/Database'; import { Service } from '../types/DockerComposeV3'; -import { SupportedApplications } from '../types/SupportedApplications'; import { Base } from './base'; export class Jira extends Base { - name = SupportedApplications.JIRA; + name = SupportedApplications.Values.jira; database: DatabaseEngine; logFilePath = '/var/atlassian/application-data/jira/log/atlassian-jira.log'; // ------------------------------------------------------------------------------------------ Constructor - constructor(options: ApplicationOptions) { + constructor(options: TApplicationOptions) { super(options); this.database = this.getDatabaseEngine(options.database); } @@ -23,14 +22,13 @@ export class Jira extends Base { // ------------------------------------------------------------------------------------------ Protected Methods protected getService(): Service { - const volumes = this.getVolumes(); const environment = this.getEnvironmentVariables(); return { build: { context: toAbsolutePath('../../assets'), dockerfile_inline: ` -FROM dcdx/${this.name}:${this.options.version} +FROM dcdx/${this.name}:${this.options.tag} COPY ./jira-data-generator-5.0.0.jar /var/atlassian/application-data/jira/plugins/installed-plugins/jira-data-generator-5.0.0.jar COPY ./mysql-connector-j-8.3.0.jar /opt/atlassian/jira/lib/mysql-connector-j-8.3.0.jar COPY ./quickreload-5.0.4.jar /var/atlassian/application-data/jira/plugins/installed-plugins/quickreload-5.0.4.jar @@ -41,11 +39,10 @@ RUN echo "/opt/quickreload" > /var/atlassian/application-data/jira/quickreload.p RUN chown -R jira:jira /var/atlassian/application-data/jira` }, ports: [ - `${this.options.port || 80}:8080`, + `${this.options.port}:8080`, ...this.options.debug ? [ '5005:5005' ] : [], ], - environment: Object.keys(environment).length > 0 ? environment : undefined, - volumes: volumes.length > 0 ? volumes : undefined, + environment, networks: [ 'shared' ] } } @@ -64,25 +61,21 @@ RUN chown -R jira:jira /var/atlassian/application-data/jira` private getEnvironmentVariables() { // For some reason, Jira uses a different type for postgres - const dbType = this.database.name === 'postgresql' ? 'postgres72' : this.database.name; + const dbType = this.database.options.name === 'postgresql' ? 'postgres72' : this.database.options.name; return { ...this.options.contextPath ? { 'ATL_TOMCAT_CONTEXTPATH': this.options.contextPath } : '', + ...this.options.xms ? { 'JVM_MINIMUM_MEMORY': this.options.xms } : '', + ...this.options.xmx ? { 'JVM_MAXIMUM_MEMORY': this.options.xmx } : '', 'JVM_SUPPORT_RECOMMENDED_ARGS': this.getJVMArgs().join(' '), - 'ATL_LICENSE_KEY': this.options.license || timebomb.confluence, + 'ATL_LICENSE_KEY': timebomb.confluence, '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_DRIVER': this.database.options.driver, 'ATL_DB_TYPE': dbType, - 'JIRA_SETUP_LICENSE': this.options.license || timebomb.jira + 'JIRA_SETUP_LICENSE': timebomb.jira } }; - private getVolumes() { - return [ - ...this.options.quickReload ? [ `${this.options.quickReload}:/opt/quickreload` ] : '' - ]; - } - } \ No newline at end of file diff --git a/src/commands/build.ts b/src/commands/build.ts index fe83950..4aca70e 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -1,109 +1,98 @@ #!/usr/bin/env node -import { watch } from 'chokidar'; -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { resolve } from 'path'; -import { cwd } from 'process'; +import { FSWatcher } from 'chokidar'; +import { Command as Commander, InvalidOptionArgumentError, Option } from 'commander'; +import { gracefulExit } from 'exit-hook'; +import versions from '../../assets/versions.json'; +import { ActionHandler } from '../helpers/ActionHandler'; import { AMPS } from '../helpers/amps'; -import { Docker } from '../helpers/docker'; -import { getApplicationByName } from '../helpers/getApplication'; -import { isRecursiveBuild } from '../helpers/isRecursiveBuild'; -import { showRecursiveBuildWarning } from '../helpers/showRecursiveBuildWarning'; - -(async () => { - const options = program - .name('dcdx build') - .description('Build & install the Atlassian Data Center plugin from the current directory.\nYou can add Maven build arguments after the command options.') - .usage('[options] [...maven_arguments]') - .addOption(new Option('-w, --watch ', 'Additional list of glob patterns used to watch for file changes')) - .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) - .addOption(new Option('-o, --outputDirectory ', 'Output directory where QuickReload will look for generated JAR files').default('target')) - .allowUnknownOption(true) - .parse(process.argv) - .opts(); - - if (!AMPS.isAtlassianPlugin()) { - console.log('Unable to find an Atlassian Plugin project in the current directory 🤔'); - gracefulExit(); - } +import { FileWatcher } from '../helpers/FileWatcher'; +import { TBuildOptions } from '../types/AMPS'; - const application = AMPS.getApplication(); - if (!application) { - console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); - gracefulExit(); - process.exit(); - } +const program = new Commander(); - const Application = getApplicationByName(application); - if (!Application) { - console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); - process.exit(); - } +const Command = () => { + let quickReload: FSWatcher|null = null; - const mavenOpts = program.args.slice(); - if (options.activateProfiles) { - mavenOpts.push(...[ '-P', options.activateProfiles ]); - } + return { + action: async (options: TBuildOptions) => { + const amps = new AMPS({ + cwd: options.cwd, + profiles: options.activateProfiles?.split(',') || [] + }); - console.log(`Looking for running instances of ${application}`); - const containerIds = await Docker.getRunningContainerIds(application); - if (containerIds.length > 1) { - console.log(`There are multple running instance of ${application}, unable to determine which one to use`); - gracefulExit(0); - return; - } + if (!amps.isAtlassianPlugin()) { + throw new Error('Unable to find an Atlassian Plugin project in the current directory 🤔'); + } - const containerId = containerIds[0]; - if (!containerId) { - console.log(`Could not find running instance of ${application}, please make sure they are running first!`); - gracefulExit(0); - return; - } + const name = amps.getApplication(); + if (!name) { + throw new Error('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + } + + const version = amps.getApplicationVersion(); + if (!version) { + throw new Error('Failed to determine version from AMPS and no product version provided (--tag)'); + } else if (!versions[name].includes(version)) { + throw new Error(`Product version '${version}' is invalid. Allowed choices are ${versions[name].join(', ')}.`); + } - console.log('Watching filesystem for changes to source files (QuickReload)'); - let lastBuildCompleted = new Date().getTime(); - const patterns = Array.isArray(options.watch) ? options.watch : [ options.watch ]; - const quickReload = watch([ '**/*', ...patterns ], { - cwd: cwd(), - usePolling: true, - interval: 2 * 1000, - binaryInterval: 2 * 1000, - awaitWriteFinish: true, - persistent: true, - atomic: true - }); - - asyncExitHook(async () => { - console.log(`Stopping filesystem watcher... ⏳`); - await quickReload.close(); - console.log(`Successfully stopped all running processes 💪`); - }, { wait: 30 * 1000 }); - - quickReload.on('change', async (path) => { - if (path.startsWith(options.outputDirectory) && path.toLowerCase().endsWith('.jar')) { - console.log('Found updated JAR file, uploading them to QuickReload'); - await Docker.copy(resolve(path), `${containerId}:/opt/quickreload/`) - .then(() => console.log('Finished uploading JAR file to QuickReload')) - .catch(err => console.log('Failed to upload JAR file to QuickReload', err)); - lastBuildCompleted = new Date().getTime(); - } else if (!path.startsWith(options.outputDirectory)) { - if (isRecursiveBuild(lastBuildCompleted)) { - showRecursiveBuildWarning(options.outputDirectory); - } else { - console.log('Detected file change, rebuilding Atlasian Plugin for QuickReload'); - await AMPS.build(mavenOpts).catch(() => Promise.resolve()); + if (!options.watch && options.ext) { + throw new InvalidOptionArgumentError('Invalid argument "--ext"'); + } else if (!options.watch && options.install) { + throw new InvalidOptionArgumentError('Invalid argument "--install"'); + } else if (!options.install && options.outputDirectory) { + throw new InvalidOptionArgumentError('Invalid argument "--outputDirectory"'); } + + const mavenOpts = program.args.slice(); + if (options.activateProfiles) { + mavenOpts.push(...[ '-P', options.activateProfiles ]); + } + + if (options.watch) { + quickReload = FileWatcher(name, options, mavenOpts); + } + + console.log(`Building Atlassian Data Center plugin for ${name}... 💃`); + await amps.build(mavenOpts).then(() => { + console.log(`Finished building Atlassian Data Center plugin for ${name}... 💪`); + }); + }, + errorHandler: async () => { + if (quickReload) { + console.log(`Stopping filesystem watcher... ⏳`); + await quickReload.close().catch(() => null); + } + console.log(`Successfully stopped all running processes 💪`); } - }); + } +} + +program + .name('dcdx build') + .description( +`Build the Atlassian Data Center plugin using the Atlassian Maven Plugin Suite (AMPS) configuration. +If there is a running instance, it will try to install the plugin using QuickReload. - await AMPS.build(mavenOpts).catch(() => Promise.resolve()); - lastBuildCompleted = new Date().getTime(); -})(); +You can add Maven build arguments after the command options.`) + .usage('[options] [...maven_arguments]') + .allowUnknownOption(true) + .showHelpAfterError(true) + .addOption(new Option('-w, --watch', 'Watch for filesystem changes in the current working directory and rebuild plugin').default(false)) + .addOption(new Option('--ext ', 'Glob patterns to use when watching for file changes (only available with --watch, defaults to **/*)')) + .addOption(new Option('-i, --install', 'Install the plugin into a running instance of the host application (only available with --watch)')) + .addOption(new Option('-o, --outputDirectory ', 'Output directory where to look for generated JAR files (only available with --install, defaults to `target`)')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('--cwd ', 'Specify the working directory where to find the AMPS configuration')) + .action(options => ActionHandler(program, Command(), options)); + +program.parseAsync(process.argv).catch(() => gracefulExit(1)); process.on('SIGINT', () => { console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); + gracefulExit(1); }); + diff --git a/src/commands/database.ts b/src/commands/database.ts index 4376bc8..c4f7e66 100644 --- a/src/commands/database.ts +++ b/src/commands/database.ts @@ -1,13 +1,84 @@ #!/usr/bin/env node -import { program } from 'commander'; +import { Command as Commander, Option } from 'commander'; +import { gracefulExit } from 'exit-hook'; + +import versions from '../../assets/versions.json'; +import { ActionHandler } from '../helpers/ActionHandler'; +import { getDatabaseEngine } from '../helpers/getDatabaseEngine'; +import { getZodDefault } from '../helpers/getZodDefaults'; +import { DatabaseEngine, MSSQLOptions, MySQLOptions, PostgreSQLOptions, SupportedDatabaseEngines, SupportedMSSQLEditions, TDatabaseOptions } from '../types/Database'; + +const program = new Commander(); + +const Command = () => { + let instance: DatabaseEngine|null = null; + + return { + action: async (options: TDatabaseOptions) => { + instance = getDatabaseEngine(options); + return instance.start(); + }, + errorHandler: async () => { + if (instance) { + const { name } = instance.options; + console.log(`Stopping ${name}... ⏳`); + await instance.stop().then(() => { + console.log(`Stopped ${name} 💪`); + }).catch(() => { + console.log(`Failed to stopped ${name}, manual action is required`); + }); + } + } + } +} program .name('dcdx database') - .command('postgresql', 'Start PostgreSQL', { executableFile: './database/postgres.js'}) - .command('mysql', 'Start MySQL', { executableFile: './database/mysql.js'}) - .command('mssql', 'Start Microsoft SQL Server', { executableFile: './database/mssql.js'}) .showHelpAfterError(true); -program.parse(); +program + .command('postgresql') + .description('Start PostgreSQL') + .addOption(new Option('-t, --tag ', 'The Docker tag of Postgres').choices(versions[SupportedDatabaseEngines.Values.postgresql]).default('latest')) + .addOption(new Option('-d, --database ', 'The value passed to POSTGRES_DB environment variable').default(getZodDefault(PostgreSQLOptions, 'database'))) + .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default(getZodDefault(PostgreSQLOptions, 'port'))) + .addOption(new Option('-U, --username ', 'The value passed to POSTGRES_USER environment variable').default(getZodDefault(PostgreSQLOptions, 'username'))) + .addOption(new Option('-P, --password ', 'The value passed to POSTGRES_PASSWORD environment variable').default(getZodDefault(PostgreSQLOptions, 'password'))) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .addOption(new Option('--verbose', 'Output database log files').default(false)) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedDatabaseEngines.Values.postgresql })); + +program + .command('mysql') + .description('Start MySQL') + .addOption(new Option('-t, --tag ', 'The Docker tag of MySQL').choices(versions[SupportedDatabaseEngines.Values.mysql]).default('latest')) + .addOption(new Option('-d, --database ', 'The value passed to MYSQL_DATABASE environment variable').default(getZodDefault(MySQLOptions, 'database'))) + .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default(getZodDefault(MySQLOptions, 'port'))) + .addOption(new Option('-U, --username ', 'The value passed to MYSQL_USER environment variable').default(getZodDefault(MySQLOptions, 'username'))) + .addOption(new Option('-P, --password ', 'The value passed to MYSQL_PASSWORD environment variable').default(getZodDefault(MySQLOptions, 'password'))) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .addOption(new Option('--verbose', 'Output database log files').default(false)) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedDatabaseEngines.Values.mysql })); + +program + .command('mssql') + .description('Start Microsoft SQL Server') + .addOption(new Option('-t, --tag ', 'The Docker tag of Microsoft SQL Server').choices(versions[SupportedDatabaseEngines.Values.mssql]).default('latest')) + .addOption(new Option('-e, --edition ', 'The edition of Microsoft SQL Server').choices(Object.values(SupportedMSSQLEditions.Values)).default(SupportedMSSQLEditions.Values.Developer)) + .addOption(new Option('-d, --database ', 'The name of the database (automatically created)').default(getZodDefault(MSSQLOptions, 'database'))) + .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default(getZodDefault(MSSQLOptions, 'port'))) + .addOption(new Option('-P, --password ', 'The value passed to MSSQL_SA_PASSWORD environment variable. MS SQL Server password policy applies.').default(getZodDefault(MSSQLOptions, 'password'))) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .addOption(new Option('--verbose', 'Output database log files').default(false)) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedDatabaseEngines.Values.mssql, username: 'sa' })); + +program.parseAsync(process.argv).catch(() => gracefulExit(1)); +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); diff --git a/src/commands/database/mssql.ts b/src/commands/database/mssql.ts deleted file mode 100644 index 0f97d21..0000000 --- a/src/commands/database/mssql.ts +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env node - -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; - -import { mssql as versions } from '../../../assets/versions.json'; -import { MSSQL, MSSQLOptions } from '../../databases/mssql'; - -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of Microsoft SQL Server').choices(versions).default('latest')) - .addOption(new Option('-e, --edition ', 'The edition of Microsoft SQL Server').choices([ 'Developer', 'Express', 'Standard', 'Enterprise', 'EnterpriseCore' ]).default('Developer')) - .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('1433')) - .addOption(new Option('-P, --password ', 'The value passed to MSSQL_SA_PASSWORD environment variable. MS SQL Server password policy applies.').default('DataCenterDX!')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .parse(process.argv) - .opts(); - - const instance = new MSSQL({ - version: options.version, - edition: options.edition, - port: Number(options.port), - password: options.password, - clean: options.clean, - prune: options.prune, - logging: true - } as MSSQLOptions); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - await instance.start(); -})(); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); - -// Keep the application running until it is terminated by SIGINT -setInterval(() => {}, 1 << 30); - - diff --git a/src/commands/database/mysql.ts b/src/commands/database/mysql.ts deleted file mode 100644 index bc6a113..0000000 --- a/src/commands/database/mysql.ts +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env node - -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; - -import { mysql as versions } from '../../../assets/versions.json'; -import { MySQL } from '../../databases/mysql'; - -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of MySQL').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The value passed to MYSQL_DATABASE environment variable').default('dcdx')) - .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('3306')) - .addOption(new Option('-U, --username ', 'The value passed to MYSQL_USER environment variable').default('dcdx')) - .addOption(new Option('-P, --password ', 'The value passed to MYSQL_PASSWORD environment variable').default('dcdx')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .parse(process.argv) - .opts(); - - const instance = new MySQL({ - version: options.version, - database: options.database, - port: Number(options.port), - username: options.username, - password: options.password, - clean: options.clean, - prune: options.prune, - logging: true - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - await instance.start(); -})(); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); - -// Keep the application running until it is terminated by SIGINT -setInterval(() => {}, 1 << 30); - - diff --git a/src/commands/database/postgres.ts b/src/commands/database/postgres.ts deleted file mode 100644 index d3a5ad0..0000000 --- a/src/commands/database/postgres.ts +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env node - -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; - -import { postgres as versions } from '../../../assets/versions.json'; -import { Postgres } from '../../databases/postgres'; - -(async () => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of Postgres').choices(versions).default('latest')) - .addOption(new Option('-d, --database ', 'The value passed to POSTGRES_DB environment variable').default('dcdx')) - .addOption(new Option('-p, --port ', 'The port on which the database will be accessible').default('5432')) - .addOption(new Option('-U, --username ', 'The value passed to POSTGRES_USER environment variable').default('dcdx')) - .addOption(new Option('-P, --password ', 'The value passed to POSTGRES_PASSWORD environment variable').default('dcdx')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .parse(process.argv) - .opts(); - - const instance = new Postgres({ - version: options.version, - database: options.database, - port: Number(options.port), - username: options.username, - password: options.password, - clean: options.clean, - prune: options.prune, - logging: true - }) - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - await instance.start(); -})(); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); - -// Keep the application running until it is terminated by SIGINT -setInterval(() => {}, 1 << 30); - - diff --git a/src/commands/debug.ts b/src/commands/debug.ts new file mode 100644 index 0000000..e6e0c18 --- /dev/null +++ b/src/commands/debug.ts @@ -0,0 +1,122 @@ +#!/usr/bin/env node + +import { FSWatcher } from 'chokidar'; +import { Command as Commander, InvalidOptionArgumentError, Option } from 'commander'; +import { gracefulExit } from 'exit-hook'; + +import versions from '../../assets/versions.json'; +import { ActionHandler } from '../helpers/ActionHandler'; +import { AMPS } from '../helpers/amps'; +import { FileWatcher } from '../helpers/FileWatcher'; +import { getApplication } from '../helpers/getApplication'; +import { TDebugOptions } from '../types/AMPS'; +import { Application } from '../types/Application'; +import { SupportedDatabaseEngines } from '../types/Database'; + +const program = new Commander(); + +const Command = () => { + let instance: Application|null = null; + let quickReload: FSWatcher|null = null; + + return { + action: (options: TDebugOptions) => { + return new Promise((resolve, reject) => { + const amps = new AMPS({ + cwd: options.cwd, + profiles: options.activateProfiles?.split(',') || [] + }); + + if (!amps.isAtlassianPlugin()) { + throw new Error('Unable to find an Atlassian Plugin project in the current directory 🤔'); + } + + const name = amps.getApplication(); + if (!name) { + throw new Error('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + } + + if (!options.tag) { + const version = amps.getApplicationVersion(); + if (!version) { + throw new Error('Failed to determine version from AMPS and no product version provided (--tag)'); + } else { + options.tag = version; + } + } + + if (!versions[name].includes(options.tag)) { + throw new Error(`Product version '${options.tag}' is invalid. Allowed choices are ${versions[name].join(', ')}.`); + } + + if (!options.watch && options.ext) { + throw new InvalidOptionArgumentError('Invalid argument "--ext"'); + } else if (!options.watch && options.install) { + throw new InvalidOptionArgumentError('Invalid argument "--install"'); + } else if (!options.install && options.outputDirectory) { + throw new InvalidOptionArgumentError('Invalid argument "--outputDirectory"'); + } else if (!options.watch && options.activateProfiles) { + throw new InvalidOptionArgumentError('Invalid argument "--activate-profiles"'); + } else if (!options.watch && options.cwd) { + throw new InvalidOptionArgumentError('Invalid argument "--cwd"'); + } + + if (options.watch) { + const mavenOpts = program.args.slice(); + mavenOpts.push(...options.activateProfiles ? [ '-P', options.activateProfiles ] : []); + quickReload = FileWatcher(name, options, mavenOpts); + } + + instance = getApplication({ ...options, name }); + console.log(`Starting ${instance.name}... 💃`); + instance.start().then(resolve).catch(reject); + }); + }, + errorHandler: async () => { + if (quickReload) { + console.log(`Stopping filesystem watcher... ⏳`); + await quickReload.close(); + } + + if (instance) { + console.log(`Stopping ${instance.name}... 💔`); + await instance.stop().catch(() => null); + } + + console.log(`Successfully stopped all running processes 💪`); + } + } +} + +program + .name('dcdx debug') + .description( +`Start the host application in dev mode based on the Atlassian Maven Plugin Suite (AMPS) configuration. +AMPS configuration can be overridden by using any of the command-line options below. + +You can add Maven build arguments after the command options`) + .usage('[options] [...maven_arguments]') + .addOption(new Option('-t, --tag ', 'The Docker tag of the host application')) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) + .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) + .addOption(new Option('--xms ', 'JVM minimum heap size').default('1024m')) + .addOption(new Option('--xmx ', 'JVM maximum heap size').default('1024m')) + .addOption(new Option('-w, --watch', 'Watch for filesystem changes in the current working directory and rebuild plugin').default(false)) + .addOption(new Option('--ext ', 'Glob patterns to use when watching for file changes (only available with --watch, defaults to **/*)')) + .addOption(new Option('-i, --install', 'Install the plugin into a running instance of the host application (only available with --watch)')) + .addOption(new Option('-o, --outputDirectory ', 'Output directory where to look for generated JAR files (only available with --install, defaults to `target`)')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available with --watch)')) + .addOption(new Option('--cwd ', 'Specify the working directory where to find the AMPS configuration (only available with --watch)')) + .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) + .action(options => ActionHandler(program, Command(), { ...options, debug: true })) + .allowUnknownOption(true) + .showHelpAfterError(true); + +program.parseAsync(process.argv).catch(() => gracefulExit(1)); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); diff --git a/src/commands/reset.ts b/src/commands/reset.ts index 7b74420..506d06b 100644 --- a/src/commands/reset.ts +++ b/src/commands/reset.ts @@ -1,28 +1,131 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; +import { Command as Commander,InvalidOptionArgumentError,Option } from 'commander'; +import { gracefulExit } from 'exit-hook'; +import versions from '../../assets/versions.json'; +import { ActionHandler } from '../helpers/ActionHandler'; import { AMPS } from '../helpers/amps'; -import { SupportedApplications } from '../types/SupportedApplications'; - -// Check if there is a command in the arguments -const isDefaultCommand = !process.argv.some(item => Object.values(SupportedApplications).includes(item as SupportedApplications)); -// If there is no command, check if we are running this within the context of an Atlassian Plugin project -if (isDefaultCommand) { - const application = AMPS.getApplication(); - if (application) { - const args = [ application, ...process.argv.splice(2) ]; - process.argv = [ ...process.argv.slice(0, 2), ...args ]; +import { getApplication } from '../helpers/getApplication'; +import { showHelpWithDefaultCommandOptions } from '../helpers/showHelpWithDefaultCommandOptions'; +import { Application, SupportedApplications, TApplicationOptions } from '../types/Application'; +import { SupportedDatabaseEngines } from '../types/Database'; + +const program = new Commander(); + +const Command = () => { + let instance: Application|null = null; + return { + action: async (options: TApplicationOptions) => { + const amps = new AMPS({ + cwd: options.cwd, + profiles: options.activateProfiles?.split(',') || [] + }); + + if (options.name && options.tag && options.activateProfiles) { + throw new InvalidOptionArgumentError('Invalid argument "--activate-profiles"'); + } else if (options.name && options.tag && options.cwd) { + throw new InvalidOptionArgumentError('Invalid argument "--cwd"'); + } + + if (!options.name && !amps.isAtlassianPlugin()) { + throw new Error('Unable to find an Atlassian Plugin project in the current directory 🤔'); + } + + options.name = options.name || amps.getApplication(); + if (!options.name) { + throw new Error('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + } + + if (!options.tag) { + const version = amps.getApplicationVersion(); + if (!version) { + throw new Error('Failed to determine version from AMPS and no product version provided (--tag)'); + } else { + options.tag = version; + } + } + + if (!versions[options.name].includes(options.tag)) { + throw new Error(`Product version '${options.tag}' is invalid. Allowed choices are ${versions[options.name].join(', ')}.`); + } + + instance = getApplication(options); + console.log(`Removing ${options.name} and deleting all data... 💔`); + return instance.reset().then(() => { + console.log(`Removed ${options.name} and deleted all data 💪`); + }); + }, + errorHandler: async (options: TApplicationOptions) => { + if (instance) { + console.log(`Removing ${options.name} and deleting all data... 💔`); + await instance.reset().catch(() => null); + console.log(`Removed ${options.name} and deleted all data 💪`); + } + } } } program .name('dcdx reset') - .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) - .command('bamboo', 'Remove all data (incl. database) for Atlassian Bamboo (standalone)', { executableFile: './reset/bamboo.js'}) - .command('bitbucket', 'Remove all data (incl. database) for Atlassian Bitbucket (standalone)', { executableFile: './reset/bitbucket.js'}) - .command('confluence', 'Remove all data (incl. database) for Atlassian Confluence (standalone)', { executableFile: './reset/confluence.js'}) - .command('jira', 'Remove all data (incl. database) for Atlassian Jira (standalone)', { executableFile: './reset/jira.js'}) + .description( +`Reset the host application based on the Atlassian Maven Plugin Suite (AMPS) configuration. +If you wish to reset a specific host application running standalone, call this command with 'dcdx reset ' + +IMPORTANT: This will remove all data from host application and database! +If you wish to preserve the data, please use the 'dcdx stop' command instead`) + .addHelpText('afterAll','​') // there is a non-zero white space because the only reason to have this is to force a line break + .configureHelp(showHelpWithDefaultCommandOptions) + .allowUnknownOption(false) + .allowExcessArguments(false) .showHelpAfterError(true); -program.parse(process.argv); \ No newline at end of file +program + .command('fromAMPS', { isDefault: true, hidden: true }) + .addOption(new Option('-d, --database ', 'The database engine to reset').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('--cwd ', 'Specify the working directory')) + .action(options => ActionHandler(program, Command(), options)); + +program + .command('bamboo') + .description('Reset Atlassian Bamboo (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Bamboo').choices(versions[SupportedApplications.Values.bamboo]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine to reset').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available without --tag)')) + .addOption(new Option('--cwd ', 'Specify the working directory (only available without --tag)')) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.bamboo })); + +program + .command('bitbucket') + .description('Reset Atlassian Bitbucket (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Bitbucket').choices(versions[SupportedApplications.Values.bitbucket]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine to reset').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available without --tag)')) + .addOption(new Option('--cwd ', 'Specify the working directory (only available without --tag)')) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.bitbucket })); + +program + .command('confluence') + .description('Reset Atlassian Confluence (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Confluence').choices(versions[SupportedApplications.Values.confluence]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine to reset').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available without --tag)')) + .addOption(new Option('--cwd ', 'Specify the working director (only available without --tag)')) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.confluence })); + +program + .command('jira') + .description('Reset Atlassian Jira (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Jira').choices(versions[SupportedApplications.Values.jira]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine to reset').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available without --tag)')) + .addOption(new Option('--cwd ', 'Specify the working directory (only available without --tag)')) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.jira })); + +program.parseAsync(process.argv).catch(() => gracefulExit(1)); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); \ No newline at end of file diff --git a/src/commands/reset/bamboo.ts b/src/commands/reset/bamboo.ts deleted file mode 100644 index 0b9515c..0000000 --- a/src/commands/reset/bamboo.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { ResetCommand } from './command'; - -ResetCommand(SupportedApplications.BAMBOO); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/reset/bitbucket.ts b/src/commands/reset/bitbucket.ts deleted file mode 100644 index 1373b7d..0000000 --- a/src/commands/reset/bitbucket.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { ResetCommand } from './command'; - -ResetCommand(SupportedApplications.BITBUCKET); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/reset/command.ts b/src/commands/reset/command.ts deleted file mode 100644 index 9cafd48..0000000 --- a/src/commands/reset/command.ts +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env node - -import { Option, program } from 'commander'; - -import versions from '../../../assets/versions.json'; -import { AMPS } from '../../helpers/amps'; -import { getApplicationByName } from '../../helpers/getApplication'; -import { SupportedApplications } from '../../types/SupportedApplications'; - -const version = AMPS.getApplicationVersion() || 'latest'; - -export const ResetCommand = async (name: SupportedApplications) => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-d, --database ', 'The database engine to remove data from').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions[name]).default(version)) - .parse(process.argv) - .opts(); - - const Application = getApplicationByName(name); - const instance = new Application({ - version: options.version, - database: options.database - }); - - await instance.reset(); -}; \ No newline at end of file diff --git a/src/commands/reset/confluence.ts b/src/commands/reset/confluence.ts deleted file mode 100644 index 04fbb97..0000000 --- a/src/commands/reset/confluence.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { ResetCommand } from './command'; - -ResetCommand(SupportedApplications.CONFLUENCE); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/reset/jira.ts b/src/commands/reset/jira.ts deleted file mode 100644 index 039fc5e..0000000 --- a/src/commands/reset/jira.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { ResetCommand } from './command'; - -ResetCommand(SupportedApplications.JIRA); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/run.ts b/src/commands/run.ts index fd53e30..6106e31 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -1,28 +1,165 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; +import { Command as Commander,InvalidOptionArgumentError,Option } from 'commander'; +import { gracefulExit } from 'exit-hook'; +import versions from '../../assets/versions.json'; +import { ActionHandler } from '../helpers/ActionHandler'; import { AMPS } from '../helpers/amps'; -import { SupportedApplications } from '../types/SupportedApplications'; - -// Check if there is a command in the arguments -const isDefaultCommand = !process.argv.some(item => Object.values(SupportedApplications).includes(item as SupportedApplications)); -// If there is no command, check if we are running this within the context of an Atlassian Plugin project -if (isDefaultCommand) { - const application = AMPS.getApplication(); - if (application) { - const args = [ application, ...process.argv.splice(2) ]; - process.argv = [ ...process.argv.slice(0, 2), ...args ]; +import { getApplication } from '../helpers/getApplication'; +import { showHelpWithDefaultCommandOptions } from '../helpers/showHelpWithDefaultCommandOptions'; +import { Application, SupportedApplications, TApplicationOptions } from '../types/Application'; +import { SupportedDatabaseEngines } from '../types/Database'; + +const program = new Commander(); + +const Command = () => { + let instance: Application|null = null; + + return { + action: async (options: TApplicationOptions) => { + return new Promise((resolve, reject) => { + const amps = new AMPS({ + cwd: options.cwd, + profiles: options.activateProfiles?.split(',') || [] + }); + + if (options.name && options.tag && options.activateProfiles) { + throw new InvalidOptionArgumentError('Invalid argument "--activate-profiles"'); + } else if (options.name && options.tag && options.cwd) { + throw new InvalidOptionArgumentError('Invalid argument "--cwd"'); + } + + if (!options.name && !amps.isAtlassianPlugin()) { + throw new Error('Unable to find an Atlassian Plugin project in the current directory 🤔'); + } + + options.name = options.name || amps.getApplication(); + if (!options.name) { + throw new Error('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + } + + if (!options.tag) { + const version = amps.getApplicationVersion(); + if (!version) { + throw new Error('Failed to determine version from AMPS and no product version provided (--tag)'); + } else { + options.tag = version; + } + } + + if (!versions[options.name].includes(options.tag)) { + throw new Error(`Product version '${options.tag}' is invalid. Allowed choices are ${versions[options.name].join(', ')}.`); + } + + instance = getApplication(options); + console.log(`Starting ${instance.name}... 💃`); + instance.start().then(resolve).catch(reject); + }) + }, + errorHandler: async () => { + if (instance) { + console.log(`Stopping ${instance.name}... 💔`); + await instance.stop().catch(() => null); + console.log(`Stopped ${instance.name} 💪`); + } + } } -} +}; program .name('dcdx run') - .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) - .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'}) + .description( +`Run the host application based on the Atlassian Maven Plugin Suite (AMPS) configuration. +If you wish to run a specific host application, call this command with 'dcdx run '`) + .addHelpText('afterAll','​') // there is a non-zero white space because the only reason to have this is to force a line break + .configureHelp(showHelpWithDefaultCommandOptions) + .allowUnknownOption(false) + .allowExcessArguments(false) .showHelpAfterError(true); -program.parse(process.argv); \ No newline at end of file +program + .command('fromAMPS', { isDefault: true, hidden: true }) + .addOption(new Option('-t, --tag ', 'The Docker tag of the host application')) + .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) + .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('--xms ', 'JVM minimum heap size').default('1024m')) + .addOption(new Option('--xmx ', 'JVM maximum heap size').default('1024m')) + .addOption(new Option('--cwd ', 'Specify the working directory where to find the AMPS configuration')) + .addOption(new Option('--clean', 'Remove data files before starting the host application').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping the host application').default(false)) + .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(false)) + .action(options => ActionHandler(program, Command(), options)); + +program + .command(SupportedApplications.Values.jira) + .description('Start Atlassian Jira (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Jira').choices(versions[SupportedApplications.Values.jira]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the Atlassian Jira will run').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-p, --port ', 'The HTTP port on which Atlassian Jira will be accessible').default('80')) + .addOption(new Option('-c, --contextPath ', 'The context path on which Atlassian Jira will be accessible')) + .addOption(new Option('--xms ', 'JVM minimum heap size').default('1024m')) + .addOption(new Option('--xmx ', 'JVM maximum heap size').default('1024m')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('--cwd ', 'Specify the working directory')) + .addOption(new Option('--clean', 'Remove data files before starting Atlassian Jira').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping Atlassian Jira').default(false)) + .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(false)) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.jira })); + +program + .command(SupportedApplications.Values.confluence) + .description('Start Atlassian Confluence (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Confluence').choices(versions[SupportedApplications.Values.confluence]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the Atlassian Confluence will run').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-p, --port ', 'The HTTP port on which Atlassian Confluence will be accessible').default('80')) + .addOption(new Option('-c, --contextPath ', 'The context path on which Atlassian Confluence will be accessible')) + .addOption(new Option('--xms ', 'JVM minimum heap size').default('1024m')) + .addOption(new Option('--xmx ', 'JVM maximum heap size').default('1024m')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('--cwd ', 'Specify the working directory')) + .addOption(new Option('--clean', 'Remove data files before starting Atlassian Confluence').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping Atlassian Confluence').default(false)) + .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(false)) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.confluence })); + +program + .command(SupportedApplications.Values.bitbucket) + .description('Start Atlassian Bitbucket (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Bitbucket').choices(versions[SupportedApplications.Values.bitbucket]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the Atlassian Bitbucket will run').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-p, --port ', 'The HTTP port on which Atlassian Bitbucket will be accessible').default('80')) + .addOption(new Option('-c, --contextPath ', 'The context path on which Atlassian Bitbucket will be accessible')) + .addOption(new Option('--xms ', 'JVM minimum heap size').default('1024m')) + .addOption(new Option('--xmx ', 'JVM maximum heap size').default('1024m')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('--cwd ', 'Specify the working directory')) + .addOption(new Option('--clean', 'Remove data files before starting Atlassian Bitbucket').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping Atlassian Bitbucket').default(false)) + .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(false)) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.bitbucket })); + +program + .command(SupportedApplications.Values.bamboo) + .description('Start Atlassian Bamboo (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Bamboo').choices(versions[SupportedApplications.Values.bamboo]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine on which the Atlassian Bamboo will run').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-p, --port ', 'The HTTP port on which Atlassian Bamboo will be accessible').default('80')) + .addOption(new Option('-c, --contextPath ', 'The context path on which Atlassian Bamboo will be accessible')) + .addOption(new Option('--xms ', 'JVM minimum heap size').default('1024m')) + .addOption(new Option('--xmx ', 'JVM maximum heap size').default('1024m')) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('--cwd ', 'Specify the working directory')) + .addOption(new Option('--clean', 'Remove data files before starting Atlassian Bamboo').default(false)) + .addOption(new Option('--prune', 'Remove data files when stopping Atlassian Bamboo').default(false)) + .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(false)) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.bamboo })); + +program.parseAsync().catch(() => gracefulExit(1)); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); diff --git a/src/commands/run/bamboo.ts b/src/commands/run/bamboo.ts deleted file mode 100644 index c466e18..0000000 --- a/src/commands/run/bamboo.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { RunCommand } from './command'; - -RunCommand(SupportedApplications.BAMBOO); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/run/bitbucket.ts b/src/commands/run/bitbucket.ts deleted file mode 100644 index b1e22e0..0000000 --- a/src/commands/run/bitbucket.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { RunCommand } from './command'; - -RunCommand(SupportedApplications.BITBUCKET); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/run/command.ts b/src/commands/run/command.ts deleted file mode 100644 index 3e07442..0000000 --- a/src/commands/run/command.ts +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env node - -import { Option, program } from 'commander'; -import { asyncExitHook } from 'exit-hook'; - -import versions from '../../../assets/versions.json'; -import { getApplicationByName } from '../../helpers/getApplication'; -import { SupportedApplications } from '../../types/SupportedApplications'; - -export const RunCommand = async (name: SupportedApplications) => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions[name]).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) - .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) - .addOption(new Option('-qr, --quickReload ', 'Add support for QuickReload and add the provided path to the watch list')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005')) - .parse(process.argv) - .opts(); - - const Application = getApplicationByName(name); - const instance = new Application({ - version: options.version, - database: options.database, - port: Number(options.port), - contextPath: options.contextPath, - quickReload: options.qr, - clean: options.clean, - prune: options.prune, - debug: options.debug - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - await instance.start(); -}; \ No newline at end of file diff --git a/src/commands/run/confluence.ts b/src/commands/run/confluence.ts deleted file mode 100644 index fc87025..0000000 --- a/src/commands/run/confluence.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { RunCommand } from './command'; - -RunCommand(SupportedApplications.CONFLUENCE); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/run/jira.ts b/src/commands/run/jira.ts deleted file mode 100644 index 9bcfc90..0000000 --- a/src/commands/run/jira.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { RunCommand } from './command'; - -RunCommand(SupportedApplications.JIRA); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/start.ts b/src/commands/start.ts deleted file mode 100644 index 1a80482..0000000 --- a/src/commands/start.ts +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env node - -import { watch } from 'chokidar'; -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; -import { cwd } from 'process'; - -import { AMPS } from '../helpers/amps'; -import { getApplicationByName } from '../helpers/getApplication'; -import { isRecursiveBuild } from '../helpers/isRecursiveBuild'; -import { showRecursiveBuildWarning } from '../helpers/showRecursiveBuildWarning'; - -const version = AMPS.getApplicationVersion(); - -(async () => { - const options = program - .name('dcdx start') - .description('Start the host application in dev mode based on the Atlassian Maven Plugin Suite (AMPS) configuration.\nYou can add Maven build arguments after the command options.') - .usage('[options] [...maven_arguments]') - .addOption(new Option('-v, --version ', 'The version of the host application').default(version)) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('-p, --port ', 'The HTTP port on which the host application will be accessible').default('80')) - .addOption(new Option('-c, --contextPath ', 'The context path on which the host application will be accessible')) - .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) - .addOption(new Option('-qr, --quickReload', 'Build & install the app automatically using QuickReload')) - .addOption(new Option('-o, --outputDirectory ', 'Output directory where QuickReload will look for generated JAR files').default('target')) - .addOption(new Option('--clean', 'Remove data files before starting the database').default(false)) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .addOption(new Option('--debug', 'Add support for JVM debugger on port 5005').default(false)) - .allowUnknownOption(true) - .parse(process.argv) - .opts(); - - if (!AMPS.isAtlassianPlugin()) { - console.log('Unable to find an Atlassian Plugin project in the current directory 🤔'); - gracefulExit(); - } - - const application = AMPS.getApplication(); - if (!application) { - console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); - gracefulExit(); - process.exit(); - } - - const Application = getApplicationByName(application); - if (!Application) { - console.log('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); - process.exit(); - } - - const instance = new Application({ - devMode: true, - version: options.version, - database: options.database, - port: Number(options.port), - contextPath: options.contextPath, - clean: options.clean, - prune: options.prune, - debug: options.debug - }); - - const mavenOpts = program.args.slice(); - if (options.activateProfiles) { - mavenOpts.push(...[ '-P', options.activateProfiles ]); - } - - let quickReload = null; - if (options.quickReload) { - console.log('Watching filesystem for changes to source files (QuickReload)'); - let lastBuildCompleted = new Date().getTime(); - quickReload = watch('**/*', { - cwd: cwd(), - usePolling: true, - interval: 2 * 1000, - binaryInterval: 2 * 1000, - awaitWriteFinish: true, - atomic: true - }).on('change', async (path) => { - if (path.startsWith(options.outputDirectory) && path.toLowerCase().endsWith('.jar')) { - console.log('Found updated JAR file(s), uploading them to QuickReload'); - await instance.cp(path); - lastBuildCompleted = new Date().getTime(); - } else if (!path.startsWith(options.outputDirectory)) { - if (isRecursiveBuild(lastBuildCompleted)) { - showRecursiveBuildWarning(options.outputDirectory); - } else { - console.log('Detected file change, rebuilding Atlasian Plugin for QuickReload'); - await AMPS.build(mavenOpts).catch(() => Promise.resolve()); - } - } - }); - } - - asyncExitHook(async () => { - if (quickReload) { - console.log(`Stopping filesystem watcher... ⏳`); - await quickReload.close(); - } - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Successfully stopped all running processes 💪`); - }, { wait: 30 * 1000 }); - - console.log('Starting application...'); - await instance.start(); - -})(); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); - diff --git a/src/commands/stop.ts b/src/commands/stop.ts index fa68397..043ea9b 100644 --- a/src/commands/stop.ts +++ b/src/commands/stop.ts @@ -1,28 +1,128 @@ #!/usr/bin/env node -import { Option, program } from 'commander'; +import { Command as Commander,InvalidOptionArgumentError,Option } from 'commander'; +import { gracefulExit } from 'exit-hook'; +import versions from '../../assets/versions.json'; +import { ActionHandler } from '../helpers/ActionHandler'; import { AMPS } from '../helpers/amps'; -import { SupportedApplications } from '../types/SupportedApplications'; - -// Check if there is a command in the arguments -const isDefaultCommand = !process.argv.some(item => Object.values(SupportedApplications).includes(item as SupportedApplications)); -// If there is no command, check if we are running this within the context of an Atlassian Plugin project -if (isDefaultCommand) { - const application = AMPS.getApplication(); - if (application) { - const args = [ application, ...process.argv.splice(2) ]; - process.argv = [ ...process.argv.slice(0, 2), ...args ]; +import { getApplication } from '../helpers/getApplication'; +import { showHelpWithDefaultCommandOptions } from '../helpers/showHelpWithDefaultCommandOptions'; +import { Application, SupportedApplications, TApplicationOptions } from '../types/Application'; +import { SupportedDatabaseEngines } from '../types/Database'; + +const program = new Commander(); + +const Command = () => { + let instance: Application|null = null; + return { + action: async (options: TApplicationOptions) => { + const amps = new AMPS({ + cwd: options.cwd, + profiles: options.activateProfiles?.split(',') || [] + }); + + if (options.name && options.tag && options.activateProfiles) { + throw new InvalidOptionArgumentError('Invalid argument "--activate-profiles"'); + } else if (options.name && options.tag && options.cwd) { + throw new InvalidOptionArgumentError('Invalid argument "--cwd"'); + } + + if (!options.name && !amps.isAtlassianPlugin()) { + throw new Error('Unable to find an Atlassian Plugin project in the current directory 🤔'); + } + + options.name = options.name || amps.getApplication(); + if (!options.name) { + throw new Error('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + } + + if (!options.tag) { + const version = amps.getApplicationVersion(); + if (!version) { + throw new Error('Failed to determine version from AMPS and no product version provided (--tag)'); + } else { + options.tag = version; + } + } + + if (!versions[options.name].includes(options.tag)) { + throw new Error(`Product version '${options.tag}' is invalid. Allowed choices are ${versions[options.name].join(', ')}.`); + } + + instance = getApplication(options); + console.log(`Stopping ${options.name} and ${options.database}... 💔`); + return instance.stop().then(() => { + console.log(`Stopped ${options.name} and ${options.database} 💪`); + }); + }, + errorHandler: async (options: TApplicationOptions) => { + if (instance) { + console.log(`Stopping ${instance.name} and ${options.database}... 💔`); + await instance.stop().catch(() => null); + console.log(`Stopped ${instance.name} and ${options.database} 💪`); + } + } } } program .name('dcdx stop') - .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) - .command('bamboo', 'Stop Atlassian Bamboo (standalone)', { executableFile: './stop/bamboo.js'}) - .command('bitbucket', 'Stop Atlassian Bitbucket (standalone)', { executableFile: './stop/bitbucket.js'}) - .command('confluence', 'Stop Atlassian Confluence (standalone)', { executableFile: './stop/confluence.js'}) - .command('jira', 'Stop Atlassian Jira (standalone)', { executableFile: './stop/jira.js'}) + .description( +`Stop the host application based on the Atlassian Maven Plugin Suite (AMPS) configuration. +If you wish to stop a specific host application running standalone, call this command with 'dcdx stop '`) + .addHelpText('afterAll','​') // there is a non-zero white space because the only reason to have this is to force a line break + .configureHelp(showHelpWithDefaultCommandOptions) + .allowUnknownOption(false) + .allowExcessArguments(false) .showHelpAfterError(true); -program.parse(process.argv); \ No newline at end of file +program + .command('fromAMPS', { isDefault: true, hidden: true }) + .addOption(new Option('-d, --database ', 'The database engine to stop').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate')) + .addOption(new Option('--cwd ', 'Specify the working directory')) + .action(options => ActionHandler(program, Command(), options)); + +program + .command('bamboo') + .description('Stop Atlassian Bamboo (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Bamboo').choices(versions[SupportedApplications.Values.bamboo]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine to stop').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available without --tag)')) + .addOption(new Option('--cwd ', 'Specify the working directory (only available without --tag)')) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.bamboo })); + +program + .command('bitbucket') + .description('Stop Atlassian Bitbucket (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Bitbucket').choices(versions[SupportedApplications.Values.bitbucket]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine to stop').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available without --tag)')) + .addOption(new Option('--cwd ', 'Specify the working directory (only available without --tag)')) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.bitbucket })); + +program + .command('confluence') + .description('Stop Atlassian Confluence (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Confluence').choices(versions[SupportedApplications.Values.confluence]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine to stop').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available without --tag)')) + .addOption(new Option('--cwd ', 'Specify the working director (only available without --tag)')) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.confluence })); + +program + .command('jira') + .description('Stop Atlassian Jira (standalone)') + .addOption(new Option('-t, --tag ', 'The Docker tag of Atlassian Jira').choices(versions[SupportedApplications.Values.jira]).default('latest')) + .addOption(new Option('-d, --database ', 'The database engine to stop').choices(Object.values(SupportedDatabaseEngines.Values)).default(SupportedDatabaseEngines.Values.postgresql)) + .addOption(new Option('-P, --activate-profiles ', 'Comma-delimited list of profiles to activate (only available without --tag)')) + .addOption(new Option('--cwd ', 'Specify the working directory (only available without --tag)')) + .action(options => ActionHandler(program, Command(), { ...options, name: SupportedApplications.Values.jira })); + +program.parseAsync(process.argv).catch(() => gracefulExit(1)); + +process.on('SIGINT', () => { + console.log(`Received term signal, trying to stop gracefully 💪`); + gracefulExit(); +}); diff --git a/src/commands/stop/bamboo.ts b/src/commands/stop/bamboo.ts deleted file mode 100644 index 6d16b63..0000000 --- a/src/commands/stop/bamboo.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { StopCommand } from './command'; - -StopCommand(SupportedApplications.BAMBOO); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/stop/bitbucket.ts b/src/commands/stop/bitbucket.ts deleted file mode 100644 index 01f9ce5..0000000 --- a/src/commands/stop/bitbucket.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { StopCommand } from './command'; - -StopCommand(SupportedApplications.BITBUCKET); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/stop/command.ts b/src/commands/stop/command.ts deleted file mode 100644 index 2e3d006..0000000 --- a/src/commands/stop/command.ts +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env node - -import { Option, program } from 'commander'; -import { asyncExitHook, gracefulExit } from 'exit-hook'; - -import versions from '../../../assets/versions.json'; -import { getApplicationByName } from '../../helpers/getApplication'; -import { SupportedApplications } from '../../types/SupportedApplications'; - -export const StopCommand = async (name: SupportedApplications) => { - const options = program - .showHelpAfterError(true) - .addOption(new Option('-v, --version ', 'The version of the host application').choices(versions[name]).default('latest')) - .addOption(new Option('-d, --database ', 'The database engine on which the host application will run').choices([ 'postgresql', 'mysql', 'mssql' ]).default('postgresql')) - .addOption(new Option('--prune', 'Remove data files when stopping the database').default(false)) - .parse(process.argv) - .opts(); - - const Application = getApplicationByName(name); - const instance = new Application({ - version: options.version, - database: options.database, - prune: options.prune - }); - - asyncExitHook(async () => { - console.log(`Stopping ${instance.name}... ⏳`); - await instance.stop(); - console.log(`Stopped ${instance.name} 💪`); - }, { - wait: 30 * 1000 - }); - - gracefulExit(); -}; \ No newline at end of file diff --git a/src/commands/stop/confluence.ts b/src/commands/stop/confluence.ts deleted file mode 100644 index 8d457b4..0000000 --- a/src/commands/stop/confluence.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { StopCommand } from './command'; - -StopCommand(SupportedApplications.CONFLUENCE); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/commands/stop/jira.ts b/src/commands/stop/jira.ts deleted file mode 100644 index 53a25c5..0000000 --- a/src/commands/stop/jira.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node - -import { gracefulExit } from 'exit-hook'; - -import { SupportedApplications } from '../../types/SupportedApplications'; -import { StopCommand } from './command'; - -StopCommand(SupportedApplications.JIRA); - -process.on('SIGINT', () => { - console.log(`Received term signal, trying to stop gracefully 💪`); - gracefulExit(); -}); \ No newline at end of file diff --git a/src/databases/base.ts b/src/databases/base.ts index 189877c..d169de5 100644 --- a/src/databases/base.ts +++ b/src/databases/base.ts @@ -1,71 +1,75 @@ import { spawn } from 'child_process'; -import { upAll } from 'docker-compose'; -import { downAll, ps, stop } from 'docker-compose/dist/v2.js'; -import EventEmitter from 'events'; +import { downAll, ps, stop,upAll } from 'docker-compose/dist/v2.js'; import { gracefulExit } from 'exit-hook'; import { dump } from 'js-yaml'; -import { cwd } from 'process'; import { ConnectionAcquireTimeoutError, ConnectionError, ConnectionRefusedError, ConnectionTimedOutError, Dialect, Sequelize, TimeoutError } from 'sequelize'; import { network } from '../helpers/network'; -import { DatabaseEngine } from '../types/DatabaseEngine'; -import { DatabaseOptions } from '../types/DatabaseOptions'; +import { DatabaseEngine, TDatabaseOptions } from '../types/Database'; import { DockerComposeV3, Service } from '../types/DockerComposeV3'; -import { SupportedDatabaseDrivers } from '../types/SupportedDatabaseDrivers'; -import { SupportedDatabaseEngines } from '../types/SupportedDatabaseEngines'; -export abstract class Base extends EventEmitter implements DatabaseEngine { +export abstract class Base implements DatabaseEngine { - private sequelize: Sequelize|null = null; + private sequelize: Sequelize; - abstract get name(): SupportedDatabaseEngines; - abstract get driver(): SupportedDatabaseDrivers; - abstract get version(): string; abstract get url(): string; // ------------------------------------------------------------------------------------------ Constructor - constructor(public options: DatabaseOptions) { - super(); + constructor(public options: TDatabaseOptions) { + // We need to use 'master' for mssql because it does not initialize the database on start + const database = this.options.name === 'mssql' ? 'master' : this.options.database; + this.sequelize = new Sequelize(database, this.options.username, this.options.password, { + host: 'localhost', + port: this.options.port, + dialect: this.options.name.replace('postgresql', 'postgres') as Dialect, + retry: { + max: 30, + match: [ConnectionError, TimeoutError, ConnectionTimedOutError, ConnectionRefusedError, ConnectionAcquireTimeoutError], + backoffBase: 1000, + backoffExponent: 1 + }, + logging: false + }); } // ------------------------------------------------------------------------------------------ Public Methods - async run(sql: string | { query: string; values: unknown[] }, logging?: boolean): Promise<[unknown[], unknown]|null> { + async run(sql: string | { query: string; values: unknown[] }, logging?: boolean): Promise { try { - if (!this.sequelize) throw new Error('Database connection does not exist'); await this.sequelize.query(sql, { logging }); } catch (err) { console.error('An error occurred while trying to run the following SQL query:', sql, err); - gracefulExit(); + throw err; } - return null; } async start(clean = this.options.clean): Promise { - console.log(`Starting instance of ${this.name} ⏳`); + console.log(`Starting instance of ${this.options.name}... 💃`); if (clean) { await this.down(); } await this.up(); - this.emit(`${this.name}:up`); const isAvailable = await this.waitUntilReady(); if (!isAvailable) { - console.log(`Failed to start database ${this.name} ⛔`); - gracefulExit(0); + console.log(`Failed to verify status of ${this.options.name} ⛔`); + gracefulExit(1); } else { console.log(`Database is ready and accepting connections on localhost:${this.options.port} 🗄️`); - await this.onDatabaseReady(); - this.emit('db:ready'); - - if (this.options.logging) { - const service = await this.getServiceState(); - if (service) { - await this.showDockerLogs(service.name); + try { + await this.onDatabaseReady(); + + if (this.options.verbose) { + const service = await this.getServiceState(); + if (service) { + await this.showDockerLogs(service.name); + } } + } catch (err) { + gracefulExit(1); } } } @@ -76,12 +80,10 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { } else { const configAsString = dump(this.getDockerComposeConfig()); await stop({ - cwd: cwd(), configAsString, log: true - }) + }); } - this.emit('db:stopped'); } // ------------------------------------------------------------------------------------------ Protected Methods @@ -107,7 +109,6 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { const configAsString = dump(this.getDockerComposeConfig()); return upAll({ - cwd: cwd(), configAsString, log: true }); @@ -116,7 +117,6 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { private async down() { const configAsString = dump(this.getDockerComposeConfig()); return downAll({ - cwd: cwd(), configAsString, commandOptions: [ '-v', '--remove-orphans', '--rmi', 'local' ], log: true @@ -124,35 +124,13 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { } private async waitUntilReady(): Promise { - try { - // We need to use 'master' for mssql because it does not initialize the database on start - const database = this.name === 'mssql' ? 'master' : this.options.database; - this.sequelize = new Sequelize(database, this.options.username, this.options.password, { - host: 'localhost', - port: this.options.port, - dialect: this.name.replace('postgresql', 'postgres') as Dialect, - retry: { - max: 30, - match: [ConnectionError, TimeoutError, ConnectionTimedOutError, ConnectionRefusedError, ConnectionAcquireTimeoutError], - backoffBase: 1000, - backoffExponent: 1 - }, - logging: false - }); - - return this.sequelize.authenticate().then(() => true).catch((err) => { - console.log(err); - return false; - }); - } catch (err) { - return false; - } + return this.sequelize.authenticate().then(() => true).catch(() => false); } private async getServiceState() { const configAsString = dump(this.getDockerComposeConfig()); const result = await ps({ configAsString, log: false, commandOptions: [ '--all' ] }); - return result.data.services.find(item => item.name.includes(this.name)); + return result.data.services.find(item => item.name.includes(this.options.name)); } private async showDockerLogs(service: string) { @@ -160,7 +138,7 @@ export abstract class Base extends EventEmitter implements DatabaseEngine { const docker = spawn( 'docker', [ 'logs', '-f', '-n', '5000', service ], - { cwd: cwd(), stdio: 'inherit' } + { stdio: 'inherit' } ); docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); }); diff --git a/src/databases/mssql.ts b/src/databases/mssql.ts index 3b97ec9..f288c30 100644 --- a/src/databases/mssql.ts +++ b/src/databases/mssql.ts @@ -1,28 +1,8 @@ -import { DatabaseOptions } from '../types/DatabaseOptions'; +import { MSSQLOptions, TMSSQLOptions } from '../types/Database'; import { Service } from '../types/DockerComposeV3'; -import { SupportedDatabaseDrivers } from '../types/SupportedDatabaseDrivers'; -import { SupportedDatabaseEngines } from '../types/SupportedDatabaseEngines'; import { Base } from './base'; -export type MSSQLOptions = DatabaseOptions & { - edition: 'Developer'|'Express'|'Standard'|'Enterprise'|'EnterpriseCore'; - version: '2017'|'2019'|'2022'; -} - -const defaultOptions: MSSQLOptions = { - port: 1433, - database: 'dcdx', - username: 'sa', - password: 'DataCenterDX!', - edition: 'Developer', - version: '2022' -}; - export class MSSQL extends Base { - name: SupportedDatabaseEngines = 'mssql'; - driver: SupportedDatabaseDrivers = 'com.microsoft.sqlserver.jdbc.SQLServerDriver'; - options: MSSQLOptions = defaultOptions; - version: '2017'|'2019'|'2022' = '2022' public get url() { return `jdbc:sqlserver://db:${this.options.port};databaseName=${this.options.database};trustServerCertificate=true`; @@ -30,8 +10,8 @@ export class MSSQL extends Base { // ------------------------------------------------------------------------------------------ Constructor - constructor(options: MSSQLOptions = defaultOptions) { - super({ ...defaultOptions, ...options }); + constructor(public options: TMSSQLOptions = MSSQLOptions.parse({})) { + super(options); } // ------------------------------------------------------------------------------------------ Protected Methods @@ -44,7 +24,7 @@ export class MSSQL extends Base { protected getService = (): Service => { return { - image: `mcr.microsoft.com/mssql/server:${this.version}-latest`, + image: `mcr.microsoft.com/mssql/server:${this.options.tag}`, ports: [ `${this.options.port || 1433}:1433` ], environment: { ACCEPT_EULA: 'y', diff --git a/src/databases/mysql.ts b/src/databases/mysql.ts index 4239826..0c63f36 100644 --- a/src/databases/mysql.ts +++ b/src/databases/mysql.ts @@ -1,27 +1,8 @@ -import { DatabaseOptions } from '../types/DatabaseOptions'; +import { MySQLOptions, TMySQLOptions } from '../types/Database'; import { Service } from '../types/DockerComposeV3'; -import { SupportedDatabaseDrivers } from '../types/SupportedDatabaseDrivers'; -import { SupportedDatabaseEngines } from '../types/SupportedDatabaseEngines'; import { Base } from './base'; -type MySQLOptions = DatabaseOptions & { - version: '8.0'|'8.3' -}; - -const defaultOptions: MySQLOptions = { - port: 3306, - database: 'dcdx', - username: 'dcdx', - password: 'dcdx', - version: '8.0' -} - - export class MySQL extends Base { - name: SupportedDatabaseEngines = 'mysql'; - driver: SupportedDatabaseDrivers = 'com.mysql.jdbc.Driver'; - options: MySQLOptions = defaultOptions - version: '8.0'|'8.3' = '8.0'; public get url() { return `jdbc:mysql://db:${this.options.port}/${this.options.database}?sessionVariables=transaction_isolation='READ-COMMITTED'`; @@ -29,8 +10,8 @@ export class MySQL extends Base { // ------------------------------------------------------------------------------------------ Constructor - constructor(options: MySQLOptions = defaultOptions) { - super({ ...defaultOptions, ...options }); + constructor(public options: TMySQLOptions = MySQLOptions.parse({})) { + super(options); } // ------------------------------------------------------------------------------------------ Protected Methods @@ -41,7 +22,7 @@ export class MySQL extends Base { protected getService = (): Service => { return { - image: `mysql:${this.version}`, + image: `mysql:${this.options.tag}`, ports: [ `${this.options.port || 3306}:3306` ], environment: { MYSQL_ROOT_PASSWORD: this.options.password || 'dcdx', diff --git a/src/databases/postgres.ts b/src/databases/postgres.ts index 371cf52..f591807 100644 --- a/src/databases/postgres.ts +++ b/src/databases/postgres.ts @@ -1,27 +1,9 @@ -import { DatabaseOptions } from '../types/DatabaseOptions'; +import { PostgreSQLOptions, TPostgreSQLOptions } from '../types/Database'; import { Service } from '../types/DockerComposeV3'; -import { SupportedDatabaseDrivers } from '../types/SupportedDatabaseDrivers'; -import { SupportedDatabaseEngines } from '../types/SupportedDatabaseEngines'; import { Base } from './base'; -type PostgresOptions = DatabaseOptions & { - version?: '12'|'13'|'14'|'15'; -} - -const defaultOptions: PostgresOptions = { - version: '15', - database: 'dcdx', - port: 5432, - username: 'dcdx', - password: 'dcdx' -}; - export class Postgres extends Base { - name: SupportedDatabaseEngines = 'postgresql'; - driver: SupportedDatabaseDrivers = 'org.postgresql.Driver'; - options: PostgresOptions = defaultOptions; - version: string = '15'; public get url() { return `jdbc:postgresql://db:${this.options.port}/${this.options.database}`; @@ -29,15 +11,15 @@ export class Postgres extends Base { // ------------------------------------------------------------------------------------------ Constructor - constructor(options: PostgresOptions = defaultOptions) { - super({ ...defaultOptions, ...options }); + constructor(public options: TPostgreSQLOptions = PostgreSQLOptions.parse({})) { + super(options); } // ------------------------------------------------------------------------------------------ Protected Methods protected getService = (): Service => { return { - image: `postgres:${this.version}`, + image: `postgres:${this.options.tag}`, ports: [ `${this.options.port || 5432}:5432` ], environment: { POSTGRES_USER: this.options.username || 'dcdx', diff --git a/src/helpers/ActionHandler.ts b/src/helpers/ActionHandler.ts new file mode 100644 index 0000000..02d6561 --- /dev/null +++ b/src/helpers/ActionHandler.ts @@ -0,0 +1,47 @@ +import { Command } from 'commander'; +import { asyncExitHook, gracefulExit } from 'exit-hook'; +import { z } from 'zod'; + +import { TBuildOptions } from '../types/AMPS'; +import { TApplicationOptions } from '../types/Application'; +import { TDatabaseOptions } from '../types/Database'; + +export const ActionHandler = async (program: Command, { action, errorHandler }: { + action: (options: T) => Promise; + errorHandler: (options: T) => Promise; +}, options?: T) => { + const ops: T = options || program.opts(); + await new Promise((resolve, reject) => { + let errorMessage: string|null = ''; + + asyncExitHook(async (code) => { + try { + if (code !== 0) { + await errorHandler(ops).catch(() => null); + } + + if (errorMessage) { + throw new Error(errorMessage.trim()); + } else { + resolve(); + } + } catch (err) { + const message = err instanceof Error ? err.message : err; + reject(message); + } + }, { + wait: 30 * 1000 + }); + + action(ops).catch(err => { + if (err instanceof z.ZodError) { + err.issues.forEach(issue => { + errorMessage += `Unable to parse option ${issue.path.join(',')}: ${issue.message}\n` + }); + } else if (err instanceof Error || typeof err === 'string') { + errorMessage = err.toString(); + } + gracefulExit(1); + }); + }).catch(message => program.error(message)); +} \ No newline at end of file diff --git a/src/helpers/FileWatcher.ts b/src/helpers/FileWatcher.ts new file mode 100644 index 0000000..c192028 --- /dev/null +++ b/src/helpers/FileWatcher.ts @@ -0,0 +1,63 @@ +import { watch } from 'chokidar'; +import { resolve as resolvePath } from 'path'; +import { cwd } from 'process'; + +import { isRecursiveBuild } from '../helpers/isRecursiveBuild'; +import { showRecursiveBuildWarning } from '../helpers/showRecursiveBuildWarning'; +import { TBuildOptions } from '../types/AMPS'; +import { TSupportedApplications } from '../types/Application'; +import { AMPS } from './amps'; +import * as Docker from './docker'; + +export const FileWatcher = (name: TSupportedApplications, options: TBuildOptions, mavenOpts: Array) => { + let lastBuildCompleted = new Date().getTime(); + const outputDirectory = options.outputDirectory || 'target'; + const patterns = options.ext || [ '**/*' ]; + console.log(`Watching filesystem for changes to source files (${patterns.join(', ')})`); + + const amps = new AMPS({ + cwd: options.cwd, + profiles: options.activateProfiles?.split(',') || [] + }); + + return watch(patterns, { + cwd: options.cwd || cwd(), + usePolling: true, + interval: 2 * 1000, + binaryInterval: 2 * 1000, + awaitWriteFinish: true, + persistent: true, + atomic: true + }).on('change', async (path) => { + if (options.install && path.startsWith(outputDirectory) && path.toLowerCase().endsWith('.jar')) { + const containerIds = await Docker.getRunningContainerIds(name); + if (containerIds.length <= 0) { + console.log(`There are no running instance of ${name}, unable to install plugin 🤔`); + return; + } else if (containerIds.length > 1) { + console.log(`There are multple running instance of ${name}, unable to determine which one to use 🤔`); + return; + } + + const containerId = containerIds[0]; + if (containerId) { + console.log(`Found updated JAR file, uploading them to QuickReload on running instances of ${name}`); + await Docker.copy(resolvePath(path), `${containerId}:/opt/quickreload/`) + .then(() => console.log('Finished uploading JAR file to QuickReload')) + .catch(err => console.log('Failed to upload JAR file to QuickReload', err)); + } + } else if (!path.startsWith(outputDirectory)) { + if (isRecursiveBuild(lastBuildCompleted)) { + showRecursiveBuildWarning(outputDirectory); + } else { + console.log('Detected file change, rebuilding Atlasian Data Center plugin'); + amps.build(mavenOpts).then(() => { + console.log(`Finished building Atlassian Data Center plugin for ${name}... 💪`); + }).catch(() => { + console.log(`Failed to build Atlassian Data Center plugin for ${name}... 😰`); + }) + lastBuildCompleted = new Date().getTime(); + } + } + }); +} \ No newline at end of file diff --git a/src/helpers/amps.ts b/src/helpers/amps.ts index 12ab60d..a6698a1 100644 --- a/src/helpers/amps.ts +++ b/src/helpers/amps.ts @@ -3,45 +3,55 @@ import { DOMParser, XMLSerializer } from '@xmldom/xmldom'; import { ChildProcess, spawn } from 'child_process'; import { XMLParser } from 'fast-xml-parser'; import { existsSync, readFileSync } from 'fs' +import { join } from 'path'; import { cwd } from 'process'; import xpath from 'xpath'; -import { hideBin } from 'yargs/helpers'; -import yargs from 'yargs/yargs'; -import { SupportedApplications } from '../types/SupportedApplications'; +import { SupportedApplications, TSupportedApplications } from '../types/Application'; -const { P, activeProfiles } = yargs(hideBin(process.argv)).parseSync(); -const profile = P as string || activeProfiles as string || undefined; +type AMPSOptions = { + cwd?: string; + profiles: Array +} export class AMPS { - private static maven: ChildProcess|null; + private maven: ChildProcess|null = null; - // ------------------------------------------------------------------------------------------ Public Static Methods + // ------------------------------------------------------------------------------------------ Constructor - public static stop() { - if (AMPS.maven) { - AMPS.maven.kill(0); + constructor(private options: AMPSOptions = { + profiles: [] + }) {} + + // ------------------------------------------------------------------------------------------ Public Methods + + public stop() { + if (this.maven) { + const killed = this.maven.kill(0); + if (!killed) { + throw new Error('Failed to terminate existing Maven process'); + } } } - public static async build(args: Array) { + public async build(args: Array) { return new Promise((resolve, reject) => { - if (AMPS.maven) { - const killed = AMPS.maven.kill(0); + if (this.maven) { + const killed = this.maven.kill(0); if (!killed) { reject(new Error('Failed to terminate existing Maven process')); } } - AMPS.maven = spawn( + this.maven = spawn( 'mvn', [ 'package', ...args ], - { cwd: cwd(), stdio: 'inherit' } + { cwd: this.options?.cwd || cwd(), stdio: 'inherit' } ); - AMPS.maven.on('exit', (code) => { - AMPS.maven = null; + this.maven.on('exit', (code) => { + this.maven = null; if (code === 0 || code === 130) { resolve(); } else { @@ -51,81 +61,93 @@ export class AMPS { }); } - public static isAtlassianPlugin = (): boolean => { + public isAtlassianPlugin = (): boolean => { try { - const nodes = AMPS.getNodes('//*[local-name()=\'packaging\']'); + const nodes = this.getNodes('//*[local-name()=\'packaging\']'); return nodes.some(item => item.textContent === 'atlassian-plugin'); } catch (err) { return false; } } - public static getApplicationVersion(): string|undefined { - const node = !profile - ? AMPS.getNodes('//*[local-name()=\'groupId\' and text()=\'com.atlassian.maven.plugins\']', true) - : AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${profile}']/..//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']`, true); - - if (node) { - const parentNode = node.parentNode; - if (parentNode) { - const { plugin } = AMPS.toObject(parentNode); - const version = plugin?.configuration?.productVersion; - return version ? this.doPropertyReplacement(version) : undefined; - } + public getApplicationVersion(): string|undefined { + const applications = this.getApplications(); + if (applications.size === 1) { + const [ , version ] = applications.entries().next().value; + return version; + } else if (applications.size > 1) { + throw new Error('The Atlassian Plugin project contains multiple AMPS configuration, unable to decide which product to use 😰') } return undefined; } - public static getApplication(): SupportedApplications|null { - const applications = AMPS.getApplications(); - if (applications.length === 1) { - return applications[0]; - } else if (profile) { - const profileApplications = AMPS.getApplications(profile); - if (profileApplications.length === 1) { - return profileApplications[0]; - } + public getApplication(): TSupportedApplications|null { + const applications = this.getApplications(); + if (applications.size === 1) { + const [ name ] = applications.entries().next().value; + return name; + } else if (applications.size > 1) { + throw new Error('The Atlassian Plugin project contains multiple AMPS configuration, unable to decide which product to use 😰') } return null; } - public static getApplications(profile?: string): Array { - const result = new Set(); - const nodes = !profile - ? AMPS.getNodes('//*[local-name()=\'groupId\' and text()=\'com.atlassian.maven.plugins\']') - : AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${profile}']/..//*[local-name()='groupId' and text()='com.atlassian.maven.plugins']`); + public getApplications(): Map { + const result = new Map(); + + const nodes = this.getNodes('//*[local-name()=\'groupId\' and text()=\'com.atlassian.maven.plugins\']'); + const nodesWithActiveProfile = nodes.filter(node => this.isActivatedProfile(node)); - nodes.forEach(node => { + nodesWithActiveProfile.forEach(node => { + const version = this.getVersion(node); const parentNode = node.parentNode; if (parentNode) { - const { plugin } = AMPS.toObject(parentNode); - if (plugin?.artifactId?.includes(SupportedApplications.JIRA)) { - result.add(SupportedApplications.JIRA); - } else if (plugin?.artifactId?.includes(SupportedApplications.CONFLUENCE)) { - result.add(SupportedApplications.CONFLUENCE); - } else if (plugin?.artifactId?.includes(SupportedApplications.BAMBOO)) { - result.add(SupportedApplications.BAMBOO); - } else if (plugin?.artifactId?.includes(SupportedApplications.BITBUCKET)) { - result.add(SupportedApplications.BITBUCKET); + const { plugin } = this.toObject(parentNode); + if (plugin?.artifactId?.includes(SupportedApplications.Values.jira)) { + result.set(SupportedApplications.Values.jira, version); + } else if (plugin?.artifactId?.includes(SupportedApplications.Values.confluence)) { + result.set(SupportedApplications.Values.confluence, version); + } else if (plugin?.artifactId?.includes(SupportedApplications.Values.bamboo)) { + result.set(SupportedApplications.Values.bamboo, version); + } else if (plugin?.artifactId?.includes(SupportedApplications.Values.bitbucket)) { + result.set(SupportedApplications.Values.bitbucket, version); } } }); - return Array.from(result); + return result; } - // ------------------------------------------------------------------------------------------ Private Static Methods + // ------------------------------------------------------------------------------------------ Private Methods + + private getVersion(node: Node): string|undefined { + const parentNode = node.parentNode; + if (parentNode) { + const { plugin } = this.toObject(parentNode); + const { configuration } = plugin || {}; + + if (configuration?.productVersion) { + return this.doPropertyReplacement(plugin.configuration.productVersion); + } else if (configuration?.products) { + const product = Array.isArray(configuration?.products) ? configuration?.products[0] : configuration.products.product; + if (product && product.version) { + return this.doPropertyReplacement(product.version); + } + } + } + return undefined; + } - private static doPropertyReplacement(value: string) { + private doPropertyReplacement(value: string) { let result = value; // If there is a profile, replace profile properties first as they take precedence - const profileProperties = profile ? AMPS.getProperties(profile) : {}; + const profileProperties = this.getProperties(); Object.entries(profileProperties).forEach(([propertyKey, propertyValue]) => { result = result.replaceAll(`$\{${propertyKey}}`, propertyValue); }); - const properties = AMPS.getProperties(); + const properties = this.getProperties(); Object.entries(properties).forEach(([propertyKey, propertyValue]) => { result = result.replaceAll(`$\{${propertyKey}}`, propertyValue); }); @@ -133,26 +155,27 @@ export class AMPS { return result; } - private static getProperties(profile?: string): Record { + private getProperties(): Record { const result: Record = {}; - const nodes = !profile - ? AMPS.getNodes('//*[local-name()=\'properties\']') - : AMPS.getNodes(`//*[local-name()='profile']/*[local-name()='id' and text()='${profile}']/..//*[local-name()='properties']`); + const nodes = this.getNodes('//*[local-name()=\'properties\']'); + const nodesWithActiveProfile = nodes.filter(node => this.isActivatedProfile(node)); - nodes.forEach(node => { - const { properties } = AMPS.toObject(node); + nodesWithActiveProfile.forEach(node => { + const { properties } = this.toObject(node); Object.entries(properties as Record).forEach(([ key, value ]) => result[key] = value); }); + return result; } - private static getNodes(expression: string): Array; - private static getNodes(expression: string, single: true): Node|null; - private static getNodes(expression: string, single?: true): Array|Node|null { - const hasPomFile = existsSync('./pom.xml'); + private getNodes(expression: string): Array; + private getNodes(expression: string, single: true): Node|null; + private getNodes(expression: string, single?: true): Array|Node|null { + const pomFilePath = join(this.options?.cwd || cwd(), './pom.xml'); + const hasPomFile = existsSync(pomFilePath); if (hasPomFile) { - const xml = readFileSync('./pom.xml', 'utf8'); + const xml = readFileSync(pomFilePath, 'utf8'); const doc = new DOMParser().parseFromString(xml, 'text/xml'); const nodes = single ? xpath.select(expression, doc, true) : xpath.select(expression, doc, false); if (Array.isArray(nodes)) { @@ -166,7 +189,7 @@ export class AMPS { return []; } - private static toObject(node: Node) { + private toObject(node: Node) { try { const parser = new XMLParser(); return parser.parse(new XMLSerializer().serializeToString(node)); @@ -175,4 +198,28 @@ export class AMPS { } } + private findParentNodeByTagName(tagName: string, node: Node): Node|null { + const { parentNode } = node; + if (parentNode) { + if (parentNode.nodeName.toLowerCase() === tagName.toLowerCase()) { + return parentNode; + } + return this.findParentNodeByTagName(tagName, parentNode); + } + return null; + } + + private isActivatedProfile(node: Node) { + const profileParentNode = this.findParentNodeByTagName('profile', node); + const profileNode = profileParentNode ? this.toObject(profileParentNode) : null; + + if (!profileNode) { + return true; + } else if (profileNode && (profileNode.activeByDefault === 'true' || this.options.profiles.includes(profileNode.profile.id))) { + return true; + } else { + return false; + } + } + } \ No newline at end of file diff --git a/src/helpers/docker.ts b/src/helpers/docker.ts index 84a3ceb..63bc080 100644 --- a/src/helpers/docker.ts +++ b/src/helpers/docker.ts @@ -2,30 +2,26 @@ import { spawn } from 'child_process'; import Dockerode from 'dockerode'; import { cwd } from 'process'; -import { SupportedApplications } from '../types/SupportedApplications'; +import { TSupportedApplications } from '../types/Application'; const docker = new Dockerode(); -export class Docker { - - public static async getRunningContainerIds(application: SupportedApplications) { - const containers = await docker.listContainers({ filters: { - status: [ 'running' ], - label: [ `com.docker.compose.service=${application}` ] - }}); - return containers.map(item => item.Id); - } - - public static async copy(sourcePath: string, destinationPath:string) { - return new Promise((resolve, reject) => { - const docker = spawn( - 'docker', - [ 'cp', sourcePath, destinationPath ], - { cwd: cwd(), stdio: 'inherit' } - ); - docker.on('error', reject) - docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); - }); - } +export const getRunningContainerIds = async (application: TSupportedApplications) => { + const containers = await docker.listContainers({ filters: { + status: [ 'running' ], + label: [ `com.docker.compose.service=${application}` ] + }}); + return containers.map(item => item.Id); +} +export const copy = async (sourcePath: string, destinationPath:string) => { + return new Promise((resolve, reject) => { + const docker = spawn( + 'docker', + [ 'cp', sourcePath, destinationPath ], + { cwd: cwd(), stdio: 'inherit' } + ); + docker.on('error', reject) + docker.on('exit', (code) => (code === 0) ? resolve() : reject(new Error(`Docker exited with code ${code}`))); + }); } \ No newline at end of file diff --git a/src/helpers/getApplication.ts b/src/helpers/getApplication.ts index 2ff88c0..1028a03 100644 --- a/src/helpers/getApplication.ts +++ b/src/helpers/getApplication.ts @@ -2,14 +2,13 @@ import { Bamboo } from '../applications/bamboo'; import { Bitbucket } from '../applications/bitbucket'; import { Confluence } from '../applications/confluence'; import { Jira } from '../applications/jira'; -import { SupportedApplications } from '../types/SupportedApplications'; +import { Application, SupportedApplications, TApplicationOptions } from '../types/Application'; - -export const getApplicationByName = (name: SupportedApplications) => { - switch (name) { - case SupportedApplications.JIRA: return Jira; - case SupportedApplications.CONFLUENCE: return Confluence; - case SupportedApplications.BAMBOO: return Bamboo; - case SupportedApplications.BITBUCKET: return Bitbucket; +export const getApplication = (options: TApplicationOptions): Application => { + switch (options.name) { + case SupportedApplications.Values.jira: return new Jira(options); + case SupportedApplications.Values.confluence: return new Confluence(options); + case SupportedApplications.Values.bamboo: return new Bamboo(options); + case SupportedApplications.Values.bitbucket: return new Bitbucket(options); } } \ No newline at end of file diff --git a/src/helpers/getDatabaseEngine.ts b/src/helpers/getDatabaseEngine.ts new file mode 100644 index 0000000..a9ff82a --- /dev/null +++ b/src/helpers/getDatabaseEngine.ts @@ -0,0 +1,18 @@ +import { MSSQL } from '../databases/mssql'; +import { MySQL } from '../databases/mysql'; +import { Postgres } from '../databases/postgres'; +import { DatabaseEngine, MSSQLOptions, MySQLOptions, PostgreSQLOptions, SupportedDatabaseEngines, TDatabaseOptions } from '../types/Database'; + +export const getDatabaseEngine = (options: TDatabaseOptions): DatabaseEngine => { + switch (options.name) { + case SupportedDatabaseEngines.Values.postgresql: + return new Postgres(PostgreSQLOptions.parse(options)); + + case SupportedDatabaseEngines.Values.mysql: + return new MySQL(MySQLOptions.parse(options)); + + case SupportedDatabaseEngines.Values.mssql: + return new MSSQL(MSSQLOptions.parse(options)); + + } +} diff --git a/src/helpers/getZodDefaults.ts b/src/helpers/getZodDefaults.ts new file mode 100644 index 0000000..fcc0ec2 --- /dev/null +++ b/src/helpers/getZodDefaults.ts @@ -0,0 +1,20 @@ +import { z } from 'zod'; + +export const getZodDefault = (schema: Schema, key: keyof z.infer) => { + const defaults = getZodDefaults(schema); + return defaults[key]; +} + +// Code snippet by Jacob Weisenburger (https://github.com/JacobWeisenburger) +// Taken from https://github.com/colinhacks/zod/discussions/1953#discussioncomment-4811588 +export const getZodDefaults = (schema: Schema): z.infer => { + return Object.fromEntries( + Object.entries(schema.shape).filter(([ , value ]) => { + if (value instanceof z.ZodDefault) { + const defaultValue = value._def.defaultValue(); + return defaultValue !== null && typeof defaultValue !== 'undefined'; + } + return false; + }).map(([key, value]) => ([key, (value as z.ZodDefault)._def.defaultValue()])) + ) +} \ No newline at end of file diff --git a/src/helpers/licences.ts b/src/helpers/licences.ts index 36e3fab..8599830 100644 --- a/src/helpers/licences.ts +++ b/src/helpers/licences.ts @@ -1,6 +1,6 @@ -import { SupportedApplications } from '../types/SupportedApplications'; +import { TSupportedApplications } from '../types/Application'; -export const timebomb: Record = { +export const timebomb: Record = { 'jira': 'AAACBA0ODAoPeNp9kl1v2jAUhu/zK460OyQHSLuVIkUaTbwNVBKUhEnbugsTDuA2sSPbgbFfPxOC1i+4yEXO5+vnPR+yGiGSW/CuoXc7vBoMvT4ENAOv5/WctUIUG1lVqNx7nqPQSJfccCl8GmU0mSXjlDpRXS5Qxau5RqV90ncCKQzLTcRK9Eu944Ip/cQ/M1MwrTkTbi5LZ1arfMM0hsyg7/X6A9LvE89z2j3ZvsJmQBBPpzQJxqP7U4r+qbjaN32zG+/baR2dMl6c35ei2qIah/4dvaHkOvzpkU/x5IZ8vbodOI9cMbdSclnnxj38EC1XZscUunYy36JvVI3OxGZmbZXdzwIU5lVJWi90rnjVQGoi70B873GNhPOQO51OFGfkS5yQWRKH8yAbxxGZp9Qm/EChpbGExR7MBqGdAlTkcokK7MMeMTfwa2NM9TDsdtfSfQGnWxw7CB47frsQShDSwJJro/iiNmgncw1GQl5rI0trtetY4sKgYCJ/Y4qVFSR0lNGQ3P04aDxrTKvWOjMXT0LuxCU33txaSiPffuRjr3fsO/mC6og/VmsmuGYN0Ij93TFYcVXunQaaDb4+wJbFd7vg0OI5If53NENtoK2AlVQwGScjSFt5lrjdWymu8WEIgSwtppyzAsLgxRk8P5hG9PMA3bKiPspdsULjJRwX7ukf5/NX7TAtAhUAhQSHz9DgycOX7wcwERMJb3TLGb4CFGkB/xUWSRK3BZLrPF9Rn6ZmrUqJX02oc', 'confluence': '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', 'bitbucket': 'AAABmg0ODAoPeNp9Ul1v0zAUfc+vsMRbJadJtjGpUiS2JJRONOlaB4SAB9e9W0xTu7JvCv33eImDOoR4vefe4/PhN59hR5bckPiWRNezKJ5FCZkvGUmiJA4yrZALLPkBUrHXotEnu5eBRW6b8KMUoCyw8xH6BVZs2KKce3TJpUJQXAkofh2lOeccIV1dfRhJC7fRXrJuwJzALPL0/qF+pI9XGaPz+uGGspvbkdRx8Awcr0nRdOCnlXnmSlqOUqv0DltureTKg6vOiIZb6J9PoviaRm9pHHs0M9Cf/Rv1Dv/SP5lMyorR99WartZVXmdsUZW03hQOSHtCF+n2TLAB4hlIoYTegSFHo3+AQPK1QTx+m02nzzrko+JQ6MO0HS4oDBffQ5JrojSSnbRo5LZDcMzSEtREdBb1AYwNRzsXff0JgjCwLjG/4mB5giE+JzhbF3esyOn9lxf1F2145a6OWu2V/qmC/2dVdoctmOqptk5PGid+XJx42w3NPPHWQuD9fXJbL8PktfLhU2Av+N2rYMY6uUEFpncY/AYCgfFAMCwCFHzkBlkGH/vRdTeeBSfUMzUDb4W2AhQyM893WPg2Q8oQAYRdBSyKeVUdog==X02jr', diff --git a/src/helpers/showHelpWithDefaultCommandOptions.ts b/src/helpers/showHelpWithDefaultCommandOptions.ts new file mode 100644 index 0000000..e6880ad --- /dev/null +++ b/src/helpers/showHelpWithDefaultCommandOptions.ts @@ -0,0 +1,18 @@ +import { Command, Help } from 'commander'; + + +export const showHelpWithDefaultCommandOptions: Partial = { + visibleOptions: (cmd: Command) => { + let options = cmd.options; + if (!cmd.parent && options.length === 0) { + const defaultCommandName = (cmd as unknown as Record)._defaultCommandName; + if (defaultCommandName) { + const defaultCommand = cmd.commands.find(item => (item as unknown as Record)._name === defaultCommandName); + if (defaultCommand) { + options = defaultCommand.options; + } + } + } + return Array.from(options); + } +} \ No newline at end of file diff --git a/src/helpers/showRecursiveBuildWarning.ts b/src/helpers/showRecursiveBuildWarning.ts index ddf925f..1aa8de4 100644 --- a/src/helpers/showRecursiveBuildWarning.ts +++ b/src/helpers/showRecursiveBuildWarning.ts @@ -8,6 +8,5 @@ Alternatively, Maven is using a different output directory than configured: '${outputDirectory}' Please make sure to check your build process and/or specify a different output directory using the '-o' option -=============================================================================================================== - `); +===============================================================================================================`); } diff --git a/src/index.ts b/src/index.ts index 8a995ee..857c2dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,12 +13,12 @@ program // ------------------------------------------------------------------------------------------ Build program - .command('build', 'Build & install the Atlassian Data Center plugin from the current directory', { executableFile: './commands/build.js' }); + .command('build', 'Build the Atlassian Data Center plugin based on the Atlassian Maven Plugin Suite (AMPS) configuration', { executableFile: './commands/build.js' }); // ------------------------------------------------------------------------------------------ Start program - .command('start', 'Start the host application based on the Atlassian Maven Plugin Suite (AMPS) configuration', { executableFile: './commands/start.js' }); + .command('debug', 'Start the product in debug mode based on the Atlassian Maven Plugin Suite (AMPS) configuration with the plugin installed', { executableFile: './commands/debug.js' }); // ------------------------------------------------------------------------------------------ Run @@ -114,6 +114,6 @@ program program.parse(process.argv); }); -program.parse(process.argv); +program.parse(); diff --git a/src/types/AMPS.ts b/src/types/AMPS.ts new file mode 100644 index 0000000..ccbe913 --- /dev/null +++ b/src/types/AMPS.ts @@ -0,0 +1,23 @@ +import { z } from 'zod'; + +import { ApplicationOptions } from './Application'; + +export const BuildOptions = z.object({ + watch: z.boolean(), + ext: z.array(z.string()), + install: z.boolean(), + outputDirectory: z.string(), + activateProfiles: z.string(), + cwd: z.string() +}).partial({ + watch: true, + ext: true, + outputDirectory: true, + activateProfiles: true, + cwd: true +}); + +export const DebugOptions = z.intersection(ApplicationOptions.omit({ name: true }), BuildOptions); + +export type TBuildOptions = z.infer; +export type TDebugOptions = z.infer; \ No newline at end of file diff --git a/src/types/Application.ts b/src/types/Application.ts new file mode 100644 index 0000000..e926343 --- /dev/null +++ b/src/types/Application.ts @@ -0,0 +1,46 @@ +import { z } from 'zod'; + +import { DatabaseEngine, SupportedDatabaseEngines } from './Database'; + +export interface Application { + name: TSupportedApplications; + database: DatabaseEngine; + logFilePath: string; + + start(): Promise; + stop(): Promise; + reset(): Promise; +} + +export const SupportedApplications = z.enum([ + 'jira', + 'confluence', + 'bitbucket', + 'bamboo' +]); + +export const ApplicationOptions = z.object({ + name: SupportedApplications, + tag: z.string().default('latest'), + database: SupportedDatabaseEngines.default('postgresql'), + port: z.string().transform(Number).refine(item => !isNaN(item)), + contextPath: z.string(), + xms: z.string().default('1024m'), + xmx: z.string().default('1024m'), + watch: z.boolean().default(false), + ext: z.array(z.string()), + install: z.boolean(), + outputDirectory: z.string(), + activateProfiles: z.string(), + clean: z.boolean().default(false), + prune: z.boolean().default(false), + debug: z.boolean().default(true), + cwd: z.string() +}).partial({ + activateProfiles: true, + contextPath: true, + cwd: true +}); + +export type TApplicationOptions = z.infer; +export type TSupportedApplications = z.infer; \ No newline at end of file diff --git a/src/types/ApplicationOptions.ts b/src/types/ApplicationOptions.ts deleted file mode 100644 index f8ac837..0000000 --- a/src/types/ApplicationOptions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { SupportedDatabaseEngines } from './SupportedDatabaseEngines'; - -export type ApplicationOptions = { - version: string; - database: SupportedDatabaseEngines; - port?: number; - contextPath?: string; - quickReload?: boolean; - license?: string; - clean?: boolean; - prune?: boolean; - devMode?: boolean; - debug?: boolean; -} \ No newline at end of file diff --git a/src/types/Database.ts b/src/types/Database.ts new file mode 100644 index 0000000..197d738 --- /dev/null +++ b/src/types/Database.ts @@ -0,0 +1,68 @@ +import { z } from 'zod'; + +import versions from '../../assets/versions.json'; + +export interface DatabaseEngine { + url: string; + options: TDatabaseOptions; + start(clean?: boolean): Promise; + stop(prune?: boolean): Promise; + run(sql: string | { query: string; values: unknown[] }): Promise; +} + +export const SupportedDatabaseEngines = z.enum([ + 'postgresql', + 'mysql', + 'mssql' +]); + +export const SupportedMSSQLEditions = z.enum([ + 'Developer', + 'Express', + 'Standard', + 'Enterprise', + 'EnterpriseCore' +]); + +export const DatabaseOptions = z.object({ + name: SupportedDatabaseEngines, + port: z.number().or(z.string().transform(Number).refine(item => !isNaN(item))), + database: z.string().default('dcdx'), + username: z.string().default('dcdx'), + password: z.string().default('dcdx'), + clean: z.boolean().default(false), + prune: z.boolean().default(false), + verbose: z.boolean().default(false), + driver: z.string() +}); + +export const PostgreSQLOptions = DatabaseOptions.extend({ + name: SupportedDatabaseEngines.refine(item => item === SupportedDatabaseEngines.Values.postgresql).default(SupportedDatabaseEngines.Values.postgresql), + tag: z.string().refine(item => versions[SupportedDatabaseEngines.Values.postgresql].includes(item)).default('latest'), + port: z.number().or(z.string().transform(Number).refine(item => !isNaN(item))).default(5432), + driver: z.string().refine(item => item === 'org.postgresql.Driver').default('org.postgresql.Driver') +}); + +export const MySQLOptions = DatabaseOptions.extend({ + name: SupportedDatabaseEngines.refine(item => item === SupportedDatabaseEngines.Values.mysql).default(SupportedDatabaseEngines.Values.mysql), + tag: z.string().refine(item => versions[SupportedDatabaseEngines.Values.mysql].includes(item)).default('latest'), + port: z.number().or(z.string().transform(Number).refine(item => !isNaN(item))).default(3306), + driver: z.string().refine(item => item === 'com.mysql.jdbc.Driver').default('com.mysql.jdbc.Driver') +}); + +export const MSSQLOptions = DatabaseOptions.extend({ + name: SupportedDatabaseEngines.refine(item => item === SupportedDatabaseEngines.Values.mssql).default(SupportedDatabaseEngines.Values.mssql), + tag: z.string().refine(item => versions[SupportedDatabaseEngines.Values.mssql].includes(item)).default('latest'), + edition: SupportedMSSQLEditions.default('Developer'), + port: z.number().or(z.string().transform(Number).refine(item => !isNaN(item))).default(1433), + username: z.string().refine(item => item === 'sa').default('sa'), + password: z.string().default('DataCenterDX!'), + driver: z.string().refine(item => item === 'com.microsoft.sqlserver.jdbc.SQLServerDriver').default('com.microsoft.sqlserver.jdbc.SQLServerDriver') +}); + +export type TDatabaseOptions = z.infer; +export type TPostgreSQLOptions = z.infer; +export type TMySQLOptions = z.infer; +export type TMSSQLOptions = z.infer; +export type TSupportedDatabaseEngines = z.infer; +export type TSupportedMSSQLEditions = z.infer; \ No newline at end of file diff --git a/src/types/DatabaseEngine.ts b/src/types/DatabaseEngine.ts deleted file mode 100644 index 73eb6b6..0000000 --- a/src/types/DatabaseEngine.ts +++ /dev/null @@ -1,15 +0,0 @@ -import EventEmitter from 'events'; - -import { DatabaseOptions } from './DatabaseOptions'; -import { SupportedDatabaseDrivers } from './SupportedDatabaseDrivers'; -import { SupportedDatabaseEngines } from './SupportedDatabaseEngines'; - -export interface DatabaseEngine extends EventEmitter { - name: SupportedDatabaseEngines; - url: string; - driver: SupportedDatabaseDrivers; - options: DatabaseOptions; - start(clean?: boolean): Promise; - stop(prune?: boolean): Promise; - run(sql: string | { query: string; values: unknown[] }): Promise<[unknown[], unknown]|null>; -} \ No newline at end of file diff --git a/src/types/DatabaseOptions.ts b/src/types/DatabaseOptions.ts deleted file mode 100644 index db95b76..0000000 --- a/src/types/DatabaseOptions.ts +++ /dev/null @@ -1,11 +0,0 @@ - - -export type DatabaseOptions = { - port: number; - database: string; - username: string; - password: string; - clean?: boolean; - prune?: boolean; - logging?: boolean; -} \ No newline at end of file diff --git a/src/types/SupportedApplications.ts b/src/types/SupportedApplications.ts deleted file mode 100644 index da7613a..0000000 --- a/src/types/SupportedApplications.ts +++ /dev/null @@ -1,7 +0,0 @@ - -export enum SupportedApplications { - JIRA = 'jira', - CONFLUENCE = 'confluence', - BITBUCKET = 'bitbucket', - BAMBOO = 'bamboo' -} \ No newline at end of file diff --git a/src/types/SupportedDatabaseDrivers.ts b/src/types/SupportedDatabaseDrivers.ts deleted file mode 100644 index 22be40c..0000000 --- a/src/types/SupportedDatabaseDrivers.ts +++ /dev/null @@ -1,7 +0,0 @@ - - -export type SupportedDatabaseDrivers = - 'com.microsoft.sqlserver.jdbc.SQLServerDriver' - |'com.mysql.jdbc.Driver' - |'oracle.jdbc.OracleDriver' - |'org.postgresql.Driver'; \ No newline at end of file diff --git a/src/types/SupportedDatabaseEngines.ts b/src/types/SupportedDatabaseEngines.ts deleted file mode 100644 index 8ffcfa1..0000000 --- a/src/types/SupportedDatabaseEngines.ts +++ /dev/null @@ -1,2 +0,0 @@ - -export type SupportedDatabaseEngines = 'postgresql'|'mysql'|'mssql'; \ No newline at end of file diff --git a/test/amps.test.js b/test/amps.test.js new file mode 100644 index 0000000..39d489c --- /dev/null +++ b/test/amps.test.js @@ -0,0 +1,995 @@ +/* + Unit tests for dcdx AMPS implementation + + The purpose of these tests is to cover all situations in which an error might occur + The focus lies on making sure that the user receives meaningful feedback + + The following scenario's are being tested: + + WITHOUT PROFILES + > There is no pom file + > The pom file is not an Atlassian Plugin + > The pom file does not have an AMPS configuration + > The pom file does not have a supported AMPS product + > The pom file does not have a version + > The pom file does not have a supported version + > The pom file has incorrect property replacement for the version + > The pom file has multiple AMPS configurations + + WITH A SINGLE PROFILE + > The pom file does not have an activated profile with AMPS configuration + > The pom file does not have an activated profile with a supported AMPS product + > The pom file does not have an activated profile with a version + > The pom file does not have an activated profile with a supported version + > The pom file does have an activated profile but has incorrect property replacement for the version + > The pom file has multiple AMPS configurations in the activated profile + + WITH MULTIPLE PROFILES + > The pom file does not have an AMPS configuration in any of the activated profiles + > The pom file does not have a supported AMPS product in any of the activated profiles + > The pom file does not have a version in any of the activated profiles + > The pom file does not have a supported version in any of the activated profiles + > The pom file has incorrect property replacement for the version in any of the activated profiles + > The pom file has multiple AMPS configurations in multple activated profiles + + LEGACY AMPS PLUGINS + > The pom file does not have a supported AMPS product (legacy) + > The pom file does not have a version (legacy) + > The pom file does not have a supported version (legacy) + > The pom file has incorrect property replacement for the version (legacy) + > The pom file has multiple AMPS configurations (legacy) +*/ + +import process, { cwd, stderr, stdout } from 'process' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import * as pomFiles from './fixtures/pomFiles'; + +const defaultCommandExecutionOptions = { + 'build': { + watch: false + }, + 'debug': { + clean: false, + database: 'postgresql', + debug: true, + port: '80', + prune: false, + watch: false, + xms: '1024m', + xmx: '1024m', + }, + 'reset': { + database: 'postgresql' + }, + 'run': { + clean: false, + database: 'postgresql', + debug: false, + port: '80', + prune: false, + xms: '1024m', + xmx: '1024m' + }, + 'stop': { + database: 'postgresql' + } +} + +let commandExecutionOptions = ''; +const mockExistsSync = vi.fn(); +const mockReadFileSync = vi.fn(); +const mockedDockerCompose = vi.hoisted(() => vi.fn().mockReturnValue(Promise.resolve())); + +beforeEach(() => { + vi.mock('exit-hook'); + vi.spyOn(process, 'on').mockImplementation(() => {}); + vi.spyOn(process, 'exit').mockImplementation(() => {}); + + vi.doMock('node:fs', async (importOriginal) => ({ + ...(await importOriginal()), + existsSync: mockExistsSync, + readFileSync: mockReadFileSync + })); + + vi.mock('docker-compose/dist/v2.js', async (importOriginal) => ({ + ...(await importOriginal()), + downAll: mockedDockerCompose, + execCompose: mockedDockerCompose, + ps: mockedDockerCompose, + upAll: mockedDockerCompose, + stop: mockedDockerCompose + })); + + vi.doMock('../src/helpers/ActionHandler.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + ActionHandler: (program, executor, options) => { + commandExecutionOptions = { ...(options || program.opts()) }; + return actual.ActionHandler(program, executor, options); + } + } + }); +}); + +afterEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + commandExecutionOptions = ''; + process.argv = [ 'vitest', cwd() ]; +}); + +[ 'build', 'debug', 'reset', 'run', 'stop' ].forEach(command => { + + describe(`Testing AMPS support for 'dcdx ${command}'`, async () => { + + /****************************************************************************** + * + * POM FILES WITHOUT PROFILE(S) + * + ******************************************************************************/ + + /* + There is no pom file + + Test the default command without providing arguments + and without a pom.xml file present in the current directory + */ + + it(`There is no pom file`, async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(false); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('Unable to find an Atlassian Plugin project in the current directory 🤔'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file is not an Atlassian Plugin + + Test the default command without providing arguments + and with a pom.xml file present that is not an Atlassian Plugin (atlassian-plugin) + */ + + it(`The pom file is not an Atlassian Plugin`, async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithoutAtlassianPackaging); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(1); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('Unable to find an Atlassian Plugin project in the current directory 🤔'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file does not have an AMPS configuration + + Test the default command without providing arguments + and with a pom.xml file present that has an Atlassian packaging + but does not have an AMPS configuration + */ + + it('The pom file does not have an AMPS configuration', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithoutProduct); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file does not have a supported AMPS product + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + but does not contain a supported AMPS plugin + */ + + it('The pom file does not have a supported AMPS product', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithInvalidProduct); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file does not have a version + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an AMPS plugin but without a element + */ + + it('The pom file does not have a version', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithProductButWithoutVersion); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(3); + expect(mockReadFileSync).toBeCalledTimes(3); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('Failed to determine version from AMPS and no product version provided (--tag)'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file does not have a supported version + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an AMPS plugin but without a element + */ + + it('The pom file does not have a supported version', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithProductButWithInvalidVersion); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain(`Product version '1000.000.000' is invalid.`); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file has incorrect property replacement for the version + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an AMPS plugin with a element that + uses a property that cannot be resolved during property replacement + */ + + it('The pom file has incorrect property replacement for the version', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithProductAndVersionWithIncorrectPropertyReplacement); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain(`Product version '\${invalidProperty}' is invalid.`); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file has multiple AMPS configurations + + Test the default command without providing arguments + and with a pom.xml file present that has an Atlassian packaging + but has multiple AMPS configurations + */ + + it('The pom file has multiple AMPS configurations', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithMultipleProducts); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project contains multiple AMPS configuration, unable to decide which product to use 😰'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /****************************************************************************** + * + * POM FILES WITH A SINGLE ACTIVATED PROFILE + * + ******************************************************************************/ + + /* + The pom file does not have an activated profile with AMPS configuration + + Test the default command without providing arguments and with a pom.xml + file present that has an Atlassian packaging but does not have an active + profile that has that has a valid AMPS configuration + */ + + it('The pom file does not have an activated profile with AMPS configuration', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithoutActiveProfile); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'invalid' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'invalid' + }); + }); + + /* + The pom file does not have an activated profile with a supported AMPS product + + Test the default command without providing arguments and with a pom.xml + file present that has an Atlassian packaging but does not have an active + profile that has that has a valid AMPS configuration + */ + + it('The pom file does not have an activated profile with a supported AMPS product', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithActiveProfileWithoutProducts); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active' + }); + }); + + /* + The pom file does not have an activated profile with a version + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an active profile with an AMPS plugin but without a element + */ + + it('The pom file does not have an activated profile with a version', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithActiveProfileWithoutVersion); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(3); + expect(mockReadFileSync).toBeCalledTimes(3); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('Failed to determine version from AMPS and no product version provided (--tag)'); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active' + }); + }); + + /* + The pom file does not have an activated profile with a supported version + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an active profile with an AMPS plugin but with a element + that does not contain a valid version + */ + + it('The pom file does not have an activated profile with a supported version', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithActiveProfileWithoutSupportedVersion); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain(`Product version '1000.000.000' is invalid.`); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active' + }) + }); + + /* + The pom file does have an activated profile but has incorrect property replacement for the version + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an active profile with an AMPS plugin with a element + that uses a property that cannot be resolved during property replacement + */ + + it('The pom file does have an activated profile but has incorrect property replacement for the version', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithActiveProfileAndVersionWithIncorrectPropertyReplacement); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain(`Product version '\${invalidProperty}' is invalid.`); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active' + }); + }); + + /* + The pom file has multiple AMPS configurations in the activated profile + + Test the default command without providing arguments and with a pom.xml + file present that has an Atlassian packaging and an active profile + that has that has multiple valid AMPS configurations + */ + + it('The pom file has multiple AMPS configurations in the activated profile', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithActiveProfileWithMultipleProducts); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project contains multiple AMPS configuration, unable to decide which product to use 😰'); + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandExecutionOptions[command], activateProfiles: 'active' }); + }); + + /****************************************************************************** + * + * POM FILES WITH MULTIPLE ACTIVATED PROFILES + * + ******************************************************************************/ + + /* + The pom file does not have an AMPS configuration in any of the activated profiles + + Test the default command without providing arguments and with a pom.xml + file present that has an Atlassian packaging and an multiple active profiles + with none of them having a valid AMPS configurations + */ + + it('The pom file does not have an AMPS configuration in any of the activated profiles', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithMultipleActiveProfilesWithoutProducts); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active1,active2' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active1,active2' + }); + }); + + /* + The pom file does not have a supported AMPS product in any of the activated profiles + + Test the default command without providing arguments and with a pom.xml + file present that has an Atlassian packaging and an multiple active profiles + with none of them having a valid AMPS configurations with supported products + */ + + it('The pom file does not have a supported AMPS product in any of the activated profiles', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithMultipleActiveProfilesWithoutValidProducts); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active1,active2' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active1,active2' + }); + }); + + /* + The pom file does not have a version in any of the activated profiles + + Test the default command without providing arguments and with a pom.xml + file present that has an Atlassian packaging and an multiple active profiles + with none of them having a valid AMPS configurations with supported products + */ + + it('The pom file does not have a version in any of the activated profiles', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithMultipleActiveProfilesWithASingleProductWithoutVersion); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active1,active2' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(3); + expect(mockReadFileSync).toBeCalledTimes(3); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('Failed to determine version from AMPS and no product version provided (--tag)'); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active1,active2' + }); + }); + + /* + The pom file does not have a supported version in any of the activated profiles + + Test the default command without providing arguments and with a pom.xml + file present that has an Atlassian packaging and an multiple active profiles + with none of them having a valid AMPS configurations with supported products + */ + + it('The pom file does not have a supported version in any of the activated profiles', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithMultipleActiveProfilesWithASingleProductWithoutValidVersion); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active1,active2' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain(`Product version '1000.000.000' is invalid.`); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active1,active2' + }); + }); + + /* + The pom file has incorrect property replacement for the version in any of the activated profiles + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has multiple active profiles with an AMPS plugin with a element + that uses a property that cannot be resolved during property replacement + */ + + it('The pom file has incorrect property replacement for the version in any of the activated profiles', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithMultipleActiveProfilesAndVersionWithIncorrectPropertyReplacement); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active1,active2' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain(`Product version '\${invalidProperty}' is invalid.`); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandExecutionOptions[command], + activateProfiles: 'active1,active2' + }); + }); + + /* + The pom file has multiple AMPS configurations in multple activated profiles + + Test the default command without providing arguments and with a pom.xml + file present that has an Atlassian packaging and an active profile + that has that has multiple valid AMPS configurations + */ + + it('The pom file has multiple AMPS configurations in multple activated profiles', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.pomFileWithMultipleActiveProfilesWithMultipleProducts); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + process.argv = [ 'vitest', 'dcdx', '-P', 'active1,active2' ] + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(2); + expect(mockReadFileSync).toBeCalledTimes(2); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project contains multiple AMPS configuration, unable to decide which product to use 😰'); + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandExecutionOptions[command], activateProfiles: 'active1,active2' }); + }); + + /****************************************************************************** + * + * POM FILES WITH LEGACY AMPS PLUGINS + * + ******************************************************************************/ + + /* + The pom file does not have a supported AMPS product (legacy) + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + but does not contain a supported AMPS plugin (legacy) + */ + + it('The pom file does not have a supported AMPS product (legacy)', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.legacyPomFileWithInvalidProduct); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(4); + expect(mockReadFileSync).toBeCalledTimes(4); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project does not contain an AMPS configuration, unable to detect product 😰'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file does not have a version (legacy) + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an AMPS plugin but without a element (legacy) + */ + + it('The pom file does not have a version (legacy)', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.legacyPomFileWithProductButWithoutVersion); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(3); + expect(mockReadFileSync).toBeCalledTimes(3); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('Failed to determine version from AMPS and no product version provided (--tag)'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file does not have a supported version (legacy) + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an AMPS plugin but without a element (legacy) + */ + + it('The pom file does not have a supported version (legacy)', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.legacyPomFileWithProductButWithInvalidVersion); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain(`Product version '1000.000.000' is invalid.`); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file has incorrect property replacement for the version (legacy) + + Test the default command without providing arguments + and with a pom.xml file present that is an Atlassian Plugin + and has an AMPS plugin with a element that + uses a property that cannot be resolved during property replacement (legacy) + */ + + it('The pom file has incorrect property replacement for the version (legacy)', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.legacyPomFileWithProductAndVersionWithIncorrectPropertyReplacement); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain(`Product version '\${invalidProperty}' is invalid.`); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + + /* + The pom file has multiple AMPS configurations (legacy) + + Test the default command without providing arguments + and with a pom.xml file present that has an Atlassian packaging + but has multiple AMPS configurations + */ + + it('The pom file has multiple AMPS configurations (legacy)', async () => { + let stdOut = ''; + let stdErr = ''; + + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(pomFiles.legacyPomFileWithMultipleProducts); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + + await import(`../src/commands/${command}`); + + expect(mockedDockerCompose).toBeCalledTimes(0); + expect(mockExistsSync).toBeCalledTimes(6); + expect(mockReadFileSync).toBeCalledTimes(6); + + expect(stdOut).toContain( + command === 'build' || command === 'debug' + ? 'Successfully stopped all running processes 💪' + : '' + ); + expect(stdErr).toContain('The Atlassian Plugin project contains multiple AMPS configuration, unable to decide which product to use 😰'); + expect(commandExecutionOptions).toStrictEqual(defaultCommandExecutionOptions[command]); + }); + }); +}); diff --git a/test/build.test.js b/test/build.test.js new file mode 100644 index 0000000..247c94d --- /dev/null +++ b/test/build.test.js @@ -0,0 +1,1277 @@ +import { cwd } from 'node:process'; + +import process, { stderr, stdout } from 'process' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import versions from '../assets/versions.json'; +import { SupportedApplications } from '../src/types/Application'; +import { getValidLegacyPomFileFor, getValidPomFileFor } from './fixtures/pomFiles'; + +let stdOut = ''; +let stdErr = ''; + +let commandExecutionOptions = ''; +let fsWatcherPaths = ''; +let fsWatcherOptions = null; +let fsWatcher = null; + +const mockExistsSync = vi.fn(); +const mockReadFileSync = vi.fn(); +const mockRecursiveBuild = vi.fn(); +const mockedBuild = vi.fn(); +const mockFSWatcherAdd = vi.fn(); +const mockedDockerRunningContainerIds = vi.fn(); +const mockedDockerCopy = vi.fn(); + +const defaultWatchOptions = { + cwd: cwd(), + usePolling: true, + interval: 2 * 1000, + binaryInterval: 2 * 1000, + awaitWriteFinish: true, + persistent: true, + atomic: true +} + +beforeEach(() => { + stdOut = ''; + stdErr = ''; + + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + vi.spyOn(process, 'on').mockImplementation(() => {}); + vi.spyOn(process, 'exit').mockImplementation(() => {}); + vi.mock('exit-hook'); + + vi.doMock('node:fs', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + existsSync: mockExistsSync, + readFileSync: mockReadFileSync + } + }); + + vi.doMock('chokidar', async (importOriginal) => { + const actual = await importOriginal(); + actual.FSWatcher.prototype.add = mockFSWatcherAdd; + return { + ...actual, + watch: (paths, options) => { + fsWatcherPaths = paths; + fsWatcherOptions = options; + fsWatcher = actual.watch(paths, { ...options, useFsEvents: false }); + return fsWatcher; + } + } + }); + + vi.doMock('../src/helpers/docker.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getRunningContainerIds: mockedDockerRunningContainerIds, + copy: mockedDockerCopy + } + }); + + mockedBuild.mockReturnValue(Promise.resolve()); + vi.doMock('../src/helpers/amps.ts', async (importOriginal) => { + const actual = await importOriginal(); + actual.AMPS.prototype.build = mockedBuild; + return actual; + }); + + vi.doMock('../src/helpers/isRecursiveBuild.ts', () => ({ + isRecursiveBuild: mockRecursiveBuild + })); + + vi.doMock('../src/helpers/ActionHandler.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + ActionHandler: (program, executor, options) => { + commandExecutionOptions = options || program.opts(); + return actual.ActionHandler(program, executor, options); + } + } + }); +}); + +afterEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + stdOut = ''; + stdErr = ''; + commandExecutionOptions = ''; + fsWatcher = null; + fsWatcherPaths = ''; + fsWatcherOptions = null; + process.argv = [ 'vitest', cwd() ]; +}) + +Object.values(SupportedApplications.Values).forEach(name => { + + const tag = versions[name][Math.floor(Math.random()*versions[name].length)]; + + describe(`dcdx build - ${name}`, async () => { + + /** + * Building the plugin with default configuration + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin + */ + it(`dcdx build`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + watch: false + }); + }); + + /** + * Use an active profile for Apache Maven + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin + * based on the activated Apache Maven profile + */ + it(`dcdx build -P active`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag, 'active')); + + process.argv = [ 'vitest', cwd(), '-P', 'active' ] + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledWith([ '-P', 'active' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + watch: false, + activateProfiles: 'active' + }); + }); + + /** + * Use a legacy Atlassian Maven Plugin Suite product plugin + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin + * based on one of the legacy AMPS product plugins + */ + it(`dcdx build (legacy AMPS plugin)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidLegacyPomFileFor(name, tag)); + + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + watch: false + }); + }); + + /** + * Add a file system watcher + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin + * and also start a file system watcher detecting changes + */ + it(`dcdx build --watch (no change)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true + }); + }); + + /** + * Trigger a rebuild by changing a source file + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It also triggers a second build due to a change in a source file + */ + it(`dcdx build --watch (change triggerd to src/somefile.java)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(2); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true + }); + }); + + /** + * Warn the user for recursive builds due to repetitive change detection + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It also warns the user for a recursive build due to repetitive changes + * This could be due to incorrect outputDirectory configuration or IDE settings + */ + it(`dcdx build --watch (repetitive change triggerd to src/somefile.java)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/build'); + + mockRecursiveBuild.mockReturnValue(false); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + mockRecursiveBuild.mockReturnValue(true); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(2); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 + +=============================================================================================================== +Recursive build trigger detected. The last build completed last than 5 seconds ago +This may indicate that the build changes files outside of the output directory +Alternatively, Maven is using a different output directory than configured: +'target' + +Please make sure to check your build process and/or specify a different output directory using the '-o' option +=============================================================================================================== +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true + }); + }); + + /** + * Build the plugin and ignore the file change in the output directory + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It ignores the file change in the output directory because it isn't a JAR file + */ + it(`dcdx build --watch (change triggerd in output directory)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/somefile.class'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true + }); + }); + + /** + * Build the plugin and ignore the JAR in the output directory + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It ignores the JAR in the output directory because the --install flag is missing + */ + it(`dcdx build --watch (change triggerd by JAR file, without -i)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true + }); + }); + + /** + * Build the plugin and try to install the plugin, but fail due to no running application + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It tries to install the JAR file, but fails because there is no running container + */ + it(`dcdx build --watch --install (change triggerd by JAR file, with -i but without containers)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([]); + + process.argv = [ 'vitest', cwd(), '--watch', '-i' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +There are no running instance of ${name}, unable to install plugin 🤔 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + install: true + }); + }); + + /** + * Build the plugin and try to install the plugin, but fail due to multiple running application + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It tries to install the JAR file, but fails because there are multiple running containers + */ + it(`dcdx build --watch --install (change triggerd by JAR file, with -i with multiple containers)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a', 'b' ]); + + process.argv = [ 'vitest', cwd(), '--watch', '-i' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +There are multple running instance of ${name}, unable to determine which one to use 🤔 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + install: true + }); + }); + + /** + * Build the plugin and install the plugin + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It installs the JAR file into the running container + */ + it(`dcdx build --watch --install (change triggerd by JAR file, with -i and a running instance)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a' ]); + mockedDockerCopy.mockReturnValue(Promise.resolve()); + + process.argv = [ 'vitest', cwd(), '--watch', '-i' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Found updated JAR file, uploading them to QuickReload on running instances of ${name} +Finished uploading JAR file to QuickReload +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + install: true + }); + }); + + /** + * Build the plugin and detect changes in 'target' because of alternative output directory + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It detects a change to a file in 'target' because of an alternative output directory + */ + it(`dcdx build --watch --install --outputDirectory dist (change triggerd by JAR file, in different output directory)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a' ]); + mockedDockerCopy.mockReturnValue(Promise.resolve()); + + process.argv = [ 'vitest', cwd(), '--watch', '-i', '--outputDirectory', 'dist' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/someFile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(2); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + install: true, + outputDirectory: 'dist' + }); + }); + + /** + * Build the plugin and install the JAR from an alternative output directory + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It detects a change to a JAR in an alternative output directory and installs it + */ + it(`dcdx build --watch --install --outputDirectory dist (change triggerd by JAR file, with -i and a running container)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a' ]); + mockedDockerCopy.mockReturnValue(Promise.resolve()); + + process.argv = [ 'vitest', cwd(), '--watch', '-i', '--outputDirectory', 'dist' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'dist/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Found updated JAR file, uploading them to QuickReload on running instances of ${name} +Finished uploading JAR file to QuickReload +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + install: true, + outputDirectory: 'dist' + }); + }); + + /** + * Build the plugin and watch for a specific extension + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It listens to file changes to a specific extension + */ + it(`dcdx build --watch --ext **/*.java (no change)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Build the plugin and rebuild because a change in a file with a specific extension + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It listens to file changes to a specific extension and rebuilds the plugin + * because a file with such extension was changed + */ + it(`dcdx build --watch --ext **/*.java (change triggerd to src/somefile.java)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(2); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Build the plugin and ignore a change in a file that does not match the specific extension + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It listens to file changes to a specific extension and ignores changes to files + * that do not have the specified extension + */ + it(`dcdx build --watch --ext **/*.java (change triggerd to src/somefile.txt - ignored)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Build the plugin and warn for recursive build due to multiple changes to a file with the specific extension + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It also warns the user for a recursive build due to repetitive changes to files with the specified extension + * This could be due to incorrect outputDirectory configuration or IDE settings + */ + it(`dcdx build --watch --ext **/*.java (repetitive change triggerd to src/somefile.java)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/build'); + + mockRecursiveBuild.mockReturnValue(false); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + mockRecursiveBuild.mockReturnValue(true); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(2); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 + +=============================================================================================================== +Recursive build trigger detected. The last build completed last than 5 seconds ago +This may indicate that the build changes files outside of the output directory +Alternatively, Maven is using a different output directory than configured: +'target' + +Please make sure to check your build process and/or specify a different output directory using the '-o' option +=============================================================================================================== +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Build the plugin and ignore changes to a file without the specified extension, + * even if it is triggered in the 'target' directory with an alternative output directory + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It will ignore the file change to a file in the 'target' directory, even though an + * alternative output directory was specified, because the file does not have the + * correct file extension + */ + it(`dcdx build --watch --ext **/*.java (change triggerd in output directory)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/somefile.class'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Build the plugin and ignore the JAR in the output directory + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It ignores the JAR in the output directory because the --install flag is missing + */ + it(`dcdx build --watch --ext **/*.java (change triggerd by JAR file, without -i)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Build the plugin and try to install the plugin, but fail due to no running application + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It tries to install the JAR file, but fails because there is no running container + */ + it(`dcdx build --watch --ext **/*.java -i (change triggerd by JAR file, with -i but without containers)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([]); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java', '-i' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +There are no running instance of ${name}, unable to install plugin 🤔 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + install: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Build the plugin and try to install the plugin, but fail due to multiple running application + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It tries to install the JAR file, but fails because there are multiple running containers + */ + it(`dcdx build --watch --ext **/*.java -i (change triggerd by JAR file, with -i with multiple containers)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a', 'b' ]); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java', '-i' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +There are multple running instance of ${name}, unable to determine which one to use 🤔 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + install: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Build the plugin and install the plugin + * + * This will use the AMPS configuration found in the directory + * to succesfully build the Atlassian Data Center plugin. + * It installs the JAR file into the running container + */ + it(`dcdx build --watch --ext **/*.java -i (change triggerd by JAR file, with -i and a running instance)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a' ]); + mockedDockerCopy.mockReturnValue(Promise.resolve()); + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java', '-i' ]; + await import('../src/commands/build'); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Found updated JAR file, uploading them to QuickReload on running instances of ${name} +Finished uploading JAR file to QuickReload +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true, + install: true, + ext: [ '**/*.java' ] + }); + }); + + /** + * Fail to build the plugin because an invalid argument was provided (--ext) + * + * The command will fail before building the application because the --ext + * argument is invalid without --watch enabled as well + */ + it(`dcdx build --ext **/*.java`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv = [ 'vitest', cwd(), '--ext', '**/*.java' ]; + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + + expect(stdErr).toContain('InvalidArgumentError: Invalid argument "--ext"'); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + watch: false, + ext: [ '**/*.java' ] + }); + }); + + /** + * Fail to build the plugin because an invalid argument was provided (--install) + * + * The command will fail before building the application because the --install + * argument is invalid without --watch enabled as well + */ + it(`dcdx build --install`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv = [ 'vitest', cwd(), '--install' ]; + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + + expect(stdErr).toContain('InvalidArgumentError: Invalid argument "--install"'); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + watch: false, + install: true + }); + }); + + /** + * Fail to build the plugin because an invalid argument was provided (--outputDirectory) + * + * The command will fail before building the application because the --outputDirectory + * argument is invalid without --watch enabled as well + */ + it(`dcdx build --outputDirectory dist`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv = [ 'vitest', cwd(), '--outputDirectory', 'dist' ]; + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + + expect(stdErr).toContain('InvalidArgumentError: Invalid argument "--outputDirectory"'); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + watch: false, + outputDirectory: 'dist' + }); + }); + + /** + * Fail to build the plugin because an invalid argument was provided (--cwd) + * + * The command will fail before building the application because the --cwd + * argument is invalid without --watch enabled as well + */ + it(`dcdx build --cwd path/to/someDirectory`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv = [ 'vitest', cwd(), '--cwd', 'path/to/someDirectory' ]; + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + + expect(stdErr).toContain(''); + expect(stdOut).toBe(` +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + watch: false, + cwd: 'path/to/someDirectory' + }); + }); + + /** + * Fail to build the plugin because of Apache Maven error + * + * The command will fail because of an error in Apache Maven + * This does not include further information in the test + * but should normally display Maven errors in the console + */ + it(`dcdx build (failed)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedBuild.mockRejectedValue(null); + + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Building Atlassian Data Center plugin for ${name}... 💃 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + watch: false + }); + }); + + /** + * Fail to build the plugin because of Apache Maven error + * + * The command will fail because of an error in Apache Maven + * This does not include further information in the test + * but should normally display Maven errors in the console + */ + it(`dcdx build --watch (failed)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedBuild.mockRejectedValue(null); + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/build'); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Stopping filesystem watcher... ⏳ +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true + }); + }); + + /** + * Fail to rebuild the plugin because of Apache Maven error + * + * Succcesfully build the plugin on start, but fail the + * rebuild triggered by a file change. + * This does not include further information in the test + * but should normally display Maven errors in the console + */ + it(`dcdx build --watch (change triggerd to src/somefile.java, build failed)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/build'); + + mockedBuild.mockRejectedValue(null); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(2); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Building Atlassian Data Center plugin for ${name}... 💃 +Finished building Atlassian Data Center plugin for ${name}... 💪 +Detected file change, rebuilding Atlasian Data Center plugin +Failed to build Atlassian Data Center plugin for ${name}... 😰 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + watch: true + }); + }); + + }); +}); \ No newline at end of file diff --git a/test/database.test.js b/test/database.test.js new file mode 100644 index 0000000..f004c41 --- /dev/null +++ b/test/database.test.js @@ -0,0 +1,641 @@ +import { cwd } from 'node:process'; +import { EventEmitter } from 'node:stream'; + +import process, { stderr, stdout } from 'process' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import versions from '../assets/versions.json'; +import { getZodDefaults } from '../src/helpers/getZodDefaults'; +import { MSSQLOptions, MySQLOptions, PostgreSQLOptions, SupportedDatabaseEngines } from '../src/types/Database'; + +let stdOut = ''; +let stdErr = ''; + +let commandExecutionOptions = {}; +const SpawnEventEmitter = new EventEmitter(); + +const mockedDownAll = vi.fn(); +const mockedPS = vi.fn(); +const mockedStop = vi.fn(); +const mockedUpAll = vi.fn(); +const mockedAuthenticate = vi.fn(); +const mockedQuery = vi.fn(); +const mockedSpawn = vi.fn().mockImplementation(() => SpawnEventEmitter); + +beforeEach(() => { + stdOut = ''; + stdErr = ''; + + mockedStop.mockResolvedValue(true); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + vi.spyOn(process, 'on').mockImplementation(() => {}); + vi.spyOn(process, 'exit').mockImplementation(() => {}); + vi.mock('exit-hook'); + + vi.doMock('node:child_process', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + spawn: mockedSpawn + } + }); + + vi.doMock('sequelize', async (importOriginal) => { + const actual = await importOriginal(); + actual.Sequelize.prototype.authenticate = mockedAuthenticate; + actual.Sequelize.prototype.query = mockedQuery; + return actual; + }); + + vi.doMock('docker-compose/dist/v2.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + downAll: mockedDownAll, + ps: mockedPS, + stop: mockedStop, + upAll: mockedUpAll + } + }); + + vi.doMock('../src/helpers/ActionHandler.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + ActionHandler: (program, executor, options) => { + commandExecutionOptions = options || program.opts(); + return actual.ActionHandler(program, executor, options); + } + } + }); +}); + +afterEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + stdOut = ''; + stdErr = ''; + commandExecutionOptions = {}; + process.argv = [ 'vitest', cwd() ]; +}) + +Object.values(SupportedDatabaseEngines.Values).forEach(name => { + + const tag = versions[name][Math.floor(Math.random()*versions[name].length)]; + const defaultOptions = getZodDefaults( + name === 'postgresql' + ? PostgreSQLOptions + : name === 'mysql' + ? MySQLOptions + : MSSQLOptions); + delete defaultOptions.driver; + + describe(`dcdx database - ${name}`, async () => { + + /** + * Fail to start a database if no arguments are provided + */ + it(`dcdx database`, async () => { + await import('../src/commands/database'); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + + expect(stdErr.startsWith('Usage: dcdx database [options] [command]')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(commandExecutionOptions).toStrictEqual({}); + }); + + /** + * Start the database + * + * Succesfully start the database using default options + */ + it(`dcdx database ${name}`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name }); + }); + + /** + * Fail to verify the status of the database + * + * Succesfully start the database using default options + * but fail to connect & authenticate the user + */ + it(`dcdx database ${name} (failed to start)`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockRejectedValue(new Error()); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(1); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Failed to verify status of ${name} ⛔ +Stopping ${name}... ⏳ +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name }); + }); + + /** + * Succesfully start the database with the provided tag + * + * Succesfully start the database using default options + * but from the specified Docker tag + */ + it(`dcdx database ${name} --tag ${tag}`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--tag', tag ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, tag }); + }); + + /** + * Fail to start the database with the provided tag + * + * The database cannot be started because the tag is invalid + */ + it(`dcdx database ${name} --tag invalid`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--tag', 'invalid' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith(`error: option '-t, --tag ' argument 'invalid' is invalid`)).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(commandExecutionOptions).toStrictEqual({}); + }); + + /** + * Succesfully start the database and provision a database + * + * The database enigine is started and a new database is initialized + * using the provided name + */ + it(`dcdx database ${name} --database myFirstDB`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--database', 'myFirstDB' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, database: 'myFirstDB' }); + }); + + /** + * Fail to initialise the database + * + * The database enigine fails to start because of an error + * while trying to provision a new database + */ + it.skipIf(name === 'postgresql')(`dcdx database ${name} --database myFirstDB (failed to initialize)`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockRejectedValue(new Error()); + + process.argv = [ 'vitest', cwd(), name, '--database', 'myFirstDB' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(1); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(1); + + expect(stdErr.startsWith(`An error occurred while trying to run the following SQL query:`)).toBeTruthy(); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +Stopping ${name}... ⏳ +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, database: 'myFirstDB' }); + }); + + /** + * Succesfully start the database using a different port + * + * The database enigine is started using the specified port + */ + it(`dcdx database ${name} --port 1234`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--port', '1234' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:1234 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, port: '1234' }); + }); + + /** + * Succesfully start the database using the specified username + */ + it.skipIf(name === 'mssql')(`dcdx database ${name} --username atlassian`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--username', 'atlassian' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, username: 'atlassian' }); + }); + + /** + * Succesfully start the database using the specified edition + */ + it.skipIf(name !== 'mssql')(`dcdx database ${name} --edition Enterprise`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--edition', 'Enterprise' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, edition: 'Enterprise' }); + }); + + /** + * Fail to start the database using the specified edition + */ + it.skipIf(name !== 'mssql')(`dcdx database ${name} --edition invalid`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--edition', 'invalid' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith(`error: option '-e, --edition ' argument 'invalid' is invalid`)).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(commandExecutionOptions).toStrictEqual({}); + }); + + /** + * Successfully start the database using the specified password + */ + it(`dcdx database ${name} --password atlassian`, async () => { + + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--password', 'atlassian' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, password: 'atlassian' }); + }); + + /** + * Successfully start the database with "clean" option + */ + it(`dcdx database ${name} --clean`, async () => { + + mockedDownAll.mockReturnValue(Promise.resolve()); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--clean' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(1); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, clean: true }); + }); + + /** + * Successfully start the database with "prune" option + */ + it(`dcdx database ${name} --prune`, async () => { + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--prune' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, prune: true }); + }); + + /** + * Successfully prune the database after it fails to start + */ + it(`dcdx database ${name} --prune (after failure to start)`, async () => { + + mockedDownAll.mockReturnValue(Promise.resolve()); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockRejectedValue(new Error()); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--prune' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(1); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Failed to verify status of ${name} ⛔ +Stopping ${name}... ⏳ +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, prune: true }); + }); + + /** + * Successfully start the database with verbose logging + */ + it(`dcdx database ${name} --verbose`, async () => { + mockedPS.mockResolvedValue({ data: { services: [ { name }] }}); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--verbose' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + // Shut down docker gracefully + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(1); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, verbose: true }); + }); + + /** + * Successfully start the database but stop after Docker quits unexpectedly + */ + it(`dcdx database ${name} --verbose (docker quits unexpectedly)`, async () => { + mockedPS.mockResolvedValue({ data: { services: [ { name }] }}); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--verbose' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + // Shut down docker unexpectedly + SpawnEventEmitter.emit('exit', 1); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(1); + expect(mockedStop).toBeCalledTimes(1); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +Stopping ${name}... ⏳ +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, verbose: true }); + }); + + /** + * Successfully start the database and fail to stop after Docker quits unexpectedly + */ + it(`dcdx database ${name} --verbose (docker quits unexpectedly, fails to stop ${name})`, async () => { + mockedPS.mockResolvedValue({ data: { services: [ { name }] }}); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedStop.mockRejectedValue(new Error()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + + process.argv = [ 'vitest', cwd(), name, '--verbose' ]; + await import('../src/commands/database'); + await new Promise(resolve => process.nextTick(resolve)); + // Shut down docker unexpectedly + SpawnEventEmitter.emit('exit', 1); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(1); + expect(mockedStop).toBeCalledTimes(1); + expect(mockedUpAll).toBeCalledTimes(1); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(name === 'postgresql' ? 0 : name === 'mysql' ? 1 : 3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting instance of ${name}... 💃 +Database is ready and accepting connections on localhost:${defaultOptions.port} 🗄️ +Stopping ${name}... ⏳ +Failed to stopped ${name}, manual action is required +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultOptions, name, verbose: true }); + }); + + }); +}); \ No newline at end of file diff --git a/test/debug.test.js b/test/debug.test.js new file mode 100644 index 0000000..efd39f0 --- /dev/null +++ b/test/debug.test.js @@ -0,0 +1,2904 @@ +import { setMaxListeners } from 'node:events'; +import { cwd } from 'node:process'; +import { EventEmitter } from 'node:stream'; + +import axios from 'axios'; +import process, { stderr, stdout } from 'process' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import versions from '../assets/versions.json'; +import { SupportedApplications } from '../src/types/Application'; +import { getValidLegacyPomFileFor, getValidPomFileFor } from './fixtures/pomFiles'; + +let stdOut = ''; +let stdErr = ''; + +const SpawnEventEmitter = new EventEmitter(); +let commandExecutionOptions = ''; +let fsWatcherPaths = ''; +let fsWatcherOptions = null; +let fsWatcher = null; + +const mockExistsSync = vi.fn(); +const mockReadFileSync = vi.fn(); +const mockRecursiveBuild = vi.fn(); +const mockedBuild = vi.fn(); +const mockFSWatcherAdd = vi.fn(); +const mockedDockerRunningContainerIds = vi.fn(); +const mockedDockerCopy = vi.fn(); +const mockedClone = vi.fn(); +const mockedPull = vi.fn(); +const mockedSpawn = vi.fn().mockImplementation(() => SpawnEventEmitter); +const mockedDownAll = vi.fn(); +const mockedPS = vi.fn(); +const mockedStop = vi.fn(); +const mockedUpAll = vi.fn(); +const mockedAuthenticate = vi.fn(); +const mockedQuery = vi.fn(); + +const defaultCommandOptions = { + clean: false, + database: 'postgresql', + debug: true, + port: '80', + prune: false, + watch: false, + xms: '1024m', + xmx: '1024m', +} + +const defaultWatchOptions = { + cwd: cwd(), + usePolling: true, + interval: 2 * 1000, + binaryInterval: 2 * 1000, + awaitWriteFinish: true, + persistent: true, + atomic: true +} + +beforeEach(() => { + stdOut = ''; + stdErr = ''; + + setMaxListeners(300); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + vi.spyOn(process, 'on').mockImplementation(() => {}); + vi.spyOn(process, 'exit').mockImplementation(() => {}); + vi.mock('exit-hook'); + + vi.doMock('node:fs', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + existsSync: mockExistsSync, + readFileSync: mockReadFileSync + } + }); + + vi.doMock('node:child_process', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + spawn: mockedSpawn + } + }); + + vi.doMock('sequelize', async (importOriginal) => { + const actual = await importOriginal(); + actual.Sequelize.prototype.authenticate = mockedAuthenticate; + actual.Sequelize.prototype.query = mockedQuery; + return actual; + }); + + vi.doMock('docker-compose/dist/v2.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + downAll: mockedDownAll, + ps: mockedPS, + stop: mockedStop, + upAll: mockedUpAll + } + }); + + vi.mock('simple-git', () => ({ + default: vi.fn(() => ({ + pull: mockedPull, + clone: mockedClone + })) + })); + + vi.doMock('chokidar', async (importOriginal) => { + const actual = await importOriginal(); + actual.FSWatcher.prototype.add = mockFSWatcherAdd; + return { + ...actual, + watch: (paths, options) => { + fsWatcherPaths = paths; + fsWatcherOptions = options; + fsWatcher = actual.watch(paths, { ...options, useFsEvents: false }); + return fsWatcher; + } + } + }); + + vi.doMock('../src/helpers/docker.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + getRunningContainerIds: mockedDockerRunningContainerIds, + copy: mockedDockerCopy + } + }); + + mockedBuild.mockReturnValue(Promise.resolve()); + vi.doMock('../src/helpers/amps.ts', async (importOriginal) => { + const actual = await importOriginal(); + actual.AMPS.prototype.build = mockedBuild; + return actual; + }); + + vi.doMock('../src/helpers/isRecursiveBuild.ts', () => ({ + isRecursiveBuild: mockRecursiveBuild + })); + + vi.doMock('../src/helpers/ActionHandler.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + ActionHandler: (program, executor, options) => { + commandExecutionOptions = { ...(options || program.opts()) }; + return actual.ActionHandler(program, executor, options); + } + } + }); +}); + +afterEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + stdOut = ''; + stdErr = ''; + commandExecutionOptions = ''; + fsWatcher = null; + fsWatcherPaths = ''; + fsWatcherOptions = null; + process.argv = [ 'vitest', cwd() ]; + setMaxListeners(); +}) + +Object.values(SupportedApplications.Values).forEach(name => { + + const tag = versions[name][Math.floor(Math.random()*versions[name].length)]; + + describe(`dcdx debug - ${name}`, async () => { + + it(`dcdx debug (git clone)`, async () => { + mockExistsSync.mockImplementation((path) => { + if (path.endsWith('.xml')) { + return true; + } else { + return false; + } + }); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx debug (git pull)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx debug (docker quits unexpected)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 1); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith('Error: Docker exited with code 1')).toBeTruthy(); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx debug (fails to become available)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: name !== 'bamboo' ? 200 : 204, + data: { status: 'FAILED' } + }) + + vi.useFakeTimers(); + + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // Run all the timers + await vi.runAllTimersAsync(); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(301); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + let counter = ''; + [...Array(301)].forEach((_, index) => counter += `Waiting for ${name} to become available... ${index}s\n`); + + expect(stdErr).toBe(`A timeout occurred while waiting for ${name} to become available ⛔`.trim() + '\n'); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +${counter.trim()} +Failed to start ${name} ⛔ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx debug (fails to become available - Axios error)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockRejectedValue({ + status: 500 + }) + + vi.useFakeTimers(); + + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // Run all the timers + await vi.runAllTimersAsync(); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(301); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + let counter = ''; + [...Array(301)].forEach((_, index) => counter += `Waiting for ${name} to become available... ${index}s\n`); + + expect(stdErr).toBe(`A timeout occurred while waiting for ${name} to become available ⛔`.trim() + '\n'); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +${counter.trim()} +Failed to start ${name} ⛔ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx debug --tag latest`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--tag', 'latest' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(5); + expect(mockReadFileSync).toBeCalledTimes(4); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + tag: 'latest' + }); + }); + + it(`dcdx debug --tag invalid`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--tag', 'invalid' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(4); + expect(mockReadFileSync).toBeCalledTimes(4); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith(`Error: Product version 'invalid' is invalid. Allowed choices are`)).toBeTruthy(); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + tag: 'invalid' + }); + }); + + it(`dcdx debug --database mysql`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--database', 'mysql' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of mysql... 💃 +Database is ready and accepting connections on localhost:3306 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + database: 'mysql' + }); + }); + + it(`dcdx debug --database mssql`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--database', 'mssql' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of mssql... 💃 +Database is ready and accepting connections on localhost:1433 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + database: 'mssql' + }); + }); + + it(`dcdx debug --database invalid`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--database', 'invalid' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(0); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith(`error: option '-d, --database ' argument 'invalid' is invalid. Allowed choices are postgresql, mysql, mssql.`)).toBeTruthy(); + expect(stdOut).toBe(''); + expect(commandExecutionOptions).toBe(''); + }); + + it(`dcdx debug --port 1234`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--port', '1234' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:1234 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + port: '1234' + }); + }); + + it(`dcdx debug --contextPath atlassian`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '-c', 'atlassian' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80/atlassian 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + contextPath: 'atlassian' + }); + }); + + it(`dcdx debug --xms 2gb`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--xms', '2gb' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + xms: '2gb' + }); + }); + + it(`dcdx debug --xmx 2gb`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--xmx', '2gb' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + xmx: '2gb' + }); + }); + + it(`dcdx debug --clean`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--clean' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + clean: true + }); + }); + + it(`dcdx debug --prune`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--prune' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + prune: true + }); + }); + + it(`dcdx debug --watch -P active`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag, 'active')); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '-P', 'active' ] + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + activateProfiles: 'active' + }); + }); + + + it(`dcdx debug --watch (no change)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + }); + }); + + it(`dcdx debug --watch (change triggerd to src/somefile.java)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true + }); + }); + + it(`dcdx debug --watch (repetitive change triggerd to src/somefile.java)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + mockRecursiveBuild.mockReturnValue(false); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + mockRecursiveBuild.mockReturnValue(true); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 + +=============================================================================================================== +Recursive build trigger detected. The last build completed last than 5 seconds ago +This may indicate that the build changes files outside of the output directory +Alternatively, Maven is using a different output directory than configured: +'target' + +Please make sure to check your build process and/or specify a different output directory using the '-o' option +=============================================================================================================== +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true + }); + }); + + it(`dcdx debug --watch (change triggerd in output directory)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/somefile.class'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true + }); + }); + + it(`dcdx debug --watch (change triggerd by JAR file, without -i)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true + }); + }); + + it(`dcdx debug --watch --install (change triggerd by JAR file, with -i but without containers)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([]); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '-i' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +There are no running instance of ${name}, unable to install plugin 🤔 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + install: true + }); + }); + + it(`dcdx debug --watch --install (change triggerd by JAR file, with -i with multiple containers)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a', 'b' ]); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '-i' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +There are multple running instance of ${name}, unable to determine which one to use 🤔 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + install: true + }); + }); + + it(`dcdx debug --watch --install (change triggerd by JAR file, with -i and a running instance)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a' ]); + mockedDockerCopy.mockReturnValue(Promise.resolve()); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '-i' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Found updated JAR file, uploading them to QuickReload on running instances of ${name} +Finished uploading JAR file to QuickReload +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + install: true + }); + }); + + it(`dcdx debug --watch --install --outputDirectory dist (change triggerd by JAR file, in different output directory)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a' ]); + mockedDockerCopy.mockReturnValue(Promise.resolve()); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '-i', '--outputDirectory', 'dist' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + install: true, + outputDirectory: 'dist' + }); + }); + + it(`dcdx debug --watch --install --outputDirectory dist (change triggerd by JAR file, with -i and a running container)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a' ]); + mockedDockerCopy.mockReturnValue(Promise.resolve()); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '-i', '--outputDirectory', 'dist' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'dist/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Found updated JAR file, uploading them to QuickReload on running instances of ${name} +Finished uploading JAR file to QuickReload +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + install: true, + outputDirectory: 'dist' + }); + }); + + it(`dcdx debug --watch --ext **/*.java (no change)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --watch --ext **/*.java (change triggerd to src/somefile.java)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --watch --ext **/*.java (change triggerd to src/somefile.txt - ignored)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --watch --ext **/*.java (repetitive change triggerd to src/somefile.java)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + mockRecursiveBuild.mockReturnValue(false); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + + mockRecursiveBuild.mockReturnValue(true); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Detected file change, rebuilding Atlasian Data Center plugin +Finished building Atlassian Data Center plugin for ${name}... 💪 + +=============================================================================================================== +Recursive build trigger detected. The last build completed last than 5 seconds ago +This may indicate that the build changes files outside of the output directory +Alternatively, Maven is using a different output directory than configured: +'target' + +Please make sure to check your build process and/or specify a different output directory using the '-o' option +=============================================================================================================== +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --watch --ext **/*.java (change triggerd in output directory)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/somefile.class'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --watch --ext **/*.java (change triggerd by JAR file, without -i)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --watch --ext **/*.java -i (change triggerd by JAR file, with -i but without containers)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([]); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java', '-i' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +There are no running instance of ${name}, unable to install plugin 🤔 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + install: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --watch --ext **/*.java -i (change triggerd by JAR file, with -i with multiple containers)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a', 'b' ]); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java', '-i' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +There are multple running instance of ${name}, unable to determine which one to use 🤔 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + install: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --watch --ext **/*.java -i (change triggerd by JAR file, with -i and a running instance)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedDockerRunningContainerIds.mockReturnValue([ 'a' ]); + mockedDockerCopy.mockReturnValue(Promise.resolve()); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch', '--ext', '**/*.java', '-i' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + fsWatcher.emit('change', 'target/archive.jar'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*.java' ]); + expect(mockedDockerRunningContainerIds).toBeCalledTimes(1); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*.java) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Found updated JAR file, uploading them to QuickReload on running instances of ${name} +Finished uploading JAR file to QuickReload +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*.java' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true, + install: true, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --ext **/*.java`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--ext', '**/*.java' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toContain('InvalidArgumentError: Invalid argument "--ext"'); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: false, + ext: [ '**/*.java' ] + }); + }); + + it(`dcdx debug --install`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--install' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toContain('InvalidArgumentError: Invalid argument "--install"'); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: false, + install: true + }); + }); + + it(`dcdx debug --outputDirectory dist`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--outputDirectory', 'dist' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toContain('InvalidArgumentError: Invalid argument "--outputDirectory"'); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: false, + outputDirectory: 'dist' + }); + }); + + it(`dcdx debug -P active`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '-P', 'active' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toContain('InvalidArgumentError: Invalid argument "--activate-profiles"'); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: false, + activateProfiles: 'active' + }); + }); + + it(`dcdx debug --cwd path/to/someDirectory`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--cwd', 'path/to/someDirectory' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(7); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toContain('InvalidArgumentError: Invalid argument "--cwd"'); + expect(stdOut).toBe('Successfully stopped all running processes 💪'.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual(''); + expect(fsWatcherOptions).toStrictEqual(null); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: false, + cwd: 'path/to/someDirectory' + }); + }); + + it(`dcdx debug (failed)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedBuild.mockRejectedValue(null); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: false + }); + }); + + it(`dcdx debug --watch (failed)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedBuild.mockRejectedValue(null); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true + }); + }); + + it(`dcdx debug --watch (change triggerd to src/somefile.java, build failed)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockRecursiveBuild.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--watch' ]; + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + mockedBuild.mockRejectedValue(null); + fsWatcher.emit('change', 'src/somefile.java'); + // This is important, because the async/await + // in the change event handler is pushed to the next tick + await new Promise((resolve) => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(1); + expect(mockFSWatcherAdd).toHaveBeenCalledWith([ '**/*' ]); + expect(mockedBuild).toBeCalledTimes(1); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Watching filesystem for changes to source files (**/*) +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Detected file change, rebuilding Atlasian Data Center plugin +Failed to build Atlassian Data Center plugin for ${name}... 😰 +Stopping filesystem watcher... ⏳ +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(fsWatcherPaths).toStrictEqual([ '**/*' ]); + expect(fsWatcherOptions).toStrictEqual(defaultWatchOptions); + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + watch: true + }); + }); + + it(`dcdx debug (legacy AMPS plugin)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidLegacyPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/debug'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockFSWatcherAdd).toBeCalledTimes(0); + expect(mockedBuild).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Successfully stopped all running processes 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + }); +}); \ No newline at end of file diff --git a/test/fixtures/pomFiles.js b/test/fixtures/pomFiles.js new file mode 100644 index 0000000..6ae34bc --- /dev/null +++ b/test/fixtures/pomFiles.js @@ -0,0 +1,711 @@ + + + +// const getValidPomFile = (name, version) => +// ` +// +// atlassian-plugin +// +// ${version} +// +// +// +// +// com.atlassian.maven.plugins +// ${name}-maven-plugin +// 0.0.0 +// +// \${host.version} +// \${host.version} +// +// +// +// +// `; + +export const getValidPomFileFor = (name, version, profile) => + ` + + atlassian-plugin + + ${version} + + ${ profile ? ` + + + ${profile} + + + + com.atlassian.maven.plugins + ${name}-maven-plugin + 0.0.0 + + \${host.version} + \${host.version} + + + + + + + ` : ` + + + + com.atlassian.maven.plugins + ${name}-maven-plugin + 0.0.0 + + \${host.version} + \${host.version} + + + + + `} + `; + + +export const getValidLegacyPomFileFor = (name, version) => +` + + atlassian-plugin + + ${version} + + + + + com.atlassian.maven.plugins + ${name}-maven-plugin + 0.0.0 + + + + ${name} + ${name} + \${host.version} + \${host.version} + + + + + + +`; + +export const pomFileWithoutAtlassianPackaging = ` + + +`; + +export const pomFileWithoutProduct = ` + + + atlassian-plugin +`; + +export const pomFileWithInvalidProduct = ` + + + atlassian-plugin + + + + com.atlassian.maven.plugins + compass-maven-plugin + 0.0.0 + + + + + +`; + +export const pomFileWithProductButWithoutVersion = ` + + + atlassian-plugin + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + + +`; + +export const pomFileWithProductButWithInvalidVersion = ` + + + atlassian-plugin + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + 1000.000.000 + + + + +`; + +export const pomFileWithProductAndVersionWithIncorrectPropertyReplacement = ` + + + atlassian-plugin + + 1.0.0 + + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + \${invalidProperty} + + + + +`; + +export const pomFileWithMultipleProducts = ` + + + atlassian-plugin + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + + com.atlassian.maven.plugins + confluence-maven-plugin + 0.0.0 + + + + + +`; + +export const pomFileWithoutActiveProfile = ` + + + atlassian-plugin + + 1.0.0 + + + + not-active + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + + + + +`; + +export const pomFileWithActiveProfileWithoutProducts = ` + + + atlassian-plugin + + 1.0.0 + + + + active + + + + com.atlassian.maven.plugins + compass-maven-plugin + 0.0.0 + + + + + + + +`; + +export const pomFileWithActiveProfileWithoutVersion = ` + + + atlassian-plugin + + 1.0.0 + + + + active + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + + + + +`; + + +export const pomFileWithActiveProfileWithoutSupportedVersion = ` + + + atlassian-plugin + + 1.0.0 + + + + active + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + 1000.000.000 + + + + + + +`; + +export const pomFileWithActiveProfileAndVersionWithIncorrectPropertyReplacement = ` + + + atlassian-plugin + + 1.0.0 + + + + active + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + \${invalidProperty} + + + + + + +`; + +export const pomFileWithActiveProfileWithMultipleProducts = ` + + + atlassian-plugin + + 1.0.0 + + + + active + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + + com.atlassian.maven.plugins + confluence-maven-plugin + 0.0.0 + + + + + + + +`; + +export const pomFileWithMultipleActiveProfilesWithoutProducts = ` + + + atlassian-plugin + + 1.0.0 + + + + active1 + + + active2 + + +`; + +export const pomFileWithMultipleActiveProfilesWithoutValidProducts = ` + + + atlassian-plugin + + 1.0.0 + + + + active1 + + + + com.atlassian.maven.plugins + compass-maven-plugin + 0.0.0 + + + + + + + + active2 + + + + com.atlassian.maven.plugins + rovo-maven-plugin + 0.0.0 + + + + + + + +`; + +export const pomFileWithMultipleActiveProfilesWithASingleProductWithoutVersion = ` + + + atlassian-plugin + + 1.0.0 + + + + active1 + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + + + + + active2 + + + + com.atlassian.maven.plugins + compass-maven-plugin + 0.0.0 + + + + + + + +`; + +export const pomFileWithMultipleActiveProfilesWithASingleProductWithoutValidVersion = ` + + + atlassian-plugin + + 1.0.0 + + + + active1 + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + 1000.000.000 + + + + + + + active2 + + + + com.atlassian.maven.plugins + compass-maven-plugin + 0.0.0 + + + + + + + +`; + +export const pomFileWithMultipleActiveProfilesAndVersionWithIncorrectPropertyReplacement = ` + + + atlassian-plugin + + 1.0.0 + + + + active1 + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + \${invalidProperty} + + + + + + + active2 + + + + com.atlassian.maven.plugins + compass-maven-plugin + 0.0.0 + + + + + + + +`; + +export const pomFileWithMultipleActiveProfilesWithMultipleProducts = ` + + + atlassian-plugin + + 1.0.0 + + + + active1 + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + + + + + active2 + + + + com.atlassian.maven.plugins + confluence-maven-plugin + 0.0.0 + + + + + + + +`; + + + + +export const legacyPomFileWithInvalidProduct = ` + + + atlassian-plugin + + + + com.atlassian.maven.plugins + compass-maven-plugin + 0.0.0 + + + + Application + 1.0.0 + + + + + + +`; + +export const legacyPomFileWithProductButWithoutVersion = ` + + + atlassian-plugin + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + Application + + + + + + +`; + +export const legacyPomFileWithProductButWithInvalidVersion = ` + + + atlassian-plugin + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + Application + 1000.000.000 + + + + + + +`; + +export const legacyPomFileWithProductAndVersionWithIncorrectPropertyReplacement = ` + + + atlassian-plugin + + 1.0.0 + + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + Application + \${invalidProperty} + + + + + + +`; + +export const legacyPomFileWithMultipleProducts = ` + + + atlassian-plugin + + + + com.atlassian.maven.plugins + jira-maven-plugin + 0.0.0 + + + + Application + 1.0.0 + + + + + + com.atlassian.maven.plugins + confluence-maven-plugin + 0.0.0 + + + + Application + 1.0.0 + + + + + + +`; diff --git a/test/reset.test.js b/test/reset.test.js new file mode 100644 index 0000000..36e20ea --- /dev/null +++ b/test/reset.test.js @@ -0,0 +1,384 @@ +import fs from 'node:fs'; +import { cwd } from 'node:process'; + +import process, { stderr, stdout } from 'process' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import versions from '../assets/versions.json'; +import { SupportedApplications } from '../src/types/Application'; +import { getValidLegacyPomFileFor, getValidPomFileFor } from './fixtures/pomFiles'; + +let stdOut = ''; +let stdErr = ''; + +let commandExecutionOptions = ''; + +const mockExistsSync = vi.fn(); +const mockReadFileSync = vi.fn(); +const mockedDownAll = vi.fn(); + +beforeEach(() => { + stdOut = ''; + stdErr = ''; + + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + vi.spyOn(process, 'on').mockImplementation(() => {}); + vi.spyOn(process, 'exit').mockImplementation(() => {}); + vi.mock('exit-hook'); + + vi.doMock('node:fs', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + existsSync: mockExistsSync, + readFileSync: mockReadFileSync + } + }); + + vi.doMock('docker-compose/dist/v2.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + downAll: mockedDownAll, + } + }); + + vi.doMock('../src/helpers/ActionHandler.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + ActionHandler: (program, executor, options) => { + commandExecutionOptions = { ...(options || program.opts()) }; + return actual.ActionHandler(program, executor, options); + } + } + }); + +}); + +afterEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + stdOut = ''; + stdErr = ''; + commandExecutionOptions = ''; + process.argv = [ 'vitest', cwd() ]; +}) + +Object.values(SupportedApplications.Values).forEach(name => { + + const tag = versions[name][Math.floor(Math.random()*versions[name].length)]; + + describe(`dcdx reset - ${name}`, async () => { + + /****************************************************************************** + * + * AMPS BASED + * + ******************************************************************************/ + + it(`dcdx reset (defaults with AMPS configuration for ${name} - legacy POM file)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidLegacyPomFileFor(name, tag)); + vi.spyOn(fs, 'existsSync').mockImplementation(() => true); + + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql', + }); + }); + + it(`dcdx reset (defaults with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql', + }); + }); + + it(`dcdx reset --database mssql (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv.push(...[ '--database', 'mssql' ]); + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'mssql', + }); + }); + + it(`dcdx reset --database mssql --cwd myDirectory (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv.push(...[ '--database', 'mssql', '--cwd', 'myDirectory' ]); + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + cwd: 'myDirectory', + database: 'mssql', + }); + }); + + it(`dcdx reset --activate-profiles myProfile (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag, 'myProfile')); + + process.argv.push(...[ '-P', 'myProfile' ]); + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + activateProfiles: 'myProfile', + database: 'postgresql', + }); + }); + + it(`dcdx reset --activate-profiles myProfile --database mssql (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag, 'myProfile')); + + process.argv.push(...[ '-P', 'myProfile', '--database', 'mssql' ]); + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + activateProfiles: 'myProfile', + database: 'mssql', + }); + }); + + it(`dcdx reset --activate-profiles myProfile --database mssql --cwd myDirectory (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag, 'myProfile')); + + process.argv.push(...[ '-P', 'myProfile', '--database', 'mssql', '--cwd', 'myDirectory' ]); + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + cwd: 'myDirectory', + activateProfiles: 'myProfile', + database: 'mssql', + }); + }); + + /****************************************************************************** + * + * NAME BASED + * + ******************************************************************************/ + + it(`dcdx reset ${name}`, async () => { + process.argv = [ 'vitest', 'dcdx', name ] + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql', + name, + tag: 'latest', + }); + }); + + it(`dcdx reset ${name} --tag ${tag}`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', tag ] + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql', + name, + tag + }); + }); + + it(`dcdx reset ${name} --tag latest --database mssql`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'mssql' ] + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + name, + database: 'mssql', + tag: 'latest', + }); + }); + + it(`dcdx reset ${name} --database mssql`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--database', 'mssql' ] + await import('../src/commands/reset'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Removing ${name} and deleting all data... 💔 +Removed ${name} and deleted all data 💪 +`.trim() + '\n'); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'mssql', + name, + tag: 'latest', + }); + }); + + it(`dcdx reset - invalid name`, async () => { + const name = 'compass'; + mockExistsSync.mockReturnValue(false); + + process.argv = [ 'vitest', 'dcdx', name ] + await import('../src/commands/reset'); + + expect(stdErr.startsWith(`error: too many arguments for 'fromAMPS'. Expected 0 arguments but got 1.`)).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql' + }) + }); + + it(`dcdx reset ${name} --tag invalid`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'invalid' ] + await import('../src/commands/reset'); + + expect(stdErr.startsWith(`error: option '-t, --tag ' argument 'invalid' is invalid.`)).toBeTruthy();; + expect(stdOut).toBe(''); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(commandExecutionOptions).toBe(''); + }); + + it(`dcdx reset ${name} --tag latest --database invalid`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'invalid' ] + await import('../src/commands/reset'); + + expect(stdErr.startsWith(`error: option '-d, --database ' argument 'invalid' is invalid. Allowed choices are postgresql, mysql, mssql.`)).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(commandExecutionOptions).toBe(''); + }); + + it(`dcdx reset ${name} --tag latest --database mssql --activate-profiles invalid`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'mssql', '--activate-profiles', 'invalid' ] + await import('../src/commands/reset'); + + expect(stdErr.startsWith('InvalidArgumentError: Invalid argument "--activate-profiles"')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(commandExecutionOptions).toStrictEqual({ + activateProfiles: 'invalid', + database: 'mssql', + name, + tag: 'latest', + }); + }); + + it(`dcdx reset ${name} --tag latest --database mssql --activate-profiles invalid --cwd invalidDirectory`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'mssql', '--activate-profiles', 'invalid', '--cwd', 'invalidDirectory' ] + await import('../src/commands/reset'); + + expect(stdErr.startsWith('InvalidArgumentError: Invalid argument "--activate-profiles"')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(commandExecutionOptions).toStrictEqual({ + activateProfiles: 'invalid', + cwd: 'invalidDirectory', + database: 'mssql', + name, + tag: 'latest', + }); + }); + + it(`dcdx reset ${name} --tag latest --database mssql --cwd invalidDirectory`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'mssql', '--cwd', 'invalidDirectory' ] + await import('../src/commands/reset'); + + expect(stdErr.startsWith('InvalidArgumentError: Invalid argument "--cwd"')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(commandExecutionOptions).toStrictEqual({ + cwd: 'invalidDirectory', + database: 'mssql', + name, + tag: 'latest', + }); + }); + + }); +}); \ No newline at end of file diff --git a/test/run.test.js b/test/run.test.js new file mode 100644 index 0000000..5fed3b0 --- /dev/null +++ b/test/run.test.js @@ -0,0 +1,1945 @@ +import { setMaxListeners } from 'node:events'; +import { cwd } from 'node:process'; +import { EventEmitter } from 'node:stream'; + +import axios from 'axios'; +import process, { stderr, stdout } from 'process' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import versions from '../assets/versions.json'; +import { SupportedApplications } from '../src/types/Application'; +import { getValidLegacyPomFileFor, getValidPomFileFor } from './fixtures/pomFiles'; + +let stdOut = ''; +let stdErr = ''; + +const SpawnEventEmitter = new EventEmitter(); +let commandExecutionOptions = ''; + +const mockExistsSync = vi.fn(); +const mockReadFileSync = vi.fn(); +const mockedClone = vi.fn(); +const mockedPull = vi.fn(); +const mockedSpawn = vi.fn().mockImplementation(() => SpawnEventEmitter); +const mockedDownAll = vi.fn(); +const mockedPS = vi.fn(); +const mockedStop = vi.fn(); +const mockedUpAll = vi.fn(); +const mockedAuthenticate = vi.fn(); +const mockedQuery = vi.fn(); + +const defaultCommandOptions = { + clean: false, + database: 'postgresql', + debug: false, + port: '80', + prune: false, + xms: '1024m', + xmx: '1024m', +} + +beforeEach(() => { + stdOut = ''; + stdErr = ''; + + setMaxListeners(300); + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + vi.spyOn(process, 'on').mockImplementation(() => {}); + vi.spyOn(process, 'exit').mockImplementation(() => {}); + vi.mock('exit-hook'); + + vi.doMock('node:fs', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + existsSync: mockExistsSync, + readFileSync: mockReadFileSync + } + }); + + vi.doMock('node:child_process', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + spawn: mockedSpawn + } + }); + + vi.doMock('sequelize', async (importOriginal) => { + const actual = await importOriginal(); + actual.Sequelize.prototype.authenticate = mockedAuthenticate; + actual.Sequelize.prototype.query = mockedQuery; + return actual; + }); + + vi.doMock('docker-compose/dist/v2.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + downAll: mockedDownAll, + ps: mockedPS, + stop: mockedStop, + upAll: mockedUpAll + } + }); + + vi.mock('simple-git', () => ({ + default: vi.fn(() => ({ + pull: mockedPull, + clone: mockedClone + })) + })); + + vi.doMock('../src/helpers/ActionHandler.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + ActionHandler: (program, executor, options) => { + commandExecutionOptions = { ...(options || program.opts()) }; + return actual.ActionHandler(program, executor, options); + } + } + }); +}); + +afterEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + stdOut = ''; + stdErr = ''; + commandExecutionOptions = ''; + process.argv = [ 'vitest', cwd() ]; + setMaxListeners(); +}) + +Object.values(SupportedApplications.Values).forEach(name => { + + const tag = versions[name][Math.floor(Math.random()*versions[name].length)]; + + describe(`dcdx run - ${name}`, async () => { + + /****************************************************************************** + * + * AMPS BASED + * + ******************************************************************************/ + + it(`dcdx run (git clone)`, async () => { + mockExistsSync.mockImplementation((path) => { + if (path.endsWith('.xml')) { + return true; + } else { + return false; + } + }); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx run (git pull)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx run (docker quits unexpected)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 1); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith('Error: Docker exited with code 1')).toBeTruthy(); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx run (fails to become available)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: name !== 'bamboo' ? 200 : 204, + data: { status: 'FAILED' } + }) + + vi.useFakeTimers(); + + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // Run all the timers + await vi.runAllTimersAsync(); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(301); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + let counter = ''; + [...Array(301)].forEach((_, index) => counter += `Waiting for ${name} to become available... ${index}s\n`); + + expect(stdErr).toBe(`A timeout occurred while waiting for ${name} to become available ⛔`.trim() + '\n'); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +${counter.trim()} +Failed to start ${name} ⛔ +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx run (fails to become available - Axios error)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockRejectedValue({ + status: 500 + }) + + vi.useFakeTimers(); + + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // Run all the timers + await vi.runAllTimersAsync(); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(301); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + let counter = ''; + [...Array(301)].forEach((_, index) => counter += `Waiting for ${name} to become available... ${index}s\n`); + + expect(stdErr).toBe(`A timeout occurred while waiting for ${name} to become available ⛔`.trim() + '\n'); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +${counter.trim()} +Failed to start ${name} ⛔ +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + it(`dcdx run --tag latest`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--tag', 'latest' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(5); + expect(mockReadFileSync).toBeCalledTimes(4); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + tag: 'latest' + }); + }); + + it(`dcdx run --tag invalid`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--tag', 'invalid' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(4); + expect(mockReadFileSync).toBeCalledTimes(4); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith(`Error: Product version 'invalid' is invalid. Allowed choices are`)).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + tag: 'invalid' + }); + }); + + it(`dcdx run --database mysql`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--database', 'mysql' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of mysql... 💃 +Database is ready and accepting connections on localhost:3306 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + database: 'mysql' + }); + }); + + it(`dcdx run --database mssql`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--database', 'mssql' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of mssql... 💃 +Database is ready and accepting connections on localhost:1433 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + database: 'mssql' + }); + }); + + it(`dcdx run --database invalid`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--database', 'invalid' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(0); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith(`error: option '-d, --database ' argument 'invalid' is invalid. Allowed choices are postgresql, mysql, mssql.`)).toBeTruthy(); + expect(stdOut).toBe(''); + expect(commandExecutionOptions).toBe(''); + }); + + it(`dcdx run --port 1234`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--port', '1234' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:1234 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + port: '1234' + }); + }); + + it(`dcdx run --contextPath atlassian`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '-c', 'atlassian' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80/atlassian 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + contextPath: 'atlassian' + }); + }); + + it(`dcdx run --xms 2gb`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--xms', '2gb' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + xms: '2gb' + }); + }); + + it(`dcdx run --xmx 2gb`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--xmx', '2gb' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + xmx: '2gb' + }); + }); + + it(`dcdx run --clean`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--clean' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + clean: true + }); + }); + + it(`dcdx run --prune`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), '--prune' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + prune: true + }); + }); + + it(`dcdx run (legacy AMPS plugin)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidLegacyPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(8); + expect(mockReadFileSync).toBeCalledTimes(7); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ ...defaultCommandOptions }); + }); + + /****************************************************************************** + * + * NAME BASED + * + ******************************************************************************/ + + it(`dcdx run ${name} (git clone)`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name ]; + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} (git pull)`, async () => { + mockExistsSync.mockImplementation((path) => !path.endsWith('.xml')) + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name ]; + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} (docker quits unexpected)`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name ]; + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 1); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith('Error: Docker exited with code 1')).toBeTruthy(); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + name, + tag: 'latest', + }); + }); + + it(`dcdx run ${name} (fails to become available)`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: name !== 'bamboo' ? 200 : 204, + data: { status: 'FAILED' } + }) + + vi.useFakeTimers(); + + process.argv = [ 'vitest', cwd(), name ]; + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // Run all the timers + await vi.runAllTimersAsync(); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(301); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + let counter = ''; + [...Array(301)].forEach((_, index) => counter += `Waiting for ${name} to become available... ${index}s\n`); + + expect(stdErr).toBe(`A timeout occurred while waiting for ${name} to become available ⛔`.trim() + '\n'); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +${counter.trim()} +Failed to start ${name} ⛔ +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} (fails to become available - Axios error)`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockRejectedValue({ + status: 500 + }) + + vi.useFakeTimers(); + + process.argv = [ 'vitest', cwd(), name ]; + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // Run all the timers + await vi.runAllTimersAsync(); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(301); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + let counter = ''; + [...Array(301)].forEach((_, index) => counter += `Waiting for ${name} to become available... ${index}s\n`); + + expect(stdErr).toBe(`A timeout occurred while waiting for ${name} to become available ⛔`.trim() + '\n'); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +${counter.trim()} +Failed to start ${name} ⛔ +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --tag latest`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--tag', 'latest' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --tag invalid`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '-t', 'invalid' ]; + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(0); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith(`error: option '-t, --tag ' argument 'invalid' is invalid.`)).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(commandExecutionOptions).toBe(''); + }); + + it(`dcdx run ${name} --tag ${tag} -P invalid`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--tag', tag, '-P', 'invalid' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(0); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith('InvalidArgumentError: Invalid argument "--activate-profiles"')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + activateProfiles: 'invalid', + name, + tag + }); + }); + + it(`dcdx run ${name} --tag ${tag} --cwd invalid`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--tag', tag, '--cwd', 'invalid' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(0); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith('InvalidArgumentError: Invalid argument "--cwd"')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + cwd: 'invalid', + name, + tag + }); + }); + + it(`dcdx run ${name} --database mysql`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--database', 'mysql' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(1); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of mysql... 💃 +Database is ready and accepting connections on localhost:3306 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + database: 'mysql', + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --database mssql`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--database', 'mssql' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(3); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of mssql... 💃 +Database is ready and accepting connections on localhost:1433 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + database: 'mssql', + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --database invalid`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--database', 'invalid' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(0); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(0); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(0); + expect(mockedAuthenticate).toBeCalledTimes(0); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr.startsWith(`error: option '-d, --database ' argument 'invalid' is invalid. Allowed choices are postgresql, mysql, mssql.`)).toBeTruthy(); + expect(stdOut).toBe(''); + expect(commandExecutionOptions).toBe(''); + }); + + it(`dcdx run ${name} --port 1234`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--port', '1234' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(0); + expect(mockedPull).toBeCalledTimes(1); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:1234 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + port: '1234', + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --contextPath atlassian`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '-c', 'atlassian' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80/atlassian 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + contextPath: 'atlassian', + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --xms 2gb`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--xms', '2gb' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + xms: '2gb', + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --xmx 2gb`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--xmx', '2gb' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(0); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + xmx: '2gb', + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --clean`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--clean' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(2); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + clean: true, + name, + tag: 'latest' + }); + }); + + it(`dcdx run ${name} --prune`, async () => { + mockExistsSync.mockReturnValue(false); + mockedClone.mockResolvedValue(true); + mockedPull.mockResolvedValue(true); + mockedUpAll.mockReturnValue(Promise.resolve()); + mockedAuthenticate.mockResolvedValue(true); + mockedQuery.mockResolvedValue(true); + mockedPS.mockResolvedValue({ data: { services: [ { name, state: 'up' }] }}); + vi.spyOn(axios, 'get').mockResolvedValue({ + status: 200, + data: { state: 'RUNNING' } + }) + + process.argv = [ 'vitest', cwd(), name, '--prune' ] + await import('../src/commands/run'); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker build + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + // We need to stop Docker log tail + SpawnEventEmitter.emit('exit', 0); + await new Promise(resolve => process.nextTick(resolve)); + + expect(mockExistsSync).toBeCalledTimes(1); + expect(mockReadFileSync).toBeCalledTimes(0); + expect(mockedClone).toBeCalledTimes(1); + expect(mockedPull).toBeCalledTimes(0); + + expect(mockedDownAll).toBeCalledTimes(2); + expect(mockedPS).toBeCalledTimes(2); + expect(mockedStop).toBeCalledTimes(0); + expect(mockedUpAll).toBeCalledTimes(2); + expect(mockedAuthenticate).toBeCalledTimes(1); + expect(mockedQuery).toBeCalledTimes(0); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Starting ${name}... 💃 +Starting instance of postgresql... 💃 +Database is ready and accepting connections on localhost:5432 🗄️ +Waiting for ${name} to become available... 0s +The application ${name} is ready on http://localhost:80 🎉 +Stopping ${name}... 💔 +Stopped ${name} 💪 +`.trim() + '\n'); + + expect(commandExecutionOptions).toStrictEqual({ + ...defaultCommandOptions, + prune: true, + name, + tag: 'latest' + }); + }); + + }); +}); \ No newline at end of file diff --git a/test/stop.test.js b/test/stop.test.js new file mode 100644 index 0000000..277f54f --- /dev/null +++ b/test/stop.test.js @@ -0,0 +1,384 @@ +import fs from 'node:fs'; +import { cwd } from 'node:process'; + +import process, { stderr, stdout } from 'process' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import versions from '../assets/versions.json'; +import { SupportedApplications } from '../src/types/Application'; +import { getValidLegacyPomFileFor, getValidPomFileFor } from './fixtures/pomFiles'; + +let stdOut = ''; +let stdErr = ''; + +let commandExecutionOptions = ''; + +const mockExistsSync = vi.fn(); +const mockReadFileSync = vi.fn(); +const mockedStop = vi.fn(); + +beforeEach(() => { + stdOut = ''; + stdErr = ''; + + vi.spyOn(stdout, 'write').mockImplementation((value) => { stdOut += value; return true; }); + vi.spyOn(stderr, 'write').mockImplementation((value) => { stdErr += value; return true; }); + vi.spyOn(process, 'on').mockImplementation(() => {}); + vi.spyOn(process, 'exit').mockImplementation(() => {}); + vi.mock('exit-hook'); + + vi.doMock('node:fs', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + existsSync: mockExistsSync, + readFileSync: mockReadFileSync + } + }); + + vi.doMock('docker-compose/dist/v2.js', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + stop: mockedStop, + } + }); + + vi.doMock('../src/helpers/ActionHandler.ts', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + ActionHandler: (program, executor, options) => { + commandExecutionOptions = { ...(options || program.opts()) }; + return actual.ActionHandler(program, executor, options); + } + } + }); + +}); + +afterEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + stdOut = ''; + stdErr = ''; + commandExecutionOptions = ''; + process.argv = [ 'vitest', cwd() ]; +}) + +Object.values(SupportedApplications.Values).forEach(name => { + + const tag = versions[name][Math.floor(Math.random()*versions[name].length)]; + + describe(`dcdx stop - ${name}`, async () => { + + /****************************************************************************** + * + * AMPS BASED + * + ******************************************************************************/ + + it(`dcdx stop (defaults with AMPS configuration for ${name} - legacy POM file)`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidLegacyPomFileFor(name, tag)); + vi.spyOn(fs, 'existsSync').mockImplementation(() => true); + + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and postgresql... 💔 +Stopped ${name} and postgresql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql', + }); + }); + + it(`dcdx stop (defaults with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and postgresql... 💔 +Stopped ${name} and postgresql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql', + }); + }); + + it(`dcdx stop --database mssql (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv.push(...[ '--database', 'mssql' ]); + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and mssql... 💔 +Stopped ${name} and mssql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'mssql', + }); + }); + + it(`dcdx stop --database mssql --cwd myDirectory (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag)); + + process.argv.push(...[ '--database', 'mssql', '--cwd', 'myDirectory' ]); + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and mssql... 💔 +Stopped ${name} and mssql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + cwd: 'myDirectory', + database: 'mssql', + }); + }); + + it(`dcdx stop --activate-profiles myProfile (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag, 'myProfile')); + + process.argv.push(...[ '-P', 'myProfile' ]); + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and postgresql... 💔 +Stopped ${name} and postgresql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + activateProfiles: 'myProfile', + database: 'postgresql', + }); + }); + + it(`dcdx stop --activate-profiles myProfile --database mssql (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag, 'myProfile')); + + process.argv.push(...[ '-P', 'myProfile', '--database', 'mssql' ]); + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and mssql... 💔 +Stopped ${name} and mssql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + activateProfiles: 'myProfile', + database: 'mssql', + }); + }); + + it(`dcdx stop --activate-profiles myProfile --database mssql --cwd myDirectory (with AMPS configuration for ${name})`, async () => { + mockExistsSync.mockReturnValue(true); + mockReadFileSync.mockReturnValue(getValidPomFileFor(name, tag, 'myProfile')); + + process.argv.push(...[ '-P', 'myProfile', '--database', 'mssql', '--cwd', 'myDirectory' ]); + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and mssql... 💔 +Stopped ${name} and mssql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + cwd: 'myDirectory', + activateProfiles: 'myProfile', + database: 'mssql', + }); + }); + + /****************************************************************************** + * + * NAME BASED + * + ******************************************************************************/ + + it(`dcdx stop ${name}`, async () => { + process.argv = [ 'vitest', 'dcdx', name ] + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and postgresql... 💔 +Stopped ${name} and postgresql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql', + name, + tag: 'latest', + }); + }); + + it(`dcdx stop ${name} --tag ${tag}`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', tag ] + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and postgresql... 💔 +Stopped ${name} and postgresql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql', + name, + tag + }); + }); + + it(`dcdx stop ${name} --tag latest --database mssql`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'mssql' ] + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and mssql... 💔 +Stopped ${name} and mssql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + name, + database: 'mssql', + tag: 'latest', + }); + }); + + it(`dcdx stop ${name} --database mssql`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--database', 'mssql' ] + await import('../src/commands/stop'); + + expect(stdErr).toBe(''); + expect(stdOut).toBe(` +Stopping ${name} and mssql... 💔 +Stopped ${name} and mssql 💪 +`.trim() + '\n'); + + expect(mockedStop).toBeCalledTimes(2); + expect(commandExecutionOptions).toStrictEqual({ + database: 'mssql', + name, + tag: 'latest', + }); + }); + + it(`dcdx stop - invalid name`, async () => { + const name = 'compass'; + mockExistsSync.mockReturnValue(false); + + process.argv = [ 'vitest', 'dcdx', name ] + await import('../src/commands/stop'); + + expect(stdErr.startsWith(`error: too many arguments for 'fromAMPS'. Expected 0 arguments but got 1.`)).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedStop).toBeCalledTimes(0); + expect(commandExecutionOptions).toStrictEqual({ + database: 'postgresql' + }) + }); + + it(`dcdx stop ${name} --tag invalid`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'invalid' ] + await import('../src/commands/stop'); + + expect(stdErr.startsWith(`error: option '-t, --tag ' argument 'invalid' is invalid.`)).toBeTruthy();; + expect(stdOut).toBe(''); + + expect(mockedStop).toBeCalledTimes(0); + expect(commandExecutionOptions).toBe(''); + }); + + it(`dcdx stop ${name} --tag latest --database invalid`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'invalid' ] + await import('../src/commands/stop'); + + expect(stdErr.startsWith(`error: option '-d, --database ' argument 'invalid' is invalid. Allowed choices are postgresql, mysql, mssql.`)).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedStop).toBeCalledTimes(0); + expect(commandExecutionOptions).toBe(''); + }); + + it(`dcdx stop ${name} --tag latest --database mssql --activate-profiles invalid`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'mssql', '--activate-profiles', 'invalid' ] + await import('../src/commands/stop'); + + expect(stdErr.startsWith('InvalidArgumentError: Invalid argument "--activate-profiles"')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedStop).toBeCalledTimes(0); + expect(commandExecutionOptions).toStrictEqual({ + activateProfiles: 'invalid', + database: 'mssql', + name, + tag: 'latest', + }); + }); + + it(`dcdx stop ${name} --tag latest --database mssql --activate-profiles invalid --cwd invalidDirectory`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'mssql', '--activate-profiles', 'invalid', '--cwd', 'invalidDirectory' ] + await import('../src/commands/stop'); + + expect(stdErr.startsWith('InvalidArgumentError: Invalid argument "--activate-profiles"')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedStop).toBeCalledTimes(0); + expect(commandExecutionOptions).toStrictEqual({ + activateProfiles: 'invalid', + cwd: 'invalidDirectory', + database: 'mssql', + name, + tag: 'latest', + }); + }); + + it(`dcdx stop ${name} --tag latest --database mssql --cwd invalidDirectory`, async () => { + process.argv = [ 'vitest', 'dcdx', name, '--tag', 'latest', '--database', 'mssql', '--cwd', 'invalidDirectory' ] + await import('../src/commands/stop'); + + expect(stdErr.startsWith('InvalidArgumentError: Invalid argument "--cwd"')).toBeTruthy(); + expect(stdOut).toBe(''); + + expect(mockedStop).toBeCalledTimes(0); + expect(commandExecutionOptions).toStrictEqual({ + cwd: 'invalidDirectory', + database: 'mssql', + name, + tag: 'latest', + }); + }); + + }); +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0987d91..9b1954f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,6 +12,16 @@ __metadata: languageName: node linkType: hard +"@ampproject/remapping@npm:^2.2.1": + version: 2.3.0 + resolution: "@ampproject/remapping@npm:2.3.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/81d63cca5443e0f0c72ae18b544cc28c7c0ec2cea46e7cb888bb0e0f411a1191d0d6b7af798d54e30777d8d1488b2ec0732aac2be342d3d7d3ffd271c6f489ed + languageName: node + linkType: hard + "@azure/abort-controller@npm:^1.0.0": version: 1.1.0 resolution: "@azure/abort-controller@npm:1.1.0" @@ -210,6 +220,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/helper-string-parser@npm:7.24.1" + checksum: 10c0/2f9bfcf8d2f9f083785df0501dbab92770111ece2f90d120352fda6dd2a7d47db11b807d111e6f32aa1ba6d763fe2dc6603d153068d672a5d0ad33ca802632b2 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-validator-identifier@npm:7.22.20" @@ -217,6 +234,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.24.5": + version: 7.24.5 + resolution: "@babel/helper-validator-identifier@npm:7.24.5" + checksum: 10c0/05f957229d89ce95a137d04e27f7d0680d84ae48b6ad830e399db0779341f7d30290f863a93351b4b3bde2166737f73a286ea42856bb07c8ddaa95600d38645c + languageName: node + linkType: hard + "@babel/highlight@npm:^7.24.2": version: 7.24.2 resolution: "@babel/highlight@npm:7.24.2" @@ -229,6 +253,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.24.4": + version: 7.24.5 + resolution: "@babel/parser@npm:7.24.5" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/8333a6ad5328bad34fa0e12bcee147c3345ea9a438c0909e7c68c6cfbea43c464834ffd7eabd1cbc1c62df0a558e22ffade9f5b29440833ba7b33d96a71f88c0 + languageName: node + linkType: hard + "@babel/runtime@npm:^7.14.0": version: 7.24.4 resolution: "@babel/runtime@npm:7.24.4" @@ -238,6 +271,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.24.0, @babel/types@npm:^7.8.3": + version: 7.24.5 + resolution: "@babel/types@npm:7.24.5" + dependencies: + "@babel/helper-string-parser": "npm:^7.24.1" + "@babel/helper-validator-identifier": "npm:^7.24.5" + to-fast-properties: "npm:^2.0.0" + checksum: 10c0/e1284eb046c5e0451b80220d1200e2327e0a8544a2fe45bb62c952e5fdef7099c603d2336b17b6eac3cc046b7a69bfbce67fe56e1c0ea48cd37c65cb88638f2a + languageName: node + linkType: hard + "@balena/dockerignore@npm:^1.0.2": version: 1.0.2 resolution: "@balena/dockerignore@npm:1.0.2" @@ -245,6 +289,13 @@ __metadata: languageName: node linkType: hard +"@bcoe/v8-coverage@npm:^0.2.3": + version: 0.2.3 + resolution: "@bcoe/v8-coverage@npm:0.2.3" + checksum: 10c0/6b80ae4cb3db53f486da2dc63b6e190a74c8c3cca16bb2733f234a0b6a9382b09b146488ae08e2b22cf00f6c83e20f3e040a2f7894f05c045c946d6a090b1d52 + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -252,6 +303,167 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/aix-ppc64@npm:0.20.2" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-arm64@npm:0.20.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-arm@npm:0.20.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-x64@npm:0.20.2" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/darwin-arm64@npm:0.20.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/darwin-x64@npm:0.20.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/freebsd-arm64@npm:0.20.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/freebsd-x64@npm:0.20.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-arm64@npm:0.20.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-arm@npm:0.20.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-ia32@npm:0.20.2" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-loong64@npm:0.20.2" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-mips64el@npm:0.20.2" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-ppc64@npm:0.20.2" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-riscv64@npm:0.20.2" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-s390x@npm:0.20.2" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-x64@npm:0.20.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/netbsd-x64@npm:0.20.2" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/openbsd-x64@npm:0.20.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/sunos-x64@npm:0.20.2" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-arm64@npm:0.20.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-ia32@npm:0.20.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-x64@npm:0.20.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -340,6 +552,22 @@ __metadata: languageName: node linkType: hard +"@istanbuljs/schema@npm:^0.1.2": + version: 0.1.3 + resolution: "@istanbuljs/schema@npm:0.1.3" + checksum: 10c0/61c5286771676c9ca3eb2bd8a7310a9c063fb6e0e9712225c8471c582d157392c88f5353581c8c9adbe0dff98892317d2fdfc56c3499aa42e0194405206a963a + languageName: node + linkType: hard + +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" + dependencies: + "@sinclair/typebox": "npm:^0.27.8" + checksum: 10c0/b329e89cd5f20b9278ae1233df74016ebf7b385e0d14b9f4c1ad18d096c4c19d1e687aa113a9c976b16ec07f021ae53dea811fb8c1248a50ac34fbe009fdf6be + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.5 resolution: "@jridgewell/gen-mapping@npm:0.3.5" @@ -382,7 +610,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": +"@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": version: 0.3.25 resolution: "@jridgewell/trace-mapping@npm:0.3.25" dependencies: @@ -802,6 +1030,13 @@ __metadata: languageName: node linkType: hard +"@polka/url@npm:^1.0.0-next.24": + version: 1.0.0-next.25 + resolution: "@polka/url@npm:1.0.0-next.25" + checksum: 10c0/ef61f0a0fe94bb6e1143fc5b9d5a12e6ca9dbd2c57843ebf81db432c21b9f1005c09e8a1ef8b6d5ddfa42146ca65b640feb2d353bd0d3546da46ba59e48a5349 + languageName: node + linkType: hard + "@rollup/plugin-commonjs@npm:25.0.7": version: 25.0.7 resolution: "@rollup/plugin-commonjs@npm:25.0.7" @@ -893,6 +1128,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.17.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@rollup/rollup-android-arm64@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-android-arm64@npm:4.14.1" @@ -900,6 +1142,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm64@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-android-arm64@npm:4.17.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-arm64@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-darwin-arm64@npm:4.14.1" @@ -907,6 +1156,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-arm64@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-darwin-arm64@npm:4.17.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-x64@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-darwin-x64@npm:4.14.1" @@ -914,6 +1170,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-x64@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-darwin-x64@npm:4.17.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-gnueabihf@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.14.1" @@ -921,6 +1184,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-gnueabihf@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.17.2" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.17.2" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-gnu@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.14.1" @@ -928,6 +1205,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.17.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-musl@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.14.1" @@ -935,6 +1219,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-musl@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.17.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-powerpc64le-gnu@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.14.1" @@ -942,6 +1233,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.17.2" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-gnu@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.14.1" @@ -949,6 +1247,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-riscv64-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.17.2" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-s390x-gnu@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.14.1" @@ -956,6 +1261,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-s390x-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.17.2" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-gnu@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-linux-x64-gnu@npm:4.14.1" @@ -963,6 +1275,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-gnu@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.17.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-musl@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-linux-x64-musl@npm:4.14.1" @@ -970,6 +1289,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-musl@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.17.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-win32-arm64-msvc@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.14.1" @@ -977,6 +1303,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-arm64-msvc@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.17.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-ia32-msvc@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.14.1" @@ -984,6 +1317,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-ia32-msvc@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.17.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@rollup/rollup-win32-x64-msvc@npm:4.14.1": version: 4.14.1 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.14.1" @@ -991,6 +1331,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-x64-msvc@npm:4.17.2": + version: 4.17.2 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.17.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@semantic-release/commit-analyzer@npm:^12.0.0": version: 12.0.0 resolution: "@semantic-release/commit-analyzer@npm:12.0.0" @@ -1140,6 +1487,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 10c0/ef6351ae073c45c2ac89494dbb3e1f87cc60a93ce4cde797b782812b6f97da0d620ae81973f104b43c9b7eaa789ad20ba4f6a1359f1cc62f63729a55a7d22d4e + languageName: node + linkType: hard + "@sindresorhus/is@npm:^4.6.0": version: 4.6.0 resolution: "@sindresorhus/is@npm:4.6.0" @@ -1312,22 +1666,6 @@ __metadata: languageName: node linkType: hard -"@types/yargs-parser@npm:*": - version: 21.0.3 - resolution: "@types/yargs-parser@npm:21.0.3" - checksum: 10c0/e71c3bd9d0b73ca82e10bee2064c384ab70f61034bbfb78e74f5206283fc16a6d85267b606b5c22cb2a3338373586786fed595b2009825d6a9115afba36560a0 - languageName: node - linkType: hard - -"@types/yargs@npm:17.0.32": - version: 17.0.32 - resolution: "@types/yargs@npm:17.0.32" - dependencies: - "@types/yargs-parser": "npm:*" - checksum: 10c0/2095e8aad8a4e66b86147415364266b8d607a3b95b4239623423efd7e29df93ba81bb862784a6e08664f645cc1981b25fd598f532019174cd3e5e1e689e1cccf - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:7.6.0": version: 7.6.0 resolution: "@typescript-eslint/eslint-plugin@npm:7.6.0" @@ -1451,6 +1789,100 @@ __metadata: languageName: node linkType: hard +"@vitest/coverage-v8@npm:^1.6.0": + version: 1.6.0 + resolution: "@vitest/coverage-v8@npm:1.6.0" + dependencies: + "@ampproject/remapping": "npm:^2.2.1" + "@bcoe/v8-coverage": "npm:^0.2.3" + debug: "npm:^4.3.4" + istanbul-lib-coverage: "npm:^3.2.2" + istanbul-lib-report: "npm:^3.0.1" + istanbul-lib-source-maps: "npm:^5.0.4" + istanbul-reports: "npm:^3.1.6" + magic-string: "npm:^0.30.5" + magicast: "npm:^0.3.3" + picocolors: "npm:^1.0.0" + std-env: "npm:^3.5.0" + strip-literal: "npm:^2.0.0" + test-exclude: "npm:^6.0.0" + peerDependencies: + vitest: 1.6.0 + checksum: 10c0/a7beaf2a88b628a9dc16ddca7589f2b2e4681598e6788d68423dffbb06c608edc52b2dd421ada069eb3cfd83f8f592ddd6e8b8db2d037bf13965a56c5e5835ac + languageName: node + linkType: hard + +"@vitest/expect@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/expect@npm:1.6.0" + dependencies: + "@vitest/spy": "npm:1.6.0" + "@vitest/utils": "npm:1.6.0" + chai: "npm:^4.3.10" + checksum: 10c0/a4351f912a70543e04960f5694f1f1ac95f71a856a46e87bba27d3eb72a08c5d11d35021cbdc6077452a152e7d93723fc804bba76c2cc53c8896b7789caadae3 + languageName: node + linkType: hard + +"@vitest/runner@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/runner@npm:1.6.0" + dependencies: + "@vitest/utils": "npm:1.6.0" + p-limit: "npm:^5.0.0" + pathe: "npm:^1.1.1" + checksum: 10c0/27d67fa51f40effe0e41ee5f26563c12c0ef9a96161f806036f02ea5eb9980c5cdf305a70673942e7a1e3d472d4d7feb40093ae93024ef1ccc40637fc65b1d2f + languageName: node + linkType: hard + +"@vitest/snapshot@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/snapshot@npm:1.6.0" + dependencies: + magic-string: "npm:^0.30.5" + pathe: "npm:^1.1.1" + pretty-format: "npm:^29.7.0" + checksum: 10c0/be027fd268d524589ff50c5fad7b4faa1ac5742b59ac6c1dc6f5a3930aad553560e6d8775e90ac4dfae4be746fc732a6f134ba95606a1519707ce70db3a772a5 + languageName: node + linkType: hard + +"@vitest/spy@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/spy@npm:1.6.0" + dependencies: + tinyspy: "npm:^2.2.0" + checksum: 10c0/df66ea6632b44fb76ef6a65c1abbace13d883703aff37cd6d062add6dcd1b883f19ce733af8e0f7feb185b61600c6eb4042a518e4fb66323d0690ec357f9401c + languageName: node + linkType: hard + +"@vitest/ui@npm:^1.6.0": + version: 1.6.0 + resolution: "@vitest/ui@npm:1.6.0" + dependencies: + "@vitest/utils": "npm:1.6.0" + fast-glob: "npm:^3.3.2" + fflate: "npm:^0.8.1" + flatted: "npm:^3.2.9" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + sirv: "npm:^2.0.4" + peerDependencies: + vitest: 1.6.0 + checksum: 10c0/1d2f971f1efb9f9b22af2881e5ed66ceca8a7f091c31bfb6181ea2cb63c0cf3ff00272433892a38be67721dad26c3502b084f3c8d63c574f743ed45ae0016582 + languageName: node + linkType: hard + +"@vitest/utils@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/utils@npm:1.6.0" + dependencies: + diff-sequences: "npm:^29.6.3" + estree-walker: "npm:^3.0.3" + loupe: "npm:^2.3.7" + pretty-format: "npm:^29.7.0" + checksum: 10c0/8b0d19835866455eb0b02b31c5ca3d8ad45f41a24e4c7e1f064b480f6b2804dc895a70af332f14c11ed89581011b92b179718523f55f5b14787285a0321b1301 + languageName: node + linkType: hard + "@xmldom/xmldom@npm:0.8.10": version: 0.8.10 resolution: "@xmldom/xmldom@npm:0.8.10" @@ -1502,6 +1934,13 @@ __metadata: languageName: node linkType: hard +"acorn-walk@npm:^8.3.2": + version: 8.3.2 + resolution: "acorn-walk@npm:8.3.2" + checksum: 10c0/7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52 + languageName: node + linkType: hard + "acorn@npm:^8.11.3, acorn@npm:^8.8.2": version: 8.11.3 resolution: "acorn@npm:8.11.3" @@ -1591,6 +2030,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df + languageName: node + linkType: hard + "ansi-styles@npm:^6.1.0": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" @@ -1699,6 +2145,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^1.1.0": + version: 1.1.0 + resolution: "assertion-error@npm:1.1.0" + checksum: 10c0/25456b2aa333250f01143968e02e4884a34588a8538fbbf65c91a637f1dbfb8069249133cd2f4e530f10f624d206a664e7df30207830b659e9f5298b00a4099b + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -1890,6 +2343,13 @@ __metadata: languageName: node linkType: hard +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 + languageName: node + linkType: hard + "cacache@npm:^18.0.0, cacache@npm:^18.0.2": version: 18.0.2 resolution: "cacache@npm:18.0.2" @@ -1930,6 +2390,21 @@ __metadata: languageName: node linkType: hard +"chai@npm:^4.3.10": + version: 4.4.1 + resolution: "chai@npm:4.4.1" + dependencies: + assertion-error: "npm:^1.1.0" + check-error: "npm:^1.0.3" + deep-eql: "npm:^4.1.3" + get-func-name: "npm:^2.0.2" + loupe: "npm:^2.3.6" + pathval: "npm:^1.1.1" + type-detect: "npm:^4.0.8" + checksum: 10c0/91590a8fe18bd6235dece04ccb2d5b4ecec49984b50924499bdcd7a95c02cb1fd2a689407c19bb854497bde534ef57525cfad6c7fdd2507100fd802fbc2aefbd + languageName: node + linkType: hard + "chalk@npm:^2.3.2, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -1965,6 +2440,15 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^1.0.3": + version: 1.0.3 + resolution: "check-error@npm:1.0.3" + dependencies: + get-func-name: "npm:^2.0.2" + checksum: 10c0/94aa37a7315c0e8a83d0112b5bfb5a8624f7f0f81057c73e4707729cdd8077166c6aefb3d8e2b92c63ee130d4a2ff94bad46d547e12f3238cc1d78342a973841 + languageName: node + linkType: hard + "chokidar@npm:3.6.0, chokidar@npm:^3.5.2": version: 3.6.0 resolution: "chokidar@npm:3.6.0" @@ -2210,6 +2694,13 @@ __metadata: languageName: node linkType: hard +"confbox@npm:^0.1.7": + version: 0.1.7 + resolution: "confbox@npm:0.1.7" + checksum: 10c0/18b40c2f652196a833f3f1a5db2326a8a579cd14eacabfe637e4fc8cb9b68d7cf296139a38c5e7c688ce5041bf46f9adce05932d43fde44cf7e012840b5da111 + languageName: node + linkType: hard + "config-chain@npm:^1.1.11": version: 1.1.13 resolution: "config-chain@npm:1.1.13" @@ -2390,9 +2881,10 @@ __metadata: "@types/js-yaml": "npm:4" "@types/node": "npm:18.16.0" "@types/pg": "npm:8" - "@types/yargs": "npm:17.0.32" "@typescript-eslint/eslint-plugin": "npm:7.6.0" "@typescript-eslint/parser": "npm:7.6.0" + "@vitest/coverage-v8": "npm:^1.6.0" + "@vitest/ui": "npm:^1.6.0" "@xmldom/xmldom": "npm:0.8.10" axios: "npm:1.6.8" chokidar: "npm:3.6.0" @@ -2415,8 +2907,10 @@ __metadata: tedious: "npm:18.1.0" typescript: "npm:5.4.4" typescript-eslint: "npm:7.6.0" + vitest: "npm:1.6.0" + vitest-mock-process: "npm:1.0.4" xpath: "npm:0.0.34" - yargs: "npm:17.7.2" + zod: "npm:3.23.6" bin: dcdx: ./lib/index.js languageName: unknown @@ -2434,6 +2928,24 @@ __metadata: languageName: node linkType: hard +"deep-clone-fn@npm:^1.1.0": + version: 1.1.0 + resolution: "deep-clone-fn@npm:1.1.0" + dependencies: + rfdc: "npm:^1.3.0" + checksum: 10c0/e722b183dce09a66ead1d967be0d7b24fbc0f373d098d34dc25beb1dc112851e0faa6fedd0880ba1a4fcfd1fb55a5897a672642eca5d32c15f1145629f161fa7 + languageName: node + linkType: hard + +"deep-eql@npm:^4.1.3": + version: 4.1.3 + resolution: "deep-eql@npm:4.1.3" + dependencies: + type-detect: "npm:^4.0.0" + checksum: 10c0/ff34e8605d8253e1bf9fe48056e02c6f347b81d9b5df1c6650a1b0f6f847b4a86453b16dc226b34f853ef14b626e85d04e081b022e20b00cd7d54f079ce9bbdd + languageName: node + linkType: hard + "deep-extend@npm:^0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -2507,6 +3019,13 @@ __metadata: languageName: node linkType: hard +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: 10c0/32e27ac7dbffdf2fb0eb5a84efd98a9ad084fbabd5ac9abb8757c6770d5320d2acd172830b28c4add29bb873d59420601dfc805ac4064330ce59b1adfd0593b2 + languageName: node + linkType: hard + "diff@npm:^5.1.0": version: 5.2.0 resolution: "diff@npm:5.2.0" @@ -2769,6 +3288,86 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.20.1": + version: 0.20.2 + resolution: "esbuild@npm:0.20.2" + dependencies: + "@esbuild/aix-ppc64": "npm:0.20.2" + "@esbuild/android-arm": "npm:0.20.2" + "@esbuild/android-arm64": "npm:0.20.2" + "@esbuild/android-x64": "npm:0.20.2" + "@esbuild/darwin-arm64": "npm:0.20.2" + "@esbuild/darwin-x64": "npm:0.20.2" + "@esbuild/freebsd-arm64": "npm:0.20.2" + "@esbuild/freebsd-x64": "npm:0.20.2" + "@esbuild/linux-arm": "npm:0.20.2" + "@esbuild/linux-arm64": "npm:0.20.2" + "@esbuild/linux-ia32": "npm:0.20.2" + "@esbuild/linux-loong64": "npm:0.20.2" + "@esbuild/linux-mips64el": "npm:0.20.2" + "@esbuild/linux-ppc64": "npm:0.20.2" + "@esbuild/linux-riscv64": "npm:0.20.2" + "@esbuild/linux-s390x": "npm:0.20.2" + "@esbuild/linux-x64": "npm:0.20.2" + "@esbuild/netbsd-x64": "npm:0.20.2" + "@esbuild/openbsd-x64": "npm:0.20.2" + "@esbuild/sunos-x64": "npm:0.20.2" + "@esbuild/win32-arm64": "npm:0.20.2" + "@esbuild/win32-ia32": "npm:0.20.2" + "@esbuild/win32-x64": "npm:0.20.2" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/66398f9fb2c65e456a3e649747b39af8a001e47963b25e86d9c09d2a48d61aa641b27da0ce5cad63df95ad246105e1d83e7fee0e1e22a0663def73b1c5101112 + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.2 resolution: "escalade@npm:3.1.2" @@ -2917,6 +3516,15 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -2938,7 +3546,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:^8.0.0": +"execa@npm:^8.0.0, execa@npm:^8.0.1": version: 8.0.1 resolution: "execa@npm:8.0.1" dependencies: @@ -3030,6 +3638,13 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.8.1": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 10c0/03448d630c0a583abea594835a9fdb2aaf7d67787055a761515bf4ed862913cfd693b4c4ffd5c3f3b355a70cf1e19033e9ae5aedcca103188aaff91b8bd6e293 + languageName: node + linkType: hard + "figures@npm:^2.0.0": version: 2.0.0 resolution: "figures@npm:2.0.0" @@ -3212,7 +3827,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:~2.3.2": +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -3222,7 +3837,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -3296,6 +3911,13 @@ __metadata: languageName: node linkType: hard +"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": + version: 2.0.2 + resolution: "get-func-name@npm:2.0.2" + checksum: 10c0/89830fd07623fa73429a711b9daecdb304386d237c71268007f788f113505ef1d4cc2d0b9680e072c5082490aec9df5d7758bf5ac6f1c37062855e8e3dc0b9df + languageName: node + linkType: hard + "get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" @@ -3388,6 +4010,20 @@ __metadata: languageName: node linkType: hard +"glob@npm:^7.1.4": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: "npm:^1.0.0" + inflight: "npm:^1.0.4" + inherits: "npm:2" + minimatch: "npm:^3.1.1" + once: "npm:^1.3.0" + path-is-absolute: "npm:^1.0.0" + checksum: 10c0/65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe + languageName: node + linkType: hard + "glob@npm:^8.0.3": version: 8.1.0 resolution: "glob@npm:8.1.0" @@ -3585,6 +4221,13 @@ __metadata: languageName: node linkType: hard +"html-escaper@npm:^2.0.0": + version: 2.0.2 + resolution: "html-escaper@npm:2.0.2" + checksum: 10c0/208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -4128,6 +4771,45 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.2": + version: 3.2.2 + resolution: "istanbul-lib-coverage@npm:3.2.2" + checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b + languageName: node + linkType: hard + +"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": + version: 3.0.1 + resolution: "istanbul-lib-report@npm:3.0.1" + dependencies: + istanbul-lib-coverage: "npm:^3.0.0" + make-dir: "npm:^4.0.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/84323afb14392de8b6a5714bd7e9af845cfbd56cfe71ed276cda2f5f1201aea673c7111901227ee33e68e4364e288d73861eb2ed48f6679d1e69a43b6d9b3ba7 + languageName: node + linkType: hard + +"istanbul-lib-source-maps@npm:^5.0.4": + version: 5.0.4 + resolution: "istanbul-lib-source-maps@npm:5.0.4" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.23" + debug: "npm:^4.1.1" + istanbul-lib-coverage: "npm:^3.0.0" + checksum: 10c0/48b48294590675005ba439888a53157fc71a99d78321428f3ce5f64e28cdfb6bc6eb45871333f448437118ef56a0ef371f4958163e2c2d066d3a703415a71b2e + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.1.6": + version: 3.1.7 + resolution: "istanbul-reports@npm:3.1.7" + dependencies: + html-escaper: "npm:^2.0.0" + istanbul-lib-report: "npm:^3.0.0" + checksum: 10c0/a379fadf9cf8dc5dfe25568115721d4a7eb82fbd50b005a6672aff9c6989b20cc9312d7865814e0859cd8df58cbf664482e1d3604be0afde1f7fc3ccc1394a51 + languageName: node + linkType: hard + "jackspeak@npm:^2.3.6": version: 2.3.6 resolution: "jackspeak@npm:2.3.6" @@ -4162,6 +4844,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^9.0.0": + version: 9.0.0 + resolution: "js-tokens@npm:9.0.0" + checksum: 10c0/4ad1c12f47b8c8b2a3a99e29ef338c1385c7b7442198a425f3463f3537384dab6032012791bfc2f056ea5ecdb06b1ed4f70e11a3ab3f388d3dcebfe16a52b27d + languageName: node + linkType: hard + "js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" @@ -4503,6 +5192,16 @@ __metadata: languageName: node linkType: hard +"local-pkg@npm:^0.5.0": + version: 0.5.0 + resolution: "local-pkg@npm:0.5.0" + dependencies: + mlly: "npm:^1.4.2" + pkg-types: "npm:^1.0.3" + checksum: 10c0/f61cbd00d7689f275558b1a45c7ff2a3ddf8472654123ed880215677b9adfa729f1081e50c27ffb415cdb9fa706fb755fec5e23cdd965be375c8059e87ff1cc9 + languageName: node + linkType: hard + "locate-path@npm:^2.0.0": version: 2.0.0 resolution: "locate-path@npm:2.0.0" @@ -4620,6 +5319,15 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^2.3.6, loupe@npm:^2.3.7": + version: 2.3.7 + resolution: "loupe@npm:2.3.7" + dependencies: + get-func-name: "npm:^2.0.1" + checksum: 10c0/71a781c8fc21527b99ed1062043f1f2bb30bdaf54fa4cf92463427e1718bc6567af2988300bc243c1f276e4f0876f29e3cbf7b58106fdc186915687456ce5bf4 + languageName: node + linkType: hard + "lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": version: 10.2.0 resolution: "lru-cache@npm:10.2.0" @@ -4659,6 +5367,35 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.5": + version: 0.30.10 + resolution: "magic-string@npm:0.30.10" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.4.15" + checksum: 10c0/aa9ca17eae571a19bce92c8221193b6f93ee8511abb10f085e55ffd398db8e4c089a208d9eac559deee96a08b7b24d636ea4ab92f09c6cf42a7d1af51f7fd62b + languageName: node + linkType: hard + +"magicast@npm:^0.3.3": + version: 0.3.4 + resolution: "magicast@npm:0.3.4" + dependencies: + "@babel/parser": "npm:^7.24.4" + "@babel/types": "npm:^7.24.0" + source-map-js: "npm:^1.2.0" + checksum: 10c0/7ebaaac397b13c31ca05e6d9649296751d76749b945d10a0800107872119fbdf267acdb604571d25e38ec6fd7ab3568a951b6e76eaef1caba9eaa11778fd9783 + languageName: node + linkType: hard + +"make-dir@npm:^4.0.0": + version: 4.0.0 + resolution: "make-dir@npm:4.0.0" + dependencies: + semver: "npm:^7.5.3" + checksum: 10c0/69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68 + languageName: node + linkType: hard + "make-fetch-happen@npm:^13.0.0": version: 13.0.0 resolution: "make-fetch-happen@npm:13.0.0" @@ -4766,7 +5503,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -4910,6 +5647,18 @@ __metadata: languageName: node linkType: hard +"mlly@npm:^1.4.2, mlly@npm:^1.6.1": + version: 1.7.0 + resolution: "mlly@npm:1.7.0" + dependencies: + acorn: "npm:^8.11.3" + pathe: "npm:^1.1.2" + pkg-types: "npm:^1.1.0" + ufo: "npm:^1.5.3" + checksum: 10c0/0b90e5b86e35897fd830624635b30052d0dfeb01b62a021fff4c0a5f46fbc617db685acfbc8c1c7cdcf687d9ffb8d54f3c1b0087ab953232cb3c158a2fb2d770 + languageName: node + linkType: hard + "moment-timezone@npm:^0.5.43": version: 0.5.45 resolution: "moment-timezone@npm:0.5.45" @@ -4926,6 +5675,13 @@ __metadata: languageName: node linkType: hard +"mrmime@npm:^2.0.0": + version: 2.0.0 + resolution: "mrmime@npm:2.0.0" + checksum: 10c0/312b35ed288986aec90955410b21ed7427fd1e4ee318cb5fc18765c8d029eeded9444faa46589e5b1ed6b35fb2054a802ac8dcb917ddf6b3e189cb3bf11a965c + languageName: node + linkType: hard + "ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" @@ -4992,6 +5748,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 + languageName: node + linkType: hard + "native-duplexpair@npm:^1.0.0": version: 1.0.0 resolution: "native-duplexpair@npm:1.0.0" @@ -5452,6 +6217,15 @@ __metadata: languageName: node linkType: hard +"p-limit@npm:^5.0.0": + version: 5.0.0 + resolution: "p-limit@npm:5.0.0" + dependencies: + yocto-queue: "npm:^1.0.0" + checksum: 10c0/574e93b8895a26e8485eb1df7c4b58a1a6e8d8ae41b1750cc2cc440922b3d306044fc6e9a7f74578a883d46802d9db72b30f2e612690fcef838c173261b1ed83 + languageName: node + linkType: hard + "p-locate@npm:^2.0.0": version: 2.0.0 resolution: "p-locate@npm:2.0.0" @@ -5618,6 +6392,13 @@ __metadata: languageName: node linkType: hard +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 + languageName: node + linkType: hard + "path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -5663,6 +6444,20 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^1.1.1, pathe@npm:^1.1.2": + version: 1.1.2 + resolution: "pathe@npm:1.1.2" + checksum: 10c0/64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897 + languageName: node + linkType: hard + +"pathval@npm:^1.1.1": + version: 1.1.1 + resolution: "pathval@npm:1.1.1" + checksum: 10c0/f63e1bc1b33593cdf094ed6ff5c49c1c0dc5dc20a646ca9725cc7fe7cd9995002d51d5685b9b2ec6814342935748b711bafa840f84c0bb04e38ff40a335c94dc + languageName: node + linkType: hard + "pg-cloudflare@npm:^1.1.1": version: 1.1.1 resolution: "pg-cloudflare@npm:1.1.1" @@ -5797,6 +6592,17 @@ __metadata: languageName: node linkType: hard +"pkg-types@npm:^1.0.3, pkg-types@npm:^1.1.0": + version: 1.1.0 + resolution: "pkg-types@npm:1.1.0" + dependencies: + confbox: "npm:^0.1.7" + mlly: "npm:^1.6.1" + pathe: "npm:^1.1.2" + checksum: 10c0/b350da13d2dab7dc2fa9d65a08a2038d841d8d8c94bf3878dd911a522e20da50d6662bab510fa329e363e403892374b3b847ebf7b3e10011805cdefb00f228fd + languageName: node + linkType: hard + "possible-typed-array-names@npm:^1.0.0": version: 1.0.0 resolution: "possible-typed-array-names@npm:1.0.0" @@ -5814,6 +6620,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.38": + version: 8.4.38 + resolution: "postcss@npm:8.4.38" + dependencies: + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.2.0" + checksum: 10c0/955407b8f70cf0c14acf35dab3615899a2a60a26718a63c848cf3c29f2467b0533991b985a2b994430d890bd7ec2b1963e36352b0774a19143b5f591540f7c06 + languageName: node + linkType: hard + "postgres-array@npm:~2.0.0": version: 2.0.0 resolution: "postgres-array@npm:2.0.0" @@ -5888,6 +6705,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" + dependencies: + "@jest/schemas": "npm:^29.6.3" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^18.0.0" + checksum: 10c0/edc5ff89f51916f036c62ed433506b55446ff739358de77207e63e88a28ca2894caac6e73dcb68166a606e51c8087d32d400473e6a9fdd2dbe743f46c9c0276f + languageName: node + linkType: hard + "proc-log@npm:^3.0.0": version: 3.0.0 resolution: "proc-log@npm:3.0.0" @@ -6033,6 +6861,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^18.0.0": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 + languageName: node + linkType: hard + "read-cmd-shim@npm:^4.0.0": version: 4.0.0 resolution: "read-cmd-shim@npm:4.0.0" @@ -6250,6 +7085,13 @@ __metadata: languageName: node linkType: hard +"rfdc@npm:^1.3.0": + version: 1.3.1 + resolution: "rfdc@npm:1.3.1" + checksum: 10c0/69f65e3ed30970f8055fac9fbbef9ce578800ca19554eab1dcbffe73a4b8aef536bc4248313889cf25e3b4e38b212c721eabe30856575bf2b2bc3d90f8ba93ef + languageName: node + linkType: hard + "rollup-plugin-executable@npm:1.6.3": version: 1.6.3 resolution: "rollup-plugin-executable@npm:1.6.3" @@ -6319,6 +7161,69 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.13.0": + version: 4.17.2 + resolution: "rollup@npm:4.17.2" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.17.2" + "@rollup/rollup-android-arm64": "npm:4.17.2" + "@rollup/rollup-darwin-arm64": "npm:4.17.2" + "@rollup/rollup-darwin-x64": "npm:4.17.2" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.17.2" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.17.2" + "@rollup/rollup-linux-arm64-gnu": "npm:4.17.2" + "@rollup/rollup-linux-arm64-musl": "npm:4.17.2" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.17.2" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.17.2" + "@rollup/rollup-linux-s390x-gnu": "npm:4.17.2" + "@rollup/rollup-linux-x64-gnu": "npm:4.17.2" + "@rollup/rollup-linux-x64-musl": "npm:4.17.2" + "@rollup/rollup-win32-arm64-msvc": "npm:4.17.2" + "@rollup/rollup-win32-ia32-msvc": "npm:4.17.2" + "@rollup/rollup-win32-x64-msvc": "npm:4.17.2" + "@types/estree": "npm:1.0.5" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-powerpc64le-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/4fa6644e5c7fc4a34f654ea7e209be6c2c5897ed9dd43e7135230137204df748a795c7553804130f6c41da0b71e83f8c35a4a7881d385a77996adee50b609a6e + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -6565,6 +7470,13 @@ __metadata: languageName: node linkType: hard +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + "signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" @@ -6617,6 +7529,17 @@ __metadata: languageName: node linkType: hard +"sirv@npm:^2.0.4": + version: 2.0.4 + resolution: "sirv@npm:2.0.4" + dependencies: + "@polka/url": "npm:^1.0.0-next.24" + mrmime: "npm:^2.0.0" + totalist: "npm:^3.0.0" + checksum: 10c0/68f8ee857f6a9415e9c07a1f31c7c561df8d5f1b1ba79bee3de583fa37da8718def5309f6b1c6e2c3ef77de45d74f5e49efc7959214443aa92d42e9c99180a4e + languageName: node + linkType: hard + "skin-tone@npm:^2.0.0": version: 2.0.0 resolution: "skin-tone@npm:2.0.0" @@ -6675,6 +7598,13 @@ __metadata: languageName: node linkType: hard +"source-map-js@npm:^1.2.0": + version: 1.2.0 + resolution: "source-map-js@npm:1.2.0" + checksum: 10c0/7e5f896ac10a3a50fe2898e5009c58ff0dc102dcb056ed27a354623a0ece8954d4b2649e1a1b2b52ef2e161d26f8859c7710350930751640e71e374fe2d321a4 + languageName: node + linkType: hard + "source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -6806,6 +7736,20 @@ __metadata: languageName: node linkType: hard +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 + languageName: node + linkType: hard + +"std-env@npm:^3.5.0": + version: 3.7.0 + resolution: "std-env@npm:3.7.0" + checksum: 10c0/60edf2d130a4feb7002974af3d5a5f3343558d1ccf8d9b9934d225c638606884db4a20d2fe6440a09605bca282af6b042ae8070a10490c0800d69e82e478f41e + languageName: node + linkType: hard + "stoppable@npm:^1.1.0": version: 1.1.0 resolution: "stoppable@npm:1.1.0" @@ -6943,6 +7887,15 @@ __metadata: languageName: node linkType: hard +"strip-literal@npm:^2.0.0": + version: 2.1.0 + resolution: "strip-literal@npm:2.1.0" + dependencies: + js-tokens: "npm:^9.0.0" + checksum: 10c0/bc8b8c8346125ae3c20fcdaf12e10a498ff85baf6f69597b4ab2b5fbf2e58cfd2827f1a44f83606b852da99a5f6c8279770046ddea974c510c17c98934c9cc24 + languageName: node + linkType: hard + "strnum@npm:^1.0.5": version: 1.0.5 resolution: "strnum@npm:1.0.5" @@ -7091,6 +8044,17 @@ __metadata: languageName: node linkType: hard +"test-exclude@npm:^6.0.0": + version: 6.0.0 + resolution: "test-exclude@npm:6.0.0" + dependencies: + "@istanbuljs/schema": "npm:^0.1.2" + glob: "npm:^7.1.4" + minimatch: "npm:^3.0.4" + checksum: 10c0/019d33d81adff3f9f1bfcff18125fb2d3c65564f437d9be539270ee74b994986abb8260c7c2ce90e8f30162178b09dbbce33c6389273afac4f36069c48521f57 + languageName: node + linkType: hard + "text-extensions@npm:^2.0.0": version: 2.4.0 resolution: "text-extensions@npm:2.4.0" @@ -7156,6 +8120,34 @@ __metadata: languageName: node linkType: hard +"tinybench@npm:^2.5.1": + version: 2.8.0 + resolution: "tinybench@npm:2.8.0" + checksum: 10c0/5a9a642351fa3e4955e0cbf38f5674be5f3ba6730fd872fd23a5c953ad6c914234d5aba6ea41ef88820180a81829ceece5bd8d3967c490c5171bca1141c2f24d + languageName: node + linkType: hard + +"tinypool@npm:^0.8.3": + version: 0.8.4 + resolution: "tinypool@npm:0.8.4" + checksum: 10c0/779c790adcb0316a45359652f4b025958c1dff5a82460fe49f553c864309b12ad732c8288be52f852973bc76317f5e7b3598878aee0beb8a33322c0e72c4a66c + languageName: node + linkType: hard + +"tinyspy@npm:^2.2.0": + version: 2.2.1 + resolution: "tinyspy@npm:2.2.1" + checksum: 10c0/0b4cfd07c09871e12c592dfa7b91528124dc49a4766a0b23350638c62e6a483d5a2a667de7e6282246c0d4f09996482ddaacbd01f0c05b7ed7e0f79d32409bdc + languageName: node + linkType: hard + +"to-fast-properties@npm:^2.0.0": + version: 2.0.0 + resolution: "to-fast-properties@npm:2.0.0" + checksum: 10c0/b214d21dbfb4bce3452b6244b336806ffea9c05297148d32ebb428d5c43ce7545bdfc65a1ceb58c9ef4376a65c0cb2854d645f33961658b3e3b4f84910ddcdd7 + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -7172,6 +8164,13 @@ __metadata: languageName: node linkType: hard +"totalist@npm:^3.0.0": + version: 3.0.1 + resolution: "totalist@npm:3.0.1" + checksum: 10c0/4bb1fadb69c3edbef91c73ebef9d25b33bbf69afe1e37ce544d5f7d13854cda15e47132f3e0dc4cafe300ddb8578c77c50a65004d8b6e97e77934a69aa924863 + languageName: node + linkType: hard + "touch@npm:^3.1.0": version: 3.1.0 resolution: "touch@npm:3.1.0" @@ -7244,6 +8243,13 @@ __metadata: languageName: node linkType: hard +"type-detect@npm:^4.0.0, type-detect@npm:^4.0.8": + version: 4.0.8 + resolution: "type-detect@npm:4.0.8" + checksum: 10c0/8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd + languageName: node + linkType: hard + "type-fest@npm:^1.0.1": version: 1.4.0 resolution: "type-fest@npm:1.4.0" @@ -7367,6 +8373,13 @@ __metadata: languageName: node linkType: hard +"ufo@npm:^1.5.3": + version: 1.5.3 + resolution: "ufo@npm:1.5.3" + checksum: 10c0/1df10702582aa74f4deac4486ecdfd660e74be057355f1afb6adfa14243476cf3d3acff734ccc3d0b74e9bfdefe91d578f3edbbb0a5b2430fe93cd672370e024 + languageName: node + linkType: hard + "uglify-js@npm:^3.1.4": version: 3.17.4 resolution: "uglify-js@npm:3.17.4" @@ -7515,6 +8528,122 @@ __metadata: languageName: node linkType: hard +"vite-node@npm:1.6.0": + version: 1.6.0 + resolution: "vite-node@npm:1.6.0" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.3.4" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + vite: "npm:^5.0.0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/0807e6501ac7763e0efa2b4bd484ce99fb207e92c98624c9f8999d1f6727ac026e457994260fa7fdb7060d87546d197081e46a705d05b0136a38b6f03715cbc2 + languageName: node + linkType: hard + +"vite@npm:^5.0.0": + version: 5.2.11 + resolution: "vite@npm:5.2.11" + dependencies: + esbuild: "npm:^0.20.1" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.38" + rollup: "npm:^4.13.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/664b8d68e4f5152ae16bd2041af1bbaf11c43630ac461835bc31ff7d019913b33e465386e09f66dc1037d7aeefbb06939e0173787c063319bc2bd30c3b9ad8e4 + languageName: node + linkType: hard + +"vitest-mock-process@npm:1.0.4": + version: 1.0.4 + resolution: "vitest-mock-process@npm:1.0.4" + dependencies: + deep-clone-fn: "npm:^1.1.0" + peerDependencies: + vitest: <1 + checksum: 10c0/9450ecaca804946c863548fdf8cf2be45e3de145cf87e888e1691769acc7bb35101984f1d5af39300d6f75870cdfcc5d4e7d8a03a6478149fa3ea13cc4a07279 + languageName: node + linkType: hard + +"vitest@npm:1.6.0": + version: 1.6.0 + resolution: "vitest@npm:1.6.0" + dependencies: + "@vitest/expect": "npm:1.6.0" + "@vitest/runner": "npm:1.6.0" + "@vitest/snapshot": "npm:1.6.0" + "@vitest/spy": "npm:1.6.0" + "@vitest/utils": "npm:1.6.0" + acorn-walk: "npm:^8.3.2" + chai: "npm:^4.3.10" + debug: "npm:^4.3.4" + execa: "npm:^8.0.1" + local-pkg: "npm:^0.5.0" + magic-string: "npm:^0.30.5" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + std-env: "npm:^3.5.0" + strip-literal: "npm:^2.0.0" + tinybench: "npm:^2.5.1" + tinypool: "npm:^0.8.3" + vite: "npm:^5.0.0" + vite-node: "npm:1.6.0" + why-is-node-running: "npm:^2.2.2" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/node": ^18.0.0 || >=20.0.0 + "@vitest/browser": 1.6.0 + "@vitest/ui": 1.6.0 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 10c0/065da5b8ead51eb174d93dac0cd50042ca9539856dc25e340ea905d668c41961f7e00df3e388e6c76125b2c22091db2e8465f993d0f6944daf9598d549e562e7 + languageName: node + linkType: hard + "walk-up-path@npm:^3.0.1": version: 3.0.1 resolution: "walk-up-path@npm:3.0.1" @@ -7579,6 +8708,18 @@ __metadata: languageName: node linkType: hard +"why-is-node-running@npm:^2.2.2": + version: 2.2.2 + resolution: "why-is-node-running@npm:2.2.2" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/805d57eb5d33f0fb4e36bae5dceda7fd8c6932c2aeb705e30003970488f1a2bc70029ee64be1a0e1531e2268b11e65606e88e5b71d667ea745e6dc48fc9014bd + languageName: node + linkType: hard + "wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" @@ -7694,33 +8835,33 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.5.1": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" +"yargs@npm:^16.0.0": + version: 16.2.0 + resolution: "yargs@npm:16.2.0" dependencies: - cliui: "npm:^8.0.1" + cliui: "npm:^7.0.2" escalade: "npm:^3.1.1" get-caller-file: "npm:^2.0.5" require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.3" + string-width: "npm:^4.2.0" y18n: "npm:^5.0.5" - yargs-parser: "npm:^21.1.1" - checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + yargs-parser: "npm:^20.2.2" + checksum: 10c0/b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 languageName: node linkType: hard -"yargs@npm:^16.0.0": - version: 16.2.0 - resolution: "yargs@npm:16.2.0" +"yargs@npm:^17.5.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" dependencies: - cliui: "npm:^7.0.2" + cliui: "npm:^8.0.1" escalade: "npm:^3.1.1" get-caller-file: "npm:^2.0.5" require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.0" + string-width: "npm:^4.2.3" y18n: "npm:^5.0.5" - yargs-parser: "npm:^20.2.2" - checksum: 10c0/b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 languageName: node linkType: hard @@ -7730,3 +8871,17 @@ __metadata: checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f languageName: node linkType: hard + +"yocto-queue@npm:^1.0.0": + version: 1.0.0 + resolution: "yocto-queue@npm:1.0.0" + checksum: 10c0/856117aa15cf5103d2a2fb173f0ab4acb12b4b4d0ed3ab249fdbbf612e55d1cadfd27a6110940e24746fb0a78cf640b522cc8bca76f30a3b00b66e90cf82abe0 + languageName: node + linkType: hard + +"zod@npm:3.23.6": + version: 3.23.6 + resolution: "zod@npm:3.23.6" + checksum: 10c0/9181606c656235cf6454c60ded6ec783226acafc0206844e9729b4d3c1c6ade411b195fa2590e882d3368c82580a44869567f7aebab005bf8d8476db6186d43f + languageName: node + linkType: hard