Skip to content

Commit

Permalink
Merge pull request #14 from collabsoft-net/next
Browse files Browse the repository at this point in the history
Preparing 1.2.0 release
  • Loading branch information
remie authored May 25, 2024
2 parents f2ff5f6 + b3aa05c commit bd551d1
Show file tree
Hide file tree
Showing 62 changed files with 16,475 additions and 965 deletions.
16 changes: 10 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,25 @@ jobs:
- node_modules

- run:
name: Publish to NPM
command: |
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
npm publish --access public
name: Test
command: yarn test

- run:
name: Publish
command: npx semantic-release

executors:

nodejs:
docker:
- image: cimg/node:18.16
- image: cimg/node:20.12

workflows:
deploy:
jobs:
- publish:
filters:
branches:
only: main
only:
- main
- next
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
.yarn/install-state.gz
dist/
lib/
node_modules
coverage/
node_modules
.DS_Store
144 changes: 144 additions & 0 deletions __mocks__/exit-hook.js
Original file line number Diff line number Diff line change
@@ -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);
}
Binary file not shown.
1 change: 1 addition & 0 deletions assets/versions.json

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions generateVersionList.mjs
Original file line number Diff line number Diff line change
@@ -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: 'postgresql', 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));
})();
37 changes: 32 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dcdx",
"version": "1.0.0",
"version": "0.0.0-development",
"author": "Collabsoft <info@collabsoft.net>",
"description": "The Unofficial CLI for Atlassian Data Center Plugin Development",
"type": "module",
Expand All @@ -19,14 +19,17 @@
},
"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",
"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",
Expand All @@ -44,29 +47,53 @@
"@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",
"@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",
"rollup": "4.14.1",
"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",
"axios": "1.6.8",
"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",
"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",
"zod": "3.23.6"
},
"release": {
"branches": [
{
"name": "main"
},
{
"name": "next",
"channel": "next",
"prerelease": true
}
]
}
}
25 changes: 19 additions & 6 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,32 @@
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';

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 if (item.name.endsWith('ts')) {
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 => ({
input: `./dist/${file}`,
...[ 'index.js', ...commands.map(item => item.replace('src/', '').replace('ts', 'js')) ].map(file => ({
input: `./dist/src/${file}`,
output: {
file: `./lib/${file}`,
format: 'es'
format: 'es',
sourcemap: 'inline'
},
external: [ /node_modules/ ],
plugins: [
Expand All @@ -22,7 +36,6 @@ export default [
nodeResolve({
preferBuiltins: true
}),
terser({ keep_classnames: true, keep_fnames: true }),
executable()
],
onwarn(warning, rollupWarn) {
Expand Down
Loading

0 comments on commit bd551d1

Please sign in to comment.