gitClient #11

Merged
a34sun merged 13 commits from gitClient into master 2023-04-05 17:33:11 -04:00
5 changed files with 259 additions and 35 deletions

120
package-lock.json generated
View File

@ -23,18 +23,19 @@
"pm2": "^5.2.2",
"rimraf": "^3.0.2",
"typeorm": "^0.3.11",
"typescript": "^4.8.3"
"typescript": "^4.8.3",
"uuidv4": "^6.2.13"
},
"devDependencies": {
"@types/express": "^4.17.17",
"@types/jest": "^29.2.5",
"@types/jest": "^29.5.0",
"@types/multer": "^1.4.7",
"@types/node": "^18.7.16",
"@types/supertest": "^2.0.12",
"better-sqlite3": "^8.0.1",
"concurrently": "^7.4.0",
"cross-env": "^7.0.3",
"jest": "^29.3.1",
"jest": "^29.5.0",
"nodemon": "^2.0.19",
"supertest": "^6.3.3"
}
@ -1054,6 +1055,15 @@
"node": ">=6.0"
}
},
"node_modules/@opencensus/core/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/@opencensus/propagation-b3": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz",
@ -1081,6 +1091,15 @@
"node": ">=6.0"
}
},
"node_modules/@opencensus/propagation-b3/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/@pm2/agent": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.1.tgz",
@ -1461,9 +1480,9 @@
}
},
"node_modules/@types/jest": {
"version": "29.4.0",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz",
"integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==",
"version": "29.5.0",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz",
"integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==",
"dev": true,
"dependencies": {
"expect": "^29.0.0",
@ -1544,6 +1563,11 @@
"@types/superagent": "*"
}
},
"node_modules/@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="
},
"node_modules/@types/yargs": {
"version": "17.0.22",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz",
@ -7254,14 +7278,6 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"node_modules/typeorm/node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
@ -7336,12 +7352,28 @@
}
},
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "bin/uuid"
"uuid": "dist/bin/uuid"
}
},
"node_modules/uuidv4": {
"version": "6.2.13",
"resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz",
"integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==",
"dependencies": {
"@types/uuid": "8.3.4",
"uuid": "8.3.2"
}
},
"node_modules/uuidv4/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-to-istanbul": {
@ -8387,6 +8419,13 @@
"semver": "^5.5.0",
"shimmer": "^1.2.0",
"uuid": "^3.2.1"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
"@opencensus/propagation-b3": {
@ -8409,6 +8448,11 @@
"shimmer": "^1.2.0",
"uuid": "^3.2.1"
}
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}
}
},
@ -8744,9 +8788,9 @@
}
},
"@types/jest": {
"version": "29.4.0",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz",
"integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==",
"version": "29.5.0",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz",
"integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==",
"dev": true,
"requires": {
"expect": "^29.0.0",
@ -8827,6 +8871,11 @@
"@types/superagent": "*"
}
},
"@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="
},
"@types/yargs": {
"version": "17.0.22",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz",
@ -13033,11 +13082,6 @@
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
}
}
},
@ -13083,9 +13127,25 @@
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
},
"uuidv4": {
"version": "6.2.13",
"resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz",
"integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==",
"requires": {
"@types/uuid": "8.3.4",
"uuid": "8.3.2"
},
"dependencies": {
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
}
}
},
"v8-to-istanbul": {
"version": "9.1.0",

View File

@ -9,7 +9,8 @@
"compile": "rimraf build && tsc",
"server": "node build/server.js",
"server:dev": "nodemon build/server.js",
"ts:watch": "tsc --watch"
"ts:watch": "tsc --watch",
"test": "tsc && jest build/test"
},
"keywords": [],
"author": "",
@ -29,18 +30,19 @@
"pm2": "^5.2.2",
"rimraf": "^3.0.2",
"typeorm": "^0.3.11",
"typescript": "^4.8.3"
"typescript": "^4.8.3",
"uuidv4": "^6.2.13"
},
"devDependencies": {
"@types/jest": "^29.5.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.2.5",
"@types/multer": "^1.4.7",
"@types/node": "^18.7.16",
"@types/supertest": "^2.0.12",
"better-sqlite3": "^8.0.1",
"concurrently": "^7.4.0",
"cross-env": "^7.0.3",
"jest": "^29.3.1",
"jest": "^29.5.0",
"nodemon": "^2.0.19",
"supertest": "^6.3.3"
}

98
src/gitClient.ts Normal file
View File

@ -0,0 +1,98 @@
import { execSync } from "child_process";
import { promises as fs } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import util from "util";
const exec = util.promisify(require("child_process").exec);
import { uuid } from "uuidv4";
function throwIfError(error) {
if (error != undefined) {
console.error(`error: ${error.message}`);
}
}
export async function cloneRepo(httpAddr: string) {
const tmpFolderPath = await fs.mkdtemp(join(tmpdir(), "dt-"));
console.log(`Cloning repo to directory: ${tmpFolderPath}`);
try {
const stdOut = execSync(
`git clone ${httpAddr} ${tmpFolderPath}`
).toString();
console.log(`Clone output is: ${stdOut}`);
} catch (error) {
console.error(
`Failed to clone repo: ${error.status} ${
error.message
} ${error.stderr.toString()} ${error.stdout.toString()}`
);
throw error.message;
}
return tmpFolderPath;
}
export async function pushRepo(repoPath: string) {
try{
const stdOut = execSync(`git -C ${repoPath} push`).toString();
console.log(`Push output is: ${stdOut}`);
} catch (error){
console.error(
`Failed to push to repo: ${error.status}
${error.message}
${error.stderr.toString()}
${error.stdout.toString()}`
);
throw error.message;
}
return repoPath;
}
export async function commitRepo(repoPath) {
try{
const stdOut = execSync(`git -C ${repoPath} add . && git -C ${repoPath} commit -m \'event commit\'`);
console.log(`Push output is: ${stdOut}`);
} catch (error){
console.error(
`Failed to push to repo: ${error.status}
${error.message}
${error.stderr.toString()}
${error.stdout.toString()}`
);
throw error.message;
}
return repoPath;
}
export async function createAndPublishNewBranch(repoPath) {
try{
var id = uuid();
execSync(
`git -C ${repoPath} checkout -b ${id} && git -C ${repoPath} push -u origin --set-upstream ${id}`
);
} catch (error){
console.error(
`Failed to push to repo: ${error.status}
${error.message}
${error.stderr.toString()}
${error.stdout.toString()}`
);
throw error.message;
}
}
export async function removeRepo(repoPath) {
try{
execSync(`rm -r ${repoPath}`);
} catch (error){
`Failed to push to repo: ${error.status}
Review

I think you forgot a console.error here

I think you forgot a console.error here
${error.message}
${error.stderr.toString()}
${error.stdout.toString()}`
throw error.message;
}
}

View File

@ -0,0 +1,64 @@
import {
cloneRepo,
commitRepo,
createAndPublishNewBranch,
pushRepo,
removeRepo,
} from "../gitClient";
import { existsSync, readdirSync } from "fs";
import { exec, execSync } from "child_process";
test("successfully clones repo", async () => {
const folderPath = await cloneRepo("https://github.com/octocat/Spoon-Knife");
expect(existsSync(folderPath)).toBe(true);
const directoryContents = readdirSync(folderPath);
expect(directoryContents).toContain("index.html");
expect(directoryContents).toContain("README.md");
});
test("successfully commits to repo", async () => {
const folderPath = await cloneRepo("https://github.com/octocat/Spoon-Knife");
snedadah marked this conversation as resolved
Review

nit: (not importnat) but just wanted to point out this test doesn't actully test that it was commited. Maybe if it ran a git add --all && git reset --hard after the commit, it would test that the commit actually worked.

nit: (not importnat) but just wanted to point out this test doesn't actully test that it was commited. Maybe if it ran a `git add --all && git reset --hard` after the commit, it would test that the commit actually worked.
let thrownError = undefined;
try {
execSync(`cd ${folderPath} && touch newFile.ts`);
await commitRepo(folderPath);
execSync("git reset --hard"); // hard reset to delete any uncommitted files
const directoryContents = readdirSync(folderPath);
expect(directoryContents).toContain("newFile.ts");
} catch (error) {
thrownError = error;
}
expect(thrownError).toBeUndefined();
});
test("successfully tries to pushes to repo", async () => {
const folderPath = await cloneRepo("https://github.com/octocat/Spoon-Knife"); // NOTE: user needs permission to push to repo in order for this to work
let thrownError = undefined;
try {
execSync(`cd ${folderPath} && touch newerFile.ts`);
await createAndPublishNewBranch(folderPath);
await commitRepo(folderPath);
await pushRepo(folderPath);
const directoryContents = readdirSync(folderPath);
expect(directoryContents).toContain("newerFile.ts");
} catch (error) {
thrownError = error;
}
// Should try to push to a repo (will get permission denied though)
expect(thrownError).toContain(
" Permission to octocat/Spoon-Knife.git denied to"
);
});
test("throws error if can't clone repo", async () => {
let thrownError = undefined;
try {
await cloneRepo("kdskdfj");
} catch (error) {
thrownError = error;
}
expect(thrownError).not.toBeUndefined();
});

View File

@ -8,7 +8,7 @@
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"outDir": "build",
"resolveJsonModule": true,
"resolveJsonModule": true
},
"include": ["src"]
}