diff --git a/README.md b/README.md index cbfe82c..2ac30a3 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,13 @@ - To start using cpm, follow the [getting started](docs/getting-started) guide. Get your projects organized and managed efficiently in no time! ## Commands -- [cpm](docs/core) +- [cpm plugin](docs/plugin) +- [cpm project](docs/project) +- [cpm flow](docs/flow) - [cpm template](docs/template) - [cpm repo](docs/repo) - [cpm task](docs/task) - [cpm pr](docs/pr) -- [cpm plugin](docs/plugin) -- [cpm flow](docs/flow) ## Workflow management To start using workflows, follow the [workflow](docs/workflow) guide. Streamline your processes effortlessly with automation—unlocking more time for what truly matters. diff --git a/docs/core/README.md b/docs/core/README.md deleted file mode 100644 index 944a220..0000000 --- a/docs/core/README.md +++ /dev/null @@ -1,41 +0,0 @@ -### Core commands - -#### init - -Initialize a cpm project. - -```bash -cpm init -``` - -#### list - -List projects. - -```bash -cpm list -``` - -#### goto - -Go to project folder. If there are multiple repositories available for given query this will prompt user to select. - -```bash -cpm goto -``` - -**Arguments:** -- `query`: Pattern to search. Example: `cpm find Cloudimpl-Inc/cpm` - -**Outputs:** -- `org`: Organization name extracted from URL. -- `repo`: Repository name extracted from URL. -- `path`: Locally cloned directory. - -#### sync - -Sync project (sync plugins). - -```bash -cpm sync -``` \ No newline at end of file diff --git a/docs/project/README.md b/docs/project/README.md new file mode 100644 index 0000000..be84544 --- /dev/null +++ b/docs/project/README.md @@ -0,0 +1,57 @@ +### Core commands + +#### project init + +Initialize a cpm project. + +```bash +cpm project init +``` + +#### project list + +List projects. + +```bash +cpm project list +``` + +#### project clone + +Clone a repository to the root directory configured in ctx.config.rootDir. + +```bash +cpm repo clone +``` + +**Arguments:** +- `url`: URL of the git repository. + +**Outputs:** +- `org`: Organization name extracted from URL. +- `repo`: Repository name extracted from URL. +- `path`: Locally cloned directory. + +#### project goto + +Go to project folder. If there are multiple repositories available for given query this will prompt user to select. + +```bash +cpm project goto +``` + +**Arguments:** +- `query`: Pattern to search. Example: `cpm find Cloudimpl-Inc/cpm` + +**Outputs:** +- `org`: Organization name extracted from URL. +- `repo`: Repository name extracted from URL. +- `path`: Locally cloned directory. + +#### project sync + +Sync project. + +```bash +cpm prtoject sync +``` \ No newline at end of file diff --git a/docs/repo/README.md b/docs/repo/README.md index 76f35f3..fc65de4 100644 --- a/docs/repo/README.md +++ b/docs/repo/README.md @@ -2,19 +2,17 @@ #### repo clone -Clone a git repository to the root directory configured in ctx.config.rootDir. +Clone a repository. ```bash cpm repo clone ``` **Arguments:** -- `url`: URL of the git repository. +- `url`: URL of the repository. -**Outputs:** -- `org`: Organization name extracted from URL. -- `repo`: Repository name extracted from URL. -- `path`: Locally cloned directory. +**Options:** +- `-d, --destination `: Destination to clone (optional). #### repo checkout diff --git a/package-lock.json b/package-lock.json index 56adddd..e1130c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cloudimpl-inc/cpm", - "version": "2.32.0", + "version": "2.33.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cloudimpl-inc/cpm", - "version": "2.32.0", + "version": "2.33.0", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index 921ea9b..d1cea31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cloudimpl-inc/cpm", - "version": "2.32.0", + "version": "2.33.0", "description": "CloudImpl Project Manager", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/src/bin/cpm.sh b/src/bin/cpm.sh index 935bfd8..95414b0 100755 --- a/src/bin/cpm.sh +++ b/src/bin/cpm.sh @@ -1,14 +1,14 @@ #!/bin/bash -# Check if the first argument is 'goto' -if [ "$1" = "goto" ]; then - # If 'goto' command is provided, change directory - if [ $# -gt 1 ]; then +# Check if the first and second arguments are 'project' and 'goto' +if [ "$1" = "project" ] && [ "$2" = "goto" ]; then + # If 'project' and 'goto' commands are provided, continue with the rest of the script + if [ $# -gt 2 ]; then # Define the output file path using the CPM_OUTPUT environment variable output_file=$(mktemp) # Execute the cpmjs find command and redirect its output to the specified output file - env CPM_OUTPUT="$output_file" cpmjs goto "$2" || return + env CPM_OUTPUT="$output_file" cpmjs project goto "$3" || return # Read the output file and set environment variables while IFS='=' read -r key value || [ -n "$key" ]; do @@ -25,7 +25,7 @@ if [ "$1" = "goto" ]; then rm "$output_file" else # Show cpm error message - cpmjs goto + cpmjs project goto fi else # Run your Node.js CLI tool with arguments passed to the wrapper script diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 0e64471..ecbf3ce 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,4 +1,5 @@ import RootPlugin from "./root-plugin"; +import ProjectPlugin from "./project-plugin"; import FlowPlugin from "./flow-plugin"; import TemplatePlugin from "./template-plugin"; @@ -7,6 +8,10 @@ const plugins = [ name: 'inbuilt/root', creator: RootPlugin }, + { + name: 'inbuilt/project', + creator: ProjectPlugin + }, { name: 'inbuilt/flow', creator: FlowPlugin diff --git a/src/plugins/project-plugin.ts b/src/plugins/project-plugin.ts new file mode 100644 index 0000000..d22baf1 --- /dev/null +++ b/src/plugins/project-plugin.ts @@ -0,0 +1,131 @@ +import {Action, ActionOutput, CPMConfig, CPMContext, CPMPlugin} from "../index"; +import { + configFilePath, + createFile, + createFolder, executeShellCommand, + folderPath, getSelection, + gitIgnoreFilePath, + isProjectRepo, syncProject, + writeJson, + writeYaml +} from "../util"; +import chalk from "chalk"; +import {appendFileSync, readdirSync} from "fs"; +import path from "path"; +import fs from "fs"; + +const init: Action = async (ctx, input) => { + if (isProjectRepo) { + console.log(chalk.yellow('already initialized')); + } else { + const config: CPMConfig = { + plugins: [] + } + writeYaml(configFilePath, config); + + createFolder(folderPath); + writeJson(`${folderPath}/package.json`, {}); + + createFile(gitIgnoreFilePath, ''); + appendFileSync(gitIgnoreFilePath, + '\n# cpm\n' + + '.cpm/node_modules\n' + + '.cpm/_*\n', + ) + + console.log(chalk.green('cpm project initialized')); + } + return {}; +}; + +const list: Action = (ctx, input) => { + readdirSync(ctx.config.rootDir, {withFileTypes: true}) + .filter(orgDir => orgDir.isDirectory()) + .forEach(orgDir => { + console.log(`|--${orgDir.name}`) + readdirSync(`${orgDir.path}/${orgDir.name}`, {withFileTypes: true}) + .filter(repoDir => repoDir.isDirectory()) + .forEach(repoDir => console.log(`| |--${repoDir.name} => ${repoDir.path}/${repoDir.name}`)) + }) + + return {}; +}; + +const clone: Action = async (ctx, input): Promise => { + const {url} = input.args; + + if (!url.startsWith('http')) { + console.log(chalk.red('Only support http/https urls')); + return {}; + } + + const [org, repo] = url.split('/').slice(-2).map((segment: string) => segment.replace('.git', '')); + const repoDir = path.join(ctx.config.rootDir, org, repo); + + // Check if the repository is already cloned + if (fs.existsSync(repoDir)) { + console.log(chalk.yellow(`Repository already exists at ${repoDir}. Skipping clone step.`)); + return {org, repo, path: repoDir}; + } + + await executeShellCommand(`cpm repo clone ${url} ${repoDir}`); + return {org, repo, path: repoDir}; +} + +const goTo: Action = async (ctx, input) => { + const {query} = input.args; + const filtered: {id: string, name: string, org: string, repo: string, path: string}[] = []; + + for (const orgDir of readdirSync(ctx.config.rootDir, {withFileTypes: true})) { + if (!orgDir.isDirectory()) { + continue; + } + + const org = orgDir.name; + + for (const repoDir of readdirSync(`${orgDir.path}/${orgDir.name}`, {withFileTypes: true})) { + const repo = repoDir.name; + const repoNameFull = `${orgDir.name}/${repoDir.name}`; + const path = `${repoDir.path}/${repoDir.name}`; + + if (orgDir.isDirectory() && repoNameFull.toLowerCase().includes(query.toLowerCase())) { + filtered.push({id: path, name: `${org}/${repo}`, org, repo, path}); + } + } + } + + if (filtered.length === 0) { + console.log(chalk.red('repository not found')); + return {}; + } else if (filtered.length === 1) { + const result = filtered[0]; + console.log(chalk.green(result.path)); + return result; + } else { + const selection = await getSelection('Select repository:', filtered); + const result = filtered.find(f => f.id === selection)!; + console.log(chalk.green(result.path)); + return result; + } +}; + +const sync: Action = async (ctx, input) => { + await syncProject(ctx.config); + await executeShellCommand('cpm repo sync'); + return {}; +}; + +const projectPlugin: CPMPlugin = { + name: 'project', + actions: { + 'project init': init, + 'project list': list, + 'project clone': clone, + 'project goto': goTo, + 'project sync': sync + } +}; + +export default function createProjectPlugin(ctx: CPMContext): CPMPlugin { + return projectPlugin; +} \ No newline at end of file diff --git a/src/plugins/root-plugin.ts b/src/plugins/root-plugin.ts index 797f9dc..0417307 100644 --- a/src/plugins/root-plugin.ts +++ b/src/plugins/root-plugin.ts @@ -1,105 +1,14 @@ -import {Action, CPMConfig, CPMPluginCreator} from "../index"; -import {appendFileSync, readdirSync} from "fs"; +import {Action, CPMPluginCreator} from "../index"; import { - computeIfNotExist, - configFilePath, - createFile, - createFolder, + computeIfNotExist, configFilePath, executeShellCommand, folderPath, - gitIgnoreFilePath, globalConfigFilePath, globalFolderPath, - isProjectRepo, - readJson, - readYaml, - secretsFilePath, - syncProject, - variablesFilePath, - writeJson, - writeYaml, - getSelection + isProjectRepo, readJson, + readYaml, secretsFilePath, variablesFilePath, writeJson, writeYaml } from "../util"; -import chalk from 'chalk'; - -const init: Action = async (ctx, input) => { - if (isProjectRepo) { - console.log(chalk.yellow('already initialized')); - } else { - const config: CPMConfig = { - plugins: [] - } - writeYaml(configFilePath, config); - - createFolder(folderPath); - writeJson(`${folderPath}/package.json`, {}); - - createFile(gitIgnoreFilePath, ''); - appendFileSync(gitIgnoreFilePath, - '\n# cpm\n' + - '.cpm/node_modules\n' + - '.cpm/_*\n', - ) - - console.log(chalk.green('cpm project initialized')); - } - return {}; -}; - -const goTo: Action = async (ctx, input) => { - const {query} = input.args; - const filtered: {id: string, name: string, org: string, repo: string, path: string}[] = []; - - for (const orgDir of readdirSync(ctx.config.rootDir, {withFileTypes: true})) { - if (!orgDir.isDirectory()) { - continue; - } - - const org = orgDir.name; - - for (const repoDir of readdirSync(`${orgDir.path}/${orgDir.name}`, {withFileTypes: true})) { - const repo = repoDir.name; - const repoNameFull = `${orgDir.name}/${repoDir.name}`; - const path = `${repoDir.path}/${repoDir.name}`; - - if (orgDir.isDirectory() && repoNameFull.toLowerCase().includes(query.toLowerCase())) { - filtered.push({id: path, name: `${org}/${repo}`, org, repo, path}); - } - } - } - - if (filtered.length === 0) { - console.log(chalk.red('repository not found')); - return {}; - } else if (filtered.length === 1) { - const result = filtered[0]; - console.log(chalk.green(result.path)); - return result; - } else { - const selection = await getSelection('Select repository:', filtered); - const result = filtered.find(f => f.id === selection)!; - console.log(chalk.green(result.path)); - return result; - } -}; - -const list: Action = (ctx, input) => { - readdirSync(ctx.config.rootDir, {withFileTypes: true}) - .filter(orgDir => orgDir.isDirectory()) - .forEach(orgDir => { - console.log(`|--${orgDir.name}`) - readdirSync(`${orgDir.path}/${orgDir.name}`, {withFileTypes: true}) - .filter(repoDir => repoDir.isDirectory()) - .forEach(repoDir => console.log(`| |--${repoDir.name} => ${repoDir.path}/${repoDir.name}`)) - }) - - return {}; -}; - -const sync: Action = async (ctx, input) => { - await syncProject(ctx.config); - return {}; -}; +import chalk from "chalk"; const pluginList: Action = async (ctx, input) => { if (input.options.global) { @@ -234,10 +143,6 @@ const pluginInit: CPMPluginCreator = ctx => { return { name: "root", actions: { - "init": init, - "goto": goTo, - "list": list, - "sync": sync, "plugin list": pluginList, "plugin add": pluginAdd, "plugin remove": pluginRemove, diff --git a/src/resources/commands.yml b/src/resources/commands.yml index 3374cb8..b2bd939 100644 --- a/src/resources/commands.yml +++ b/src/resources/commands.yml @@ -1,10 +1,70 @@ -init: +plugin list: + description: "list plugin versions" + options: + global: + shortName: "g" + description: "list global plugin versions" + +plugin add: + description: "install cpm plugin" + arguments: + plugin: + description: "plugin name" + options: + global: + shortName: "g" + description: "install plugin globally" + +plugin remove: + description: "uninstall cpm plugin" + arguments: + plugin: + description: "plugin name" + options: + global: + shortName: "g" + description: "uninstall global plugin" + +plugin purge: + description: "uninstall cpm plugin and remove all configuration" + arguments: + plugin: + description: "plugin name" + options: + global: + shortName: "g" + description: "purge global plugin" + +plugin configure: + description: "configure cpm plugin" + arguments: + plugin: + description: "plugin name" + options: + global: + shortName: "g" + description: "install plugin globally" + +project init: description: "initialized cpm project" -list: - description: "list projects" +project list: + description: "list projects for current folder" + +project clone: + description: "clone cpm project" + arguments: + url: + description: "url of the repository" + outputs: + org: + description: "organization name extracted from url" + repo: + description: "repo name extracted from url" + path: + description: "locally cloned directory" -goto: +project goto: description: "go to project folder" arguments: query: @@ -17,8 +77,27 @@ goto: path: description: "locally cloned directory" -sync: - description: "sync project (sync plugins)" +project sync: + description: "sync project" + +flow enable: + description: "enable cpm flow" + +flow configure: + description: "configure cpm flow" + +flow setup: + description: "make repository ready to use cpm flow (every new clone should run this)" + +flow checkout: + description: "checkout issue to start development" + options: + taskId: + shortName: "t" + description: "task id to checkout" + +flow submit: + description: "create pr for issue for current working issue" template fill: description: "read files and prompt user to enter value for placeholder and fill" @@ -87,13 +166,11 @@ repo clone: arguments: url: description: "url of the git repository" - outputs: - org: - description: "organization name extracted from url" - repo: - description: "repo name extracted from url" - path: - description: "locally cloned directory" + options: + destination: + shortName: "d" + description: "destination to clone repository" + valueRequired: true repo checkout: description: "checkout branch inside git repository. if branch not exist create branch from default branch and update default branch from upstream before creating new branch" @@ -121,69 +198,3 @@ pr create: description: "head branch name" base: description: "base branch name" - -plugin list: - description: "list plugin versions" - options: - global: - shortName: "g" - description: "list global plugin versions" - -plugin add: - description: "install cpm plugin" - arguments: - plugin: - description: "plugin name" - options: - global: - shortName: "g" - description: "install plugin globally" - -plugin remove: - description: "uninstall cpm plugin" - arguments: - plugin: - description: "plugin name" - options: - global: - shortName: "g" - description: "uninstall global plugin" - -plugin purge: - description: "uninstall cpm plugin and remove all configuration" - arguments: - plugin: - description: "plugin name" - options: - global: - shortName: "g" - description: "purge global plugin" - -plugin configure: - description: "configure cpm plugin" - arguments: - plugin: - description: "plugin name" - options: - global: - shortName: "g" - description: "install plugin globally" - -flow enable: - description: "enable cpm flow" - -flow configure: - description: "configure cpm flow" - -flow setup: - description: "make repository ready to use cpm flow (every new clone should run this)" - -flow checkout: - description: "checkout issue to start development" - options: - taskId: - shortName: "t" - description: "task id to checkout" - -flow submit: - description: "create pr for issue for current working issue"