diff --git a/package.json b/package.json index e21a05d..68852fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "glitch-deploy-tool", - "version": "0.1.4-alpha", + "version": "0.1.5-alpha", "description": "CLI tool for deploying files to Glitch.com", "keywords": [ "glitch.com", @@ -53,6 +53,7 @@ "@types/dashdash": "^1.14.0", "@types/fs-extra": "^9.0.2", "@types/jest": "^26.0.15", + "@types/lodash": "^4.14.165", "@types/nock": "^11.1.0", "@types/node": "^14.14.2", "@types/node-fetch": "^2.5.7", @@ -75,6 +76,7 @@ "dependencies": { "dashdash": "^2.0.0", "fs-extra": "^9.0.1", + "lodash": "^4.17.20", "node": "^15.0.1", "node-fetch": "^2.6.1", "query-string": "^6.13.6", diff --git a/scripts/run.ts b/scripts/run.ts index 91f0442..fe756ae 100644 --- a/scripts/run.ts +++ b/scripts/run.ts @@ -2,49 +2,45 @@ //note: this cli script is for testing and being executed directly from source import path from 'path'; -import { GlitchGit, GlitchProject } from '../src/models'; +import { GlitchGit } from '../src/models'; -const gitPushTest = async () => { +const gitConfigTest = 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'; + // the folder that contains the deploy files + const targetFolder = 'dist'; - const glitchRepo = new GlitchGit(source, true); + const glitchRepo = new GlitchGit(source, true, { name: 'Hello World', email: 'info@deploy.me' }); - await glitchRepo.publishFilesToGlitch(path.join(__dirname, targetFolder)); -}; - -const gitImportTest = async () => { - const projId = process.env.GLITCH_PROJ_ID; - const token = process.env.GLITCH_USER_TOKEN; + const gitConfigList = await glitchRepo.git.listConfig(); + const config = { + name: gitConfigList.all['user.name'], + email: gitConfigList.all['user.email'], + }; - if (typeof projId === 'undefined' || typeof token === 'undefined') { - throw new Error('the required environment variables were not provided'); - } + // obtain the first entry of the user info if there are multiple values + const user = { + name: Array.isArray(config.name) ? config.name[0] : config.name, + email: Array.isArray(config.email) ? config.email[0] : config.email, + }; - const sourceRepo = 'staketechnologies/lockdrop-ui'; - const glitchProj = new GlitchProject(token, projId); + const author = user.email.startsWith('none') || user.name.startsWith('none') ? null : user; - const res = await glitchProj.importFromGithub(sourceRepo, 'public'); + console.log(author); - if (res.status !== 200) { - throw new Error('Failed to import project'); - } - console.log('successfully imported project ' + sourceRepo); + await glitchRepo.publishFilesToGlitch(targetFolder); }; // script entry point (async () => { throw new Error('Script not implemented!'); - //await gitPushTest(); - //await gitImportTest(); + //await gitConfigTest(); - //process.exit(0); + process.exit(0); })().catch((err) => { console.error(err); process.exit(1); diff --git a/src/models/glitchGit.ts b/src/models/glitchGit.ts index 6e962b9..1f69b49 100644 --- a/src/models/glitchGit.ts +++ b/src/models/glitchGit.ts @@ -3,25 +3,38 @@ import path from 'path'; import fs from 'fs-extra'; import rimraf from 'rimraf'; import * as Helpers from '../helpers'; +import _ from 'lodash'; + +export interface CommitAuthor { + name: string; + email: string; +} const REPO_FOLDER = '.__source-repo'; const ROOT_DIR = process.cwd(); +const DEFAULT_AUTHOR: CommitAuthor = { + name: 'Glitch Deploy Tool', + email: 'glitch@users.noreply.deploy.com', +}; export default class GlitchRepo { - private _gitUrl: string; + private _remoteOrigin: string; + private _gitInst: SimpleGit; private _glitchRepoDir: string | undefined; private _logMsg: boolean; - constructor(gitUrl: string, logMessage = false) { - if (!gitUrl.startsWith('https://')) { + private _authorInfo: CommitAuthor; + + constructor(remoteOrigin: string, logMessage = false, commitAs?: CommitAuthor) { + if (!remoteOrigin.startsWith('https://')) { // add the https prefix if the user did not provide one - this._gitUrl = 'https://' + gitUrl; + this._remoteOrigin = 'https://' + remoteOrigin; } else { - this._gitUrl = gitUrl; + this._remoteOrigin = remoteOrigin; } const gitOptions: SimpleGitOptions = { baseDir: ROOT_DIR, @@ -32,42 +45,52 @@ export default class GlitchRepo { this._logMsg = logMessage; // setup a git client this._gitInst = simpleGit(gitOptions); + + this._authorInfo = commitAs || DEFAULT_AUTHOR; } - public async publishFilesToGlitch(targetFolder?: string) { - let folderToCopy = ''; + public get git() { + return this._gitInst; + } - // if no folder name or path is given, import everything in the current directory - if (!targetFolder || targetFolder === '*' || targetFolder === '.') { - folderToCopy = ROOT_DIR; - } else { - // if the folder name was given, check if it's in absolute path or not - folderToCopy = targetFolder.startsWith('/') ? targetFolder : path.join(ROOT_DIR, targetFolder); - if (!fs.existsSync(folderToCopy)) { - throw new Error(`target folder ${folderToCopy} does not exists`); + public async publishFilesToGlitch(targetFolder?: string) { + try { + let folderToCopy = ''; + + // if no folder name or path is given, import everything in the current directory + if (!targetFolder || targetFolder === '*' || targetFolder === '.') { + folderToCopy = ROOT_DIR; + } else { + // if the folder name was given, check if it's in absolute path or not + folderToCopy = targetFolder.startsWith('/') ? targetFolder : path.join(ROOT_DIR, targetFolder); + if (!fs.existsSync(folderToCopy)) { + throw new Error(`target folder ${folderToCopy} does not exists`); + } } - } - await this._cloneRepo(); - - if (!this._glitchRepoDir) { - throw new Error('Glitch project is not cloned to the local machine yet'); - } + await this._cloneRepo(); - this._replaceRepoContentWith(folderToCopy); + if (!this._glitchRepoDir) { + throw new Error('Glitch project is not cloned to the local machine yet'); + } - await this._pushChangesToRemote(); + this._replaceRepoContentWith(folderToCopy); - this._writeLog('cleaning up...'); - this.cleanGitInstance(); + await this._pushChangesToRemote(); - this._writeLog('done'); + this._writeLog('successfully deployed to Glitch!'); + } catch (e) { + console.error(e); + } finally { + this.cleanGitInstance(); + } } public cleanGitInstance() { if (!this._glitchRepoDir) { throw new Error('Glitch project is not cloned to the local machine yet'); } + this._writeLog('cleaning up the local repo...'); this._gitInst = this._gitInst.clearQueue(); // remove the local repo @@ -86,7 +109,7 @@ export default class GlitchRepo { this._writeLog('cloning repository...'); // clone the glitch project repo to a folder - await this._gitInst.clone(this._gitUrl, REPO_FOLDER); + await this._gitInst.clone(this._remoteOrigin, REPO_FOLDER); // set the location this._glitchRepoDir = repoDir; @@ -100,7 +123,7 @@ export default class GlitchRepo { throw new Error('Glitch project is not cloned to the local machine yet'); } - this._writeLog('removing everything inside the Glitch repository'); + this._writeLog('removing everything inside the cloned repository'); // remove everything excluding the git metadata Helpers.emptyFolderContent(this._glitchRepoDir, ['.git']); @@ -108,20 +131,46 @@ export default class GlitchRepo { // if the source folder is an absolute directory, don't append the path const folderToCopy = sourceFolder.startsWith('/') ? sourceFolder : path.join(ROOT_DIR, sourceFolder); - this._writeLog(`cloning everything inside ${folderToCopy} to Glitch`); + this._writeLog(`copying everything inside ${folderToCopy} to the local repo`); // move the new contents to Glitch Helpers.copyFolderContent(folderToCopy, this._glitchRepoDir, ['.git', REPO_FOLDER]); } + private async _getCurrentAuthor() { + const gitConfigList = await this._gitInst.listConfig(); + const config = { + name: gitConfigList.all['user.name'], + email: gitConfigList.all['user.email'], + }; + + // obtain the first entry of the user info if there are multiple values + const user: CommitAuthor = { + name: Array.isArray(config.name) ? config.name[0] : config.name, + email: Array.isArray(config.email) ? config.email[0] : config.email, + }; + return user.email.startsWith('none') || user.name.startsWith('none') ? null : user; + } + private async _pushChangesToRemote() { - this._writeLog('committing all changes to Glitch...'); // add everything in the working directory - await this._gitInst.add('*'); + await this._gitInst.add('./*'); + + const localAuth = await this._getCurrentAuthor(); + + // use the local author information if there is a local author and the user did not provide any author info during instantiation + const useLocalAuthor = localAuth && _.isEqual(this._authorInfo, DEFAULT_AUTHOR); - // commit all changes to git - await this._gitInst.commit('[Auto commit] ' + Date.now()); + // we can do a non-null assertion because the above state must be true for it to pass + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { name, email } = useLocalAuthor ? localAuth! : this._authorInfo; - this._writeLog('pushing folder to Glitch...'); + // commit all changes added above to git and author it with the provided information + const commitRes = await this._gitInst.commit(`[Auto commit] ${Date.now()}`, undefined, { + '--author': `"${name} <${email}>"`, + }); + + this._writeLog('committing the following changes to Glitch...'); + this._writeLog(commitRes.summary); // pull before pushing await this._gitInst.pull('origin', 'master'); @@ -134,6 +183,13 @@ export default class GlitchRepo { * @param message message to write */ private _writeLog(message?: T) { - if (this._logMsg) console.log(message); + if (this._logMsg) { + // set the simple-git debug output variable. Refer to this for the details: + if (process.env.DEBUG !== 'simple-git:task:*') { + process.env.DEBUG = 'simple-git:task:*'; + } + // output custom logs + console.debug(message); + } } } diff --git a/yarn.lock b/yarn.lock index 7964e88..0d37fd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -619,6 +619,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== +"@types/lodash@^4.14.165": + version "4.14.165" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.165.tgz#74d55d947452e2de0742bad65270433b63a8c30f" + integrity sha512-tjSSOTHhI5mCHTy/OOXYIhi2Wt1qcbHmuXD1Ha7q70CgI/I71afO4XtLb/cVexki1oVYchpul/TOuu3Arcdxrg== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -3007,7 +3012,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==