diff --git a/.github/workflows/deploy_action.yml b/.github/workflows/deploy_action.yml deleted file mode 100644 index 5225e41..0000000 --- a/.github/workflows/deploy_action.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Deploy to Server -# action trigger condition. You can read more from here -on: - push: # you can remove unused branch names - branches: - - "main" - - "master" - - "release/**" - - "production/**" - release: # triggers on all release events. Remove this if you're not using it - types: [published, created, edited] - -jobs: - # build the source code - build: - runs-on: ubuntu-latest - steps: - - uses: actions/setup-node@v2-beta - - uses: actions/checkout@v2 - with: - node-version: 12.x - - name: Build App - run: | - yarn - yarn build - # deploy the build - deploy: - needs: build - runs-on: ubuntu-latest - steps: - - name: Deploy to Server - run: echo "Please add the deploy behavior here" diff --git a/.github/workflows/publish_action.yml b/.github/workflows/publish_action.yml new file mode 100644 index 0000000..da89c05 --- /dev/null +++ b/.github/workflows/publish_action.yml @@ -0,0 +1,17 @@ +name: Publish to NPM +on: + release: + types: [created] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: "12.x" + registry-url: "https://registry.npmjs.org" + - run: yarn + - run: npm publish --access=public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..89e77f9 --- /dev/null +++ b/.npmignore @@ -0,0 +1,7 @@ +tests +scripts +src +img +.vscode +.github +*.md diff --git a/package.json b/package.json index 2270213..7e3438f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glitch-deploy-tool", - "version": "1.0.0", + "version": "0.0.1-alpha", "description": "CLI tool for deploying files to Glitch.com", "keywords": [ "glitch.com", @@ -20,20 +20,36 @@ "type": "git", "url": "https://github.com/hoonsubin/glitch-deploy-tool.git" }, - "main": "src/index.ts", + "files": [ + "lib" + ], + "os": [ + "darwin", + "linux" + ], + "module": "lib/index.js", + "bin": { + "glitch-deploy-tool": "lib/cli/index.js" + }, "scripts": { "start": "NODE_ENV=production ts-node -r dotenv/config src/index.ts", "dev": "NODE_ENV=development ts-node-dev -r dotenv/config src/index.ts", + "prepublishOnly": "yarn run test && yarn run lint && yarn run build", "build": "rimraf ./lib && tsc --project tsconfig.json", "lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix", "test": "NODE_ENV=test jest --setupFiles dotenv/config --verbose --coverage && eslint '*/**/*.{js,ts,tsx}'", - "cli": "NODE_ENV=development ts-node -r dotenv/config scripts/cli.ts" + "cli": "NODE_ENV=development ts-node -r dotenv/config src/cli/index.ts" }, "engines": { "node": ">=12.x" }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/teamstep" + }, "license": "MIT", "devDependencies": { + "@types/dashdash": "^1.14.0", "@types/fs-extra": "^9.0.2", "@types/jest": "^26.0.15", "@types/nock": "^11.1.0", @@ -56,6 +72,7 @@ "typescript": "^4.0.5" }, "dependencies": { + "dashdash": "^2.0.0", "fs-extra": "^9.0.1", "node": "^15.0.1", "node-fetch": "^2.6.1", diff --git a/scripts/cli.ts b/scripts/cli.ts deleted file mode 100644 index e9c491c..0000000 --- a/scripts/cli.ts +++ /dev/null @@ -1,97 +0,0 @@ -import simpleGit, { SimpleGitOptions } from 'simple-git'; -import path from 'path'; -import fs from 'fs-extra'; -import rimraf from 'rimraf'; -import * as Helpers from '../src/helpers'; -import { GlitchGit } from '../src/models'; - -const REPO_SOURCE = process.env.REPO_SOURCE; -// the folder to where it will copy things -const TARGET_FOLDER = 'target'; - -/** - * A script function used to test the deploy feature - */ -const pushToGlitch = async () => { - if (typeof REPO_SOURCE === 'undefined') { - throw new Error('target repository has not been provided'); - } - const ROOT_DIR = __dirname; - // the folder name where the repo will be cloned - // the name itself will not matter too much we only need its contents - const REPO_FOLDER = '.__source-repo'; - - const glitchRepoDir = path.join(ROOT_DIR, REPO_FOLDER); - const deployContentDir = TARGET_FOLDER ? path.join(ROOT_DIR, TARGET_FOLDER) : ROOT_DIR; - - const gitOptions: SimpleGitOptions = { - baseDir: ROOT_DIR, - binary: 'git', - maxConcurrentProcesses: 2, - }; - - // setup a git client - let git = simpleGit(gitOptions); - - if (!fs.existsSync(deployContentDir)) { - throw new Error(`target folder ${deployContentDir} does not exists`); - } - if (fs.existsSync(glitchRepoDir)) { - // remove the repo folder if it already exists - console.log('found an existing folder, removing it before cloning...'); - rimraf.sync(glitchRepoDir); - } - - console.log(`cloning repository...`); - // clone the repo to the folder - await git.clone(REPO_SOURCE, glitchRepoDir); - await git.pull('origin'); - - console.log(`changing working directory...`); - // change the directory to access git - await git.cwd(glitchRepoDir); - - console.log(`removing all contents in the glitch project...`); - // remove everything excluding the git metadata - Helpers.emptyFolderContent(glitchRepoDir, ['.git']); - - console.log(`copying new contents to the glitch project...`); - // move the new contents - Helpers.copyFolderContent(deployContentDir, glitchRepoDir, ['.git']); - - console.log('committing changes as [Auto commit] ' + Date.now()); - - // add everything in the working directory - await git.add('*'); - - // commit and push - const res = await git.commit('[Auto commit] ' + Date.now()); - console.log(res); - - //const pushRes = await git.push('origin', 'master'); - //console.log(pushRes); - - git = git.clearQueue(); - - // remove the local repo - rimraf.sync(glitchRepoDir); - console.log('successfully deployed to Glitch!'); -}; - -// script entry point -(async () => { - if (typeof REPO_SOURCE === 'undefined') { - throw new Error('target repository has not been provided'); - } - - //await pushToGlitch(); - - const glitchRepo = new GlitchGit(REPO_SOURCE, true); - - await glitchRepo.publishFilesToGlitch(path.join(__dirname, TARGET_FOLDER)); - - process.exit(0); -})().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/scripts/run.ts b/scripts/run.ts new file mode 100644 index 0000000..91f0442 --- /dev/null +++ b/scripts/run.ts @@ -0,0 +1,51 @@ +#!/usr/bin/env ts-node +//note: this cli script is for testing and being executed directly from source + +import path from 'path'; +import { GlitchGit, GlitchProject } from '../src/models'; + +const gitPushTest = async () => { + const source = process.env.REPO_SOURCE; + + if (typeof source === 'undefined') { + throw new Error('target repository has not been provided'); + } + + // the folder to where it will copy things + const targetFolder = 'target'; + + const glitchRepo = new GlitchGit(source, true); + + await glitchRepo.publishFilesToGlitch(path.join(__dirname, targetFolder)); +}; + +const gitImportTest = async () => { + const projId = process.env.GLITCH_PROJ_ID; + const token = process.env.GLITCH_USER_TOKEN; + + if (typeof projId === 'undefined' || typeof token === 'undefined') { + throw new Error('the required environment variables were not provided'); + } + + const sourceRepo = 'staketechnologies/lockdrop-ui'; + const glitchProj = new GlitchProject(token, projId); + + const res = await glitchProj.importFromGithub(sourceRepo, 'public'); + + if (res.status !== 200) { + throw new Error('Failed to import project'); + } + console.log('successfully imported project ' + sourceRepo); +}; + +// script entry point +(async () => { + throw new Error('Script not implemented!'); + //await gitPushTest(); + //await gitImportTest(); + + //process.exit(0); +})().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/src/app.ts b/src/app.ts deleted file mode 100644 index ba23059..0000000 --- a/src/app.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * the main entry function for running the server application. - * all application endpoint listener should be placed here. - */ -export default async function main() { - console.log('Hello world!'); -} diff --git a/src/cli/deploy.ts b/src/cli/deploy.ts new file mode 100644 index 0000000..c1b84f2 --- /dev/null +++ b/src/cli/deploy.ts @@ -0,0 +1,23 @@ +import { GlitchGit, GlitchProject } from '../models'; + +export const importFromFolder = async (repoUrl: string, targetPath?: string, debugMessage?: boolean) => { + const glitchRepo = new GlitchGit(repoUrl, debugMessage); + + await glitchRepo.publishFilesToGlitch(targetPath); + + console.log('successfully imported projects from ' + targetPath); +}; + +export const importFromGithub = async (token: string, projId: string, repo: string, path?: string) => { + if (typeof projId === 'undefined' || typeof token === 'undefined') { + throw new Error('the required environment variables were not provided'); + } + const glitchProj = new GlitchProject(token, projId); + + const res = await glitchProj.importFromGithub(repo, path); + + if (res.status !== 200) { + throw new Error('Failed to import project'); + } + console.log('successfully imported project ' + repo); +}; diff --git a/src/cli/index.ts b/src/cli/index.ts new file mode 100644 index 0000000..518de1f --- /dev/null +++ b/src/cli/index.ts @@ -0,0 +1,168 @@ +#!/usr/bin/env ts-node + +/** + * everything in the cli folder should be isolated from the reset of the code. + * the src/index.ts is meant to export the project classes that are used in this tool for developers. + * everything in the cli folder is meant for functions directly executed by the user via node. + */ +import dashdash, { ParserConfiguration } from 'dashdash'; +import * as Deployer from './deploy'; + +const defaultOptions: ParserConfiguration['options'] = [ + { + group: 'Tool Information', + }, + { + name: 'version', + type: 'bool', + help: 'Print tool version and exit.', + }, + { + names: ['help', 'h'], + type: 'bool', + help: 'Print this help and exit.', + }, +]; + +const commonOpts: ParserConfiguration['options'] = [ + { + names: ['path', 'p'], + type: 'string', + help: 'Folder to upload to Glitch', + completionType: 'file', + helpArg: 'PATH', + }, + + ...defaultOptions, +]; + +const uploadLocalOpts: ParserConfiguration['options'] = [ + { + group: 'Upload Local Folder Options', + }, + { + names: ['remote', 'r'], + type: 'string', + env: 'REPO_SOURCE', + help: "Glitch project repository's remote URL", + helpArg: '', + }, + { + names: ['verbose', 'v'], + type: 'bool', + help: 'Verbose output. This will log all debug messages.', + }, + ...commonOpts, +]; + +const importGhOpts: ParserConfiguration['options'] = [ + { + group: 'Import from Github Options', + }, + { + names: ['token', 't'], + type: 'string', + env: 'GLITCH_USER_TOKEN', + help: 'Glitch user API token used to access the account.', + helpArg: '', + }, + { + names: ['glitch-id', 'i'], + type: 'string', + env: 'GLITCH_PROJ_ID', + help: 'Glitch project ID that will', + helpArg: '', + }, + { + names: ['repo', 'r'], + type: 'string', + env: 'REPO_SOURCE', + help: 'Github repository name that will be imported to Glitch.', + helpArg: '', + }, + ...commonOpts, +]; + +interface SubCommand { + subcommand: string; + options: ParserConfiguration['options']; + help: string; +} + +const scriptArgs: SubCommand[] = [ + { + subcommand: 'github', + options: importGhOpts, + help: '', + }, + { + subcommand: 'from-local', + options: uploadLocalOpts, + help: '', + }, +]; + +const checkToolMode = () => { + const userArg = process.argv.slice(2)[0]; + + if (!userArg) { + return { + subcommand: '', + options: defaultOptions, + help: '', + }; + } + const command = scriptArgs.find((i) => i.subcommand === userArg); + if (typeof command === 'undefined') throw new Error('Invalid argument ' + userArg); + + return command; +}; + +(async () => { + const mode = checkToolMode(); + const parser = dashdash.createParser({ options: mode.options }); + const opts = parser.parse(process.argv); + console.log(opts._order); + // print the help message if the user passes the help flag or nothing + if (opts.help || opts._order.length === 0) { + const help = parser.help({ includeEnv: true }).trimRight(); + const currentCommand = mode.subcommand || '[from-local | github]'; + //const commandList = + console.log(`usage: glitch-deploy-tool ${currentCommand} [OPTIONS]\noptions:\n${help}\n`); + process.exit(0); + } + + // print the tool version + if (opts.version) { + //todo: dynamically parse the version + console.log(`glitch-deploy-tool version 1.0.0-alpha`); + process.exit(0); + } + + //todo: right now we are string comparing user sub-commands. This is very ugly. + // change this to implement an interface or some other dynamic way of executing commands + const toolType = opts._args[0]; + if (toolType === 'from-local') { + const sourceRepo = opts.remote; + const targetFolder = opts.path; + const showDebug = opts.verbose; + if (typeof sourceRepo !== 'string') { + throw new Error('Invalid repository'); + } + await Deployer.importFromFolder(sourceRepo, targetFolder, showDebug); + } else if (toolType === 'github') { + const glitchID = opts.glitch_id; + const glitchToken = opts.token; + const sourceRepo = opts.repo; + const targetFolder = opts.path; + if (typeof glitchID !== 'string') { + throw new Error('Invalid ID'); + } + await Deployer.importFromGithub(glitchToken, glitchID, sourceRepo, targetFolder); + } + + process.exit(0); +})().catch((err) => { + console.error('error: ' + err.message); + process.exit(1); +}); diff --git a/src/index.ts b/src/index.ts index 082afbf..e9644da 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1 @@ -import main from './app'; - -// the entry point for the server application -(() => { - main().catch((e) => { - console.log(e); - }); -})(); +export * from './models'; diff --git a/src/models/glitchGit.ts b/src/models/glitchGit.ts index 1438c9b..7d195fb 100644 --- a/src/models/glitchGit.ts +++ b/src/models/glitchGit.ts @@ -38,7 +38,7 @@ export default class GlitchRepo { let folderToCopy = ''; // if no folder name or path is given, import everything in the current directory - if (!targetFolder || targetFolder === '*') { + if (!targetFolder || targetFolder === '*' || targetFolder === '.') { folderToCopy = ROOT_DIR; } else { // if the folder name was given, check if it's in absolute path or not diff --git a/tsconfig.json b/tsconfig.json index 840da77..0fa2083 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,34 +3,20 @@ "module": "commonjs", "esModuleInterop": true, "target": "es2015", - "lib": [ - "es6" - ], + "lib": ["es6"], "moduleResolution": "node", "sourceMap": true, "outDir": "lib", "strict": true, - "rootDir": "src", + "rootDir": ".", "resolveJsonModule": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, - // we set this to false because of TypeORM entity decorators "strictPropertyInitialization": false, "removeComments": true, "declaration": true }, - "lib": [ - "es2015" - ], - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "build", - "lib", - "dist", - "scripts", - "tests" - ] -} \ No newline at end of file + "lib": ["es2015"], + "include": ["src/**/*"], + "exclude": ["node_modules", "build", "lib", "dist", "tests", "scripts"] +} diff --git a/yarn.lock b/yarn.lock index 7e4262a..7964e88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -560,6 +560,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/dashdash@^1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@types/dashdash/-/dashdash-1.14.0.tgz#bfa457c2688497cf0e6695dbd522c67a9232833f" + integrity sha512-dBnfu9H6TVawx85FGmVEs5lYFXNwUVxn3Nqu5FHhCAi4aPvZR35W4FEMK3ljlpM2vHPGgEnCZGARF59/QGTNJw== + "@types/fs-extra@^9.0.2": version "9.0.2" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.2.tgz#e1e1b578c48e8d08ae7fc36e552b94c6f4621609" @@ -635,9 +640,9 @@ form-data "^3.0.0" "@types/node@*", "@types/node@^14.14.2": - version "14.14.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.5.tgz#e92d3b8f76583efa26c1a63a21c9d3c1143daa29" - integrity sha512-H5Wn24s/ZOukBmDn03nnGTp18A60ny9AmCwnEcgJiTgSGsCO7k+NWP7zjCCbhlcnVCoI+co52dUAt9GMhOSULw== + version "14.14.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.6.tgz#146d3da57b3c636cc0d1769396ce1cfa8991147f" + integrity sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1117,9 +1122,9 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.1.0.tgz#27dc176173725fb0adf8a48b647f4d7871944d78" - integrity sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ== + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== capture-exit@^2.0.0: version "2.0.0" @@ -1326,6 +1331,13 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +dashdash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-2.0.0.tgz#4bd6f2bc0e89001523a68d804bf2a7da9f73088b" + integrity sha512-ElMoAPlrzmF4l0OscF5pPBZv8LhUJBnwh7rHKllUOrwabAr47R1aQIIwC53rc59ycCb7k5Sj1/es+A3Bep/x5w== + dependencies: + assert-plus "^1.0.0" + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -1667,9 +1679,9 @@ execa@^1.0.0: strip-eof "^1.0.0" execa@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" - integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== dependencies: cross-spawn "^7.0.0" get-stream "^5.0.0" @@ -1788,9 +1800,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastq@^1.6.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" - integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== + version "1.9.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.9.0.tgz#e16a72f338eaca48e91b5c23593bcc2ef66b7947" + integrity sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w== dependencies: reusify "^1.0.4" @@ -4484,9 +4496,9 @@ uuid@^8.3.0: integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== v8-compile-cache@^2.0.3: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" - integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" + integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== v8-to-istanbul@^6.0.1: version "6.0.1"