Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create initial modelina cli #1785

Merged
merged 16 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ examples/integrate-with-react
src/processors/TemplateInputProcessor.ts
test/processors/TemplateInputProcessor.spec.ts
modelina-website
modelina-cli

test/runtime/runtime-**
2 changes: 2 additions & 0 deletions modelina-cli/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/lib
/tmp
25 changes: 25 additions & 0 deletions modelina-cli/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"extends": [
"oclif",
"oclif-typescript",
"prettier"
],
"rules": {
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"new-cap": "off",
"require-await": "off",
"no-useless-call": "off",
"no-await-in-loop": "off",
"no-warning-comments": "error",
"prettier/prettier": "off",
"complexity": "off",
"unicorn/filename-case": "off",
"unicorn/prefer-array-some": "off",
"unicorn/prefer-top-level-await": "off",
"unicorn/no-abusive-eslint-disable": "off",
"unicorn/no-await-expression-member": "off",
"security/detect-non-literal-fs-filename": "off"
}
}
12 changes: 12 additions & 0 deletions modelina-cli/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
**/.DS_Store
*-debug.log
*-error.log
/.idea
/.nyc_output
/lib
/package-lock.json
/tmp
/yarn.lock
node_modules
oclif.lock
oclif.manifest.json
15 changes: 15 additions & 0 deletions modelina-cli/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"require": [
"ts-node/register"
],
"watch-extensions": [
"ts"
],
"recursive": true,
"reporter": "spec",
"timeout": 60000,
"node-option": [
"loader=ts-node/esm",
"experimental-specifier-resolution=node"
]
}
1 change: 1 addition & 0 deletions modelina-cli/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"@oclif/prettier-config"
3 changes: 3 additions & 0 deletions modelina-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Modelina CLI

A CLI for generating models using AsyncAPI Modelina.
18 changes: 18 additions & 0 deletions modelina-cli/bin/dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env nod

const oclif = require('@oclif/core')

const path = require('path')
const project = path.join(__dirname, '..', 'tsconfig.json')

// In dev mode -> use ts-node and dev plugins
process.env.NODE_ENV = 'development'

require('ts-node').register({project})

// In dev mode, always show stack traces
oclif.settings.debug = true;

// Start the CLI
oclif.run().then(oclif.flush).catch(oclif.Errors.handle)

3 changes: 3 additions & 0 deletions modelina-cli/bin/dev.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

node "%~dp0\dev" %*
11 changes: 11 additions & 0 deletions modelina-cli/bin/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env node

const oclif = require('@oclif/core');

oclif.run()
.then(require('@oclif/core/flush'))
.catch((err) => {
const oclifHandler = require('@oclif/core/handle');
return oclifHandler(err.message);
});

3 changes: 3 additions & 0 deletions modelina-cli/bin/run.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

node "%~dp0\run" %*
Empty file added modelina-cli/dist/.gitkeep
Empty file.
104 changes: 104 additions & 0 deletions modelina-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
devilkiller-ag marked this conversation as resolved.
Show resolved Hide resolved
"name": "@asyncapi/modelina",
"description": "CLI to work with Modelina",
"version": "0.0.0",
"author": "@asyncapi",
"bin": {
"asyncapi": "./bin/run"
},
"bugs": "https://github.com/asyncapi/modelina/issues",
"dependencies": {
"@asyncapi/modelina": "^3.2.3",
"@oclif/core": "^1.26.2",
"@oclif/errors": "^1.3.6",
"@oclif/plugin-not-found": "^2.3.22",
"js-yaml": "^4.1.0",
"node-fetch": "^2.0.0",
"oclif": "^3.7.3"
},
"devDependencies": {
"@oclif/prettier-config": "^0.2.1",
"@oclif/test": "^2",
"@types/chai": "^4",
"@types/fs-extra": "^11.0.4",
"@types/js-yaml": "^4.0.9",
"@types/mocha": "^10",
"@types/node": "^18",
"@types/node-fetch": "^2.6.11",
"@types/rimraf": "^4.0.5",
"chai": "^4",
"eslint": "^8",
"eslint-config-oclif": "^4.0.0",
"eslint-config-oclif-typescript": "^1.0.3",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-unicorn": "^51.0.1",
"mocha": "^10",
"nyc": "^15.1.0",
"rimraf": "^5.0.5",
"shx": "^0.3.4",
"ts-node": "^10.9.2",
"typescript": "4.6.4"
},
"files": [
"/bin",
"/lib",
"/assets",
"/npm-shrinkwrap.json",
"/oclif.manifest.json"
],
"homepage": "https://github.com/asyncapi/modelina",
"keywords": [
"modelina",
"cli",
"modelina-cli"
],
"license": "Apache-2.0",
"main": "lib/index.js",
"oclif": {
"commands": "./lib/commands",
"bin": "modelina",
"plugins": [],
"macos": {
"identifier": "com.asyncapi.modelina"
},
"topicSeparator": " ",
"topics": {
"config:context": {},
"config": {
"description": "CLI config settings"
},
"generate": {
"description": "Generate models"
}
}
},
"publishConfig": {
"access": "public"
},
"repository": "asyncapi/modelina",
"scripts": {
"build": "rimraf lib && tsc && oclif manifest && echo \"Build Completed\"",
"dev": "tsc --watch",
"generate:readme:create": "printf '\n\n# Usage\n\n<!-- usage -->\n\n# Commands\n\n<!-- commands -->\n' > scripts/README.md",
"generate:readme:commands": "npm run build && cd scripts && DEBUG=* oclif readme",
"generate:assets": "npm run generate:readme:toc && npm run generate:commands",
"generate:commands": "npm run generate:readme:create && npm run generate:readme:commands && node ./scripts/updateUsageDocs.js && rimraf ./scripts/README.md",
"generate:readme:toc": "markdown-toc -i README.md",
"lint": "eslint --max-warnings 0 --config .eslintrc .",
"lint:fix": "eslint --max-warnings 5 --config .eslintrc . --fix",
"pack:macos": "oclif pack macos && npm run pack:rename",
"pack:linux": "oclif pack deb && npm run pack:rename",
"pack:tarballs": "oclif pack tarballs -t linux-x64 && npm run pack:rename",
"pack:windows": "oclif pack win && npm run pack:rename",
"pack:rename": "echo node scripts/releasePackagesRename.js",
"prepublishOnly": "npm run build",
"pretest": "npm run build",
"pretest:coverage": "npm run build",
"test": "npm run test:unit",
"test:unit": "cross-env NODE_ENV=development CUSTOM_CONTEXT_FILENAME=\"test.asyncapi-modelina\" CUSTOM_CONTEXT_FILE_LOCATION=\"\" nyc --extension .ts mocha --require ts-node/register --require test/helpers/init.js --reporter spec --timeout 100000 \"test/**/*.test.ts\"",
"get-version": "echo $npm_package_version",
"createhook": "oclif generate hook myhook --event=command_not_found",
"createhookinit": "oclif generate hook inithook --event=init"
},
"types": "lib/index.d.ts"
}
77 changes: 77 additions & 0 deletions modelina-cli/scripts/releasePackagesRename.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* eslint-disable @typescript-eslint/no-var-requires */

const { rename, access, mkdir } = require('fs').promises;
const packageJson = require('../package.json');
const path = require('path');
const simpleGit = require('simple-git');
const git = simpleGit({baseDir: process.cwd()});

async function fileExists(checkPath) {
try {
await access(checkPath);
return true;
} catch (e) {
return false;
}
}

async function checkAndRenameFile(generatedPath, newPath) {
if (await fileExists(generatedPath)) {
await rename(generatedPath, newPath);
}
}

async function createDirectory(directoryPath) {
await mkdir(directoryPath);
}

async function renameDeb({version, name, sha}) {
const dist = 'dist/deb';

// deb package naming convention: https://github.com/oclif/oclif/blob/fb5da961f925fa0eba5c5b05c8cee0c9bd156c00/src/upload-util.ts#L51
const generatedPath = path.resolve(dist, `${name}_${version}.${sha}-1_amd64.deb`);
const newPath = path.resolve(dist, 'modelinacli.deb');
await checkAndRenameFile(generatedPath, newPath);
}

async function renameTar({version, name, sha}) {
const dist = 'dist';

const generatedPath = path.resolve(dist, `${name}-v${version}-${sha}-linux-x64.tar.gz`);
// for tarballs, the files are generated in `dist/` directory.
// Creates a new `tar` directory(`dist/tar`), and moves the generated tarball inside that directory.
const tarDirectory = path.resolve(dist, 'tar');
await createDirectory(tarDirectory);
const newPath = path.resolve(tarDirectory, 'modelinacli.tar.gz');
await checkAndRenameFile(generatedPath, newPath);
}

async function renameWindows({version, name, sha, arch}) {
const dist = 'dist/win32';

const generatedPath = path.resolve(dist, `${name}-v${version}-${sha}-${arch}.exe`);
const newPath = path.resolve(dist, `modelinacli.${arch}.exe`);
await checkAndRenameFile(generatedPath, newPath);
}

async function renamePkg({version, name, sha, arch}) {
const dist = 'dist/macos';

const generatedPath = path.resolve(dist, `${name}-v${version}-${sha}-${arch}.pkg`);
const newPath = path.resolve(dist, `modelinacli.${arch}.pkg`);
await checkAndRenameFile(generatedPath, newPath);
}

async function renamePackages() {
const version = packageJson.version;
const name = 'modelinacli';
const sha = await git.revparse(['--short', 'HEAD']);
await renameDeb({version: version.split('-')[0], name, sha});
await renamePkg({version, name, sha, arch: 'x64'});
await renamePkg({version, name, sha, arch: 'arm64'});
await renameWindows({version, name, sha, arch: 'x64'});
await renameWindows({version, name, sha, arch: 'x86'});
await renameTar({version, name, sha});
}

renamePackages();
73 changes: 73 additions & 0 deletions modelina-cli/scripts/updateUsageDocs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const {writeFile, readFile} = require('fs').promises;

// Define the paths to the README and usage files
const README_PATH = './scripts/README.md'; // File path for the generated README file
const USAGE_PATH = './docs/usage.md'; // File path for the usage documentation file

const header = `---
title: 'Usage'
weight: 40
---

<!--

This file is automatically generated from updateUsageDocs.js script. In package.json in line 158-161 lines the following steps has been executed in order to run this script successfully -

* generate:readme:create: It creates the initial content for the README file by printing the usage and commands tags using printf and redirects the output to scripts/README.md file.
* generate:readme:commands: It changes the directory to the scripts folder and executes the oclif readme command. This command generates the usage and commands sections based on the CLI commands and updates the content in the scripts/README.md file.
* generate:assets: This script combines the two previously mentioned scripts (generate:readme:toc and generate:commands) to generate the necessary assets, such as the README file and usage documentation.
* generate:commands: This script executes the following steps:
- Runs the generate:readme:create script to create the initial content for the README file.
- Executes the generate:readme:commands script to generate the usage and commands sections based on the CLI commands.
- Runs the updateUsageDocs.js script using Node.js to update the usage documentation file with the contents of the generated README file.
- Deletes the scripts/README.md file using the rimraf command.

-->

The Modelina CLI makes it easier to generate AsyncAPI Models.
`;

// Define an async function to write the header and the README contents to the usage documentation file
async function run() {
try {
await writeFile(USAGE_PATH, header);
const readmeContents = await readContents();
// Append the contents of the README file to the usage documentation file
await writeFile(USAGE_PATH, readmeContents, { flag: 'a' });
} catch (e) {
console.error(e);
}
}

run();

async function readContents() {
let readmeContents;
let commandsContent = '';

while (commandsContent.length === 0) {
readmeContents = await readFile(README_PATH, 'utf8');

// Check if the content between <!-- commands --> and <!-- commandsstop --> is empty
const commandsStartText = '<!-- commands -->';
const commandStartIndex = readmeContents.indexOf(commandsStartText);
const commandStopIndex = readmeContents.indexOf('<!-- commandsstop -->');
//cutting the content between the above mentioned tags, removing white spaces and checking if there is some text as it will mean text was added by oclif
commandsContent = readmeContents.slice(commandStartIndex + commandsStartText.length, commandStopIndex).trim();

if (commandsContent.length === 0) {
console.log('No content between <!-- commands --> and <!-- commandsstop -->. Trying again...');
} else {
console.log('Content found!');
}

await delay(3000); // 3-second delay
}

return readmeContents;
}

function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
14 changes: 14 additions & 0 deletions modelina-cli/src/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Command } from '@oclif/core';

export default abstract class ModelinaCommand extends Command {
async catch(err: Error & { exitCode?: number; }): Promise<any> {
try {
return await super.catch(err);
} catch (error: any) {
if (error instanceof Error) {
this.logToStderr(`${error.name}: ${error.message}`);
process.exitCode = 1;
}
}
}
}
10 changes: 10 additions & 0 deletions modelina-cli/src/commands/generate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ModelinaCommand from '../../base';
import { Help } from '@oclif/core';

export default class Generate extends ModelinaCommand {
static description = 'Generate models using AsyncAPI Modelina.';
async run() {
const help = new Help(this.config);
help.showHelp(['generate', '--help']);
}
}
Loading
Loading