@ehubbell/gitty
Version:
A simple CLI that will fetch, store, and clone Github repos.
1,398 lines (1,397 loc) • 48.4 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
const version = "0.3.7";
const Fs$1 = require("fs-extra");
const checkOrCreateFile = async (pathName, fileName) => {
const fileExists = await checkPath(`${pathName}/${fileName}`);
return fileExists ? fileExists : await createFile(pathName, fileName);
};
const createFile = async (pathName, fileName) => {
await createPath(pathName);
return await Fs$1.promises.writeFile(`${pathName}/${fileName}`, "");
};
const readFile = async (filePath, encoding = "utf8") => {
return await Fs$1.promises.readFile(filePath, encoding);
};
const checkPath = async (pathName) => {
return await Fs$1.promises.stat(pathName).then(() => true).catch(() => false);
};
const createPath = async (pathName) => {
return await Fs$1.mkdirSync(pathName, { recursive: true });
};
const checkOrCreatePath = async (pathName) => {
const pathExists = await checkPath(pathName);
return pathExists ? pathExists : await createPath(pathName);
};
const fileStats = async (filePath) => {
return await Fs$1.promises.stat(filePath);
};
const removePath = async (pathName) => {
const pathExists = await checkPath(pathName);
if (pathExists)
await Fs$1.rm(pathName, { recursive: true });
};
const writeFile = async (filePath, content) => {
return await Fs$1.writeFile(filePath, content);
};
class ConfigService {
constructor(props) {
this.basePath = props.basePath;
}
/* ----- Methods ----- */
async setup() {
const fragments = this.basePath.split("/");
const path2 = fragments.filter((v, i) => i < fragments.length - 1).join("/");
const file = fragments[fragments.length - 1];
return await checkOrCreateFile(path2, file);
}
async checkEmpty() {
const pathExists = await checkPath(this.basePath);
if (pathExists) {
const path2 = await fileStats(this.basePath);
if (path2.isDirectory())
return false;
return true;
}
return false;
}
async readContents() {
const contents = await readFile(this.basePath);
const records = contents.split("\n");
const formattedRecords = {};
records.filter((v) => v.length > 0).map((record) => {
const key = record.split("=")[0];
const value = record.split("=")[1];
return formattedRecords[key] = value;
});
return formattedRecords;
}
}
const { simpleGit, GitConfigScope, ResetMode } = require("simple-git");
class GitService {
constructor(props) {
this.basePath = props.basePath;
this.token = props.token;
this.addConfig("user.name", "playbooks");
this.addConfig("user.email", "admin@playbooks.xyz");
}
/* ----- Computed ----- */
get client() {
return simpleGit({
baseDir: this.basePath,
binary: "git",
maxConcurrentProcesses: 6
});
}
/* ----- Helpers ----- */
addConfig(key, value) {
return this.client.addConfig(key, value, false, GitConfigScope.local);
}
/* ----- Methods ----- */
async create(ownerId, repoId) {
return await this.client.init().add(".").commit("Transfer clone").addRemote("origin", `https://${this.token}@github.com/${ownerId}/${repoId}.git`).branch(["-M", "main"]).push(["-u", "origin", "main"]);
}
async clone(remoteUrl, localPath) {
return await this.client.clone(remoteUrl, localPath);
}
async fetch(options = {}) {
await this.client.fetch(options);
}
async pull(options = {}) {
await this.client.pull(options);
}
async push(options = {}) {
await this.client.push(options);
}
async remove(branch = "origin/main") {
await this.client.removeRemote(branch);
}
async resetRepo(branch = "origin/main") {
await this.client.reset(ResetMode.HARD, { branch: null });
}
}
function getUserAgent() {
if (typeof navigator === "object" && "userAgent" in navigator) {
return navigator.userAgent;
}
if (typeof process === "object" && process.version !== void 0) {
return `Node.js/${process.version.substr(1)} (${process.platform}; ${process.arch})`;
}
return "<environment undetectable>";
}
function register(state, name, method, options) {
if (typeof method !== "function") {
throw new Error("method for before hook must be a function");
}
if (!options) {
options = {};
}
if (Array.isArray(name)) {
return name.reverse().reduce((callback, name2) => {
return register.bind(null, state, name2, callback, options);
}, method)();
}
return Promise.resolve().then(() => {
if (!state.registry[name]) {
return method(options);
}
return state.registry[name].reduce((method2, registered) => {
return registered.hook.bind(null, method2, options);
}, method)();
});
}
function addHook(state, kind, name, hook2) {
const orig = hook2;
if (!state.registry[name]) {
state.registry[name] = [];
}
if (kind === "before") {
hook2 = (method, options) => {
return Promise.resolve().then(orig.bind(null, options)).then(method.bind(null, options));
};
}
if (kind === "after") {
hook2 = (method, options) => {
let result;
return Promise.resolve().then(method.bind(null, options)).then((result_) => {
result = result_;
return orig(result, options);
}).then(() => {
return result;
});
};
}
if (kind === "error") {
hook2 = (method, options) => {
return Promise.resolve().then(method.bind(null, options)).catch((error2) => {
return orig(error2, options);
});
};
}
state.registry[name].push({
hook: hook2,
orig
});
}
function removeHook(state, name, method) {
if (!state.registry[name]) {
return;
}
const index = state.registry[name].map((registered) => {
return registered.orig;
}).indexOf(method);
if (index === -1) {
return;
}
state.registry[name].splice(index, 1);
}
const bind = Function.bind;
const bindable = bind.bind(bind);
function bindApi(hook2, state, name) {
const removeHookRef = bindable(removeHook, null).apply(
null,
name ? [state, name] : [state]
);
hook2.api = { remove: removeHookRef };
hook2.remove = removeHookRef;
["before", "error", "after", "wrap"].forEach((kind) => {
const args = name ? [state, kind, name] : [state, kind];
hook2[kind] = hook2.api[kind] = bindable(addHook, null).apply(null, args);
});
}
function Singular() {
const singularHookName = Symbol("Singular");
const singularHookState = {
registry: {}
};
const singularHook = register.bind(null, singularHookState, singularHookName);
bindApi(singularHook, singularHookState, singularHookName);
return singularHook;
}
function Collection() {
const state = {
registry: {}
};
const hook2 = register.bind(null, state);
bindApi(hook2, state);
return hook2;
}
const Hook = { Singular, Collection };
var VERSION$3 = "0.0.0-development";
var userAgent = `octokit-endpoint.js/${VERSION$3} ${getUserAgent()}`;
var DEFAULTS = {
method: "GET",
baseUrl: "https://api.github.com",
headers: {
accept: "application/vnd.github.v3+json",
"user-agent": userAgent
},
mediaType: {
format: ""
}
};
function lowercaseKeys(object) {
if (!object) {
return {};
}
return Object.keys(object).reduce((newObj, key) => {
newObj[key.toLowerCase()] = object[key];
return newObj;
}, {});
}
function isPlainObject$1(value) {
if (typeof value !== "object" || value === null)
return false;
if (Object.prototype.toString.call(value) !== "[object Object]")
return false;
const proto = Object.getPrototypeOf(value);
if (proto === null)
return true;
const Ctor = Object.prototype.hasOwnProperty.call(proto, "constructor") && proto.constructor;
return typeof Ctor === "function" && Ctor instanceof Ctor && Function.prototype.call(Ctor) === Function.prototype.call(value);
}
function mergeDeep(defaults, options) {
const result = Object.assign({}, defaults);
Object.keys(options).forEach((key) => {
if (isPlainObject$1(options[key])) {
if (!(key in defaults))
Object.assign(result, { [key]: options[key] });
else
result[key] = mergeDeep(defaults[key], options[key]);
} else {
Object.assign(result, { [key]: options[key] });
}
});
return result;
}
function removeUndefinedProperties(obj) {
for (const key in obj) {
if (obj[key] === void 0) {
delete obj[key];
}
}
return obj;
}
function merge(defaults, route, options) {
var _a;
if (typeof route === "string") {
let [method, url] = route.split(" ");
options = Object.assign(url ? { method, url } : { url: method }, options);
} else {
options = Object.assign({}, route);
}
options.headers = lowercaseKeys(options.headers);
removeUndefinedProperties(options);
removeUndefinedProperties(options.headers);
const mergedOptions = mergeDeep(defaults || {}, options);
if (options.url === "/graphql") {
if (defaults && ((_a = defaults.mediaType.previews) == null ? void 0 : _a.length)) {
mergedOptions.mediaType.previews = defaults.mediaType.previews.filter(
(preview) => !mergedOptions.mediaType.previews.includes(preview)
).concat(mergedOptions.mediaType.previews);
}
mergedOptions.mediaType.previews = (mergedOptions.mediaType.previews || []).map((preview) => preview.replace(/-preview/, ""));
}
return mergedOptions;
}
function addQueryParameters(url, parameters) {
const separator = /\?/.test(url) ? "&" : "?";
const names = Object.keys(parameters);
if (names.length === 0) {
return url;
}
return url + separator + names.map((name) => {
if (name === "q") {
return "q=" + parameters.q.split("+").map(encodeURIComponent).join("+");
}
return `${name}=${encodeURIComponent(parameters[name])}`;
}).join("&");
}
var urlVariableRegex = /\{[^}]+\}/g;
function removeNonChars(variableName) {
return variableName.replace(/^\W+|\W+$/g, "").split(/,/);
}
function extractUrlVariableNames(url) {
const matches = url.match(urlVariableRegex);
if (!matches) {
return [];
}
return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []);
}
function omit(object, keysToOmit) {
const result = { __proto__: null };
for (const key of Object.keys(object)) {
if (keysToOmit.indexOf(key) === -1) {
result[key] = object[key];
}
}
return result;
}
function encodeReserved(str) {
return str.split(/(%[0-9A-Fa-f]{2})/g).map(function(part) {
if (!/%[0-9A-Fa-f]/.test(part)) {
part = encodeURI(part).replace(/%5B/g, "[").replace(/%5D/g, "]");
}
return part;
}).join("");
}
function encodeUnreserved(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
return "%" + c.charCodeAt(0).toString(16).toUpperCase();
});
}
function encodeValue(operator, value, key) {
value = operator === "+" || operator === "#" ? encodeReserved(value) : encodeUnreserved(value);
if (key) {
return encodeUnreserved(key) + "=" + value;
} else {
return value;
}
}
function isDefined(value) {
return value !== void 0 && value !== null;
}
function isKeyOperator(operator) {
return operator === ";" || operator === "&" || operator === "?";
}
function getValues(context, operator, key, modifier) {
var value = context[key], result = [];
if (isDefined(value) && value !== "") {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
value = value.toString();
if (modifier && modifier !== "*") {
value = value.substring(0, parseInt(modifier, 10));
}
result.push(
encodeValue(operator, value, isKeyOperator(operator) ? key : "")
);
} else {
if (modifier === "*") {
if (Array.isArray(value)) {
value.filter(isDefined).forEach(function(value2) {
result.push(
encodeValue(operator, value2, isKeyOperator(operator) ? key : "")
);
});
} else {
Object.keys(value).forEach(function(k) {
if (isDefined(value[k])) {
result.push(encodeValue(operator, value[k], k));
}
});
}
} else {
const tmp = [];
if (Array.isArray(value)) {
value.filter(isDefined).forEach(function(value2) {
tmp.push(encodeValue(operator, value2));
});
} else {
Object.keys(value).forEach(function(k) {
if (isDefined(value[k])) {
tmp.push(encodeUnreserved(k));
tmp.push(encodeValue(operator, value[k].toString()));
}
});
}
if (isKeyOperator(operator)) {
result.push(encodeUnreserved(key) + "=" + tmp.join(","));
} else if (tmp.length !== 0) {
result.push(tmp.join(","));
}
}
}
} else {
if (operator === ";") {
if (isDefined(value)) {
result.push(encodeUnreserved(key));
}
} else if (value === "" && (operator === "&" || operator === "?")) {
result.push(encodeUnreserved(key) + "=");
} else if (value === "") {
result.push("");
}
}
return result;
}
function parseUrl(template) {
return {
expand: expand.bind(null, template)
};
}
function expand(template, context) {
var operators = ["+", "#", ".", "/", ";", "?", "&"];
template = template.replace(
/\{([^\{\}]+)\}|([^\{\}]+)/g,
function(_, expression, literal) {
if (expression) {
let operator = "";
const values = [];
if (operators.indexOf(expression.charAt(0)) !== -1) {
operator = expression.charAt(0);
expression = expression.substr(1);
}
expression.split(/,/g).forEach(function(variable) {
var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable);
values.push(getValues(context, operator, tmp[1], tmp[2] || tmp[3]));
});
if (operator && operator !== "+") {
var separator = ",";
if (operator === "?") {
separator = "&";
} else if (operator !== "#") {
separator = operator;
}
return (values.length !== 0 ? operator : "") + values.join(separator);
} else {
return values.join(",");
}
} else {
return encodeReserved(literal);
}
}
);
if (template === "/") {
return template;
} else {
return template.replace(/\/$/, "");
}
}
function parse(options) {
var _a;
let method = options.method.toUpperCase();
let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{$1}");
let headers = Object.assign({}, options.headers);
let body;
let parameters = omit(options, [
"method",
"baseUrl",
"url",
"headers",
"request",
"mediaType"
]);
const urlVariableNames = extractUrlVariableNames(url);
url = parseUrl(url).expand(parameters);
if (!/^http/.test(url)) {
url = options.baseUrl + url;
}
const omittedParameters = Object.keys(options).filter((option) => urlVariableNames.includes(option)).concat("baseUrl");
const remainingParameters = omit(parameters, omittedParameters);
const isBinaryRequest = /application\/octet-stream/i.test(headers.accept);
if (!isBinaryRequest) {
if (options.mediaType.format) {
headers.accept = headers.accept.split(/,/).map(
(format) => format.replace(
/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/,
`application/vnd$1$2.${options.mediaType.format}`
)
).join(",");
}
if (url.endsWith("/graphql")) {
if ((_a = options.mediaType.previews) == null ? void 0 : _a.length) {
const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || [];
headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map((preview) => {
const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json";
return `application/vnd.github.${preview}-preview${format}`;
}).join(",");
}
}
}
if (["GET", "HEAD"].includes(method)) {
url = addQueryParameters(url, remainingParameters);
} else {
if ("data" in remainingParameters) {
body = remainingParameters.data;
} else {
if (Object.keys(remainingParameters).length) {
body = remainingParameters;
}
}
}
if (!headers["content-type"] && typeof body !== "undefined") {
headers["content-type"] = "application/json; charset=utf-8";
}
if (["PATCH", "PUT"].includes(method) && typeof body === "undefined") {
body = "";
}
return Object.assign(
{ method, url, headers },
typeof body !== "undefined" ? { body } : null,
options.request ? { request: options.request } : null
);
}
function endpointWithDefaults(defaults, route, options) {
return parse(merge(defaults, route, options));
}
function withDefaults$2(oldDefaults, newDefaults) {
const DEFAULTS2 = merge(oldDefaults, newDefaults);
const endpoint2 = endpointWithDefaults.bind(null, DEFAULTS2);
return Object.assign(endpoint2, {
DEFAULTS: DEFAULTS2,
defaults: withDefaults$2.bind(null, DEFAULTS2),
merge: merge.bind(null, DEFAULTS2),
parse
});
}
var endpoint = withDefaults$2(null, DEFAULTS);
class RequestError extends Error {
constructor(message, statusCode, options) {
super(message);
__publicField(this, "name");
/**
* http status code
*/
__publicField(this, "status");
/**
* Request options that lead to the error.
*/
__publicField(this, "request");
/**
* Response object if a response was received
*/
__publicField(this, "response");
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
this.name = "HttpError";
this.status = statusCode;
if ("response" in options) {
this.response = options.response;
}
const requestCopy = Object.assign({}, options.request);
if (options.request.headers.authorization) {
requestCopy.headers = Object.assign({}, options.request.headers, {
authorization: options.request.headers.authorization.replace(
/ .*$/,
" [REDACTED]"
)
});
}
requestCopy.url = requestCopy.url.replace(/\bclient_secret=\w+/g, "client_secret=[REDACTED]").replace(/\baccess_token=\w+/g, "access_token=[REDACTED]");
this.request = requestCopy;
}
}
var VERSION$2 = "0.0.0-development";
function isPlainObject(value) {
if (typeof value !== "object" || value === null)
return false;
if (Object.prototype.toString.call(value) !== "[object Object]")
return false;
const proto = Object.getPrototypeOf(value);
if (proto === null)
return true;
const Ctor = Object.prototype.hasOwnProperty.call(proto, "constructor") && proto.constructor;
return typeof Ctor === "function" && Ctor instanceof Ctor && Function.prototype.call(Ctor) === Function.prototype.call(value);
}
function getBufferResponse(response) {
return response.arrayBuffer();
}
function fetchWrapper(requestOptions) {
var _a, _b, _c, _d;
const log2 = requestOptions.request && requestOptions.request.log ? requestOptions.request.log : console;
const parseSuccessResponseBody = ((_a = requestOptions.request) == null ? void 0 : _a.parseSuccessResponseBody) !== false;
if (isPlainObject(requestOptions.body) || Array.isArray(requestOptions.body)) {
requestOptions.body = JSON.stringify(requestOptions.body);
}
let headers = {};
let status;
let url;
let { fetch } = globalThis;
if ((_b = requestOptions.request) == null ? void 0 : _b.fetch) {
fetch = requestOptions.request.fetch;
}
if (!fetch) {
throw new Error(
"fetch is not set. Please pass a fetch implementation as new Octokit({ request: { fetch }}). Learn more at https://github.com/octokit/octokit.js/#fetch-missing"
);
}
return fetch(requestOptions.url, {
method: requestOptions.method,
body: requestOptions.body,
redirect: (_c = requestOptions.request) == null ? void 0 : _c.redirect,
// Header values must be `string`
headers: Object.fromEntries(
Object.entries(requestOptions.headers).map(([name, value]) => [
name,
String(value)
])
),
signal: (_d = requestOptions.request) == null ? void 0 : _d.signal,
// duplex must be set if request.body is ReadableStream or Async Iterables.
// See https://fetch.spec.whatwg.org/#dom-requestinit-duplex.
...requestOptions.body && { duplex: "half" }
}).then(async (response) => {
url = response.url;
status = response.status;
for (const keyAndValue of response.headers) {
headers[keyAndValue[0]] = keyAndValue[1];
}
if ("deprecation" in headers) {
const matches = headers.link && headers.link.match(/<([^>]+)>; rel="deprecation"/);
const deprecationLink = matches && matches.pop();
log2.warn(
`[@octokit/request] "${requestOptions.method} ${requestOptions.url}" is deprecated. It is scheduled to be removed on ${headers.sunset}${deprecationLink ? `. See ${deprecationLink}` : ""}`
);
}
if (status === 204 || status === 205) {
return;
}
if (requestOptions.method === "HEAD") {
if (status < 400) {
return;
}
throw new RequestError(response.statusText, status, {
response: {
url,
status,
headers,
data: void 0
},
request: requestOptions
});
}
if (status === 304) {
throw new RequestError("Not modified", status, {
response: {
url,
status,
headers,
data: await getResponseData(response)
},
request: requestOptions
});
}
if (status >= 400) {
const data = await getResponseData(response);
const error2 = new RequestError(toErrorMessage(data), status, {
response: {
url,
status,
headers,
data
},
request: requestOptions
});
throw error2;
}
return parseSuccessResponseBody ? await getResponseData(response) : response.body;
}).then((data) => {
return {
status,
url,
headers,
data
};
}).catch((error2) => {
if (error2 instanceof RequestError)
throw error2;
else if (error2.name === "AbortError")
throw error2;
let message = error2.message;
if (error2.name === "TypeError" && "cause" in error2) {
if (error2.cause instanceof Error) {
message = error2.cause.message;
} else if (typeof error2.cause === "string") {
message = error2.cause;
}
}
throw new RequestError(message, 500, {
request: requestOptions
});
});
}
async function getResponseData(response) {
const contentType = response.headers.get("content-type");
if (/application\/json/.test(contentType)) {
return response.json().catch(() => response.text()).catch(() => "");
}
if (!contentType || /^text\/|charset=utf-8$/.test(contentType)) {
return response.text();
}
return getBufferResponse(response);
}
function toErrorMessage(data) {
if (typeof data === "string")
return data;
let suffix;
if ("documentation_url" in data) {
suffix = ` - ${data.documentation_url}`;
} else {
suffix = "";
}
if ("message" in data) {
if (Array.isArray(data.errors)) {
return `${data.message}: ${data.errors.map(JSON.stringify).join(", ")}${suffix}`;
}
return `${data.message}${suffix}`;
}
return `Unknown error: ${JSON.stringify(data)}`;
}
function withDefaults$1(oldEndpoint, newDefaults) {
const endpoint2 = oldEndpoint.defaults(newDefaults);
const newApi = function(route, parameters) {
const endpointOptions = endpoint2.merge(route, parameters);
if (!endpointOptions.request || !endpointOptions.request.hook) {
return fetchWrapper(endpoint2.parse(endpointOptions));
}
const request2 = (route2, parameters2) => {
return fetchWrapper(
endpoint2.parse(endpoint2.merge(route2, parameters2))
);
};
Object.assign(request2, {
endpoint: endpoint2,
defaults: withDefaults$1.bind(null, endpoint2)
});
return endpointOptions.request.hook(request2, endpointOptions);
};
return Object.assign(newApi, {
endpoint: endpoint2,
defaults: withDefaults$1.bind(null, endpoint2)
});
}
var request = withDefaults$1(endpoint, {
headers: {
"user-agent": `octokit-request.js/${VERSION$2} ${getUserAgent()}`
}
});
var VERSION$1 = "0.0.0-development";
function _buildMessageForResponseErrors(data) {
return `Request failed due to following response errors:
` + data.errors.map((e) => ` - ${e.message}`).join("\n");
}
var GraphqlResponseError = class extends Error {
constructor(request2, headers, response) {
super(_buildMessageForResponseErrors(response));
__publicField(this, "name", "GraphqlResponseError");
__publicField(this, "errors");
__publicField(this, "data");
this.request = request2;
this.headers = headers;
this.response = response;
this.errors = response.errors;
this.data = response.data;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
}
};
var NON_VARIABLE_OPTIONS = [
"method",
"baseUrl",
"url",
"headers",
"request",
"query",
"mediaType"
];
var FORBIDDEN_VARIABLE_OPTIONS = ["query", "method", "url"];
var GHES_V3_SUFFIX_REGEX = /\/api\/v3\/?$/;
function graphql(request2, query, options) {
if (options) {
if (typeof query === "string" && "query" in options) {
return Promise.reject(
new Error(`[@octokit/graphql] "query" cannot be used as variable name`)
);
}
for (const key in options) {
if (!FORBIDDEN_VARIABLE_OPTIONS.includes(key))
continue;
return Promise.reject(
new Error(
`[@octokit/graphql] "${key}" cannot be used as variable name`
)
);
}
}
const parsedOptions = typeof query === "string" ? Object.assign({ query }, options) : query;
const requestOptions = Object.keys(
parsedOptions
).reduce((result, key) => {
if (NON_VARIABLE_OPTIONS.includes(key)) {
result[key] = parsedOptions[key];
return result;
}
if (!result.variables) {
result.variables = {};
}
result.variables[key] = parsedOptions[key];
return result;
}, {});
const baseUrl = parsedOptions.baseUrl || request2.endpoint.DEFAULTS.baseUrl;
if (GHES_V3_SUFFIX_REGEX.test(baseUrl)) {
requestOptions.url = baseUrl.replace(GHES_V3_SUFFIX_REGEX, "/api/graphql");
}
return request2(requestOptions).then((response) => {
if (response.data.errors) {
const headers = {};
for (const key of Object.keys(response.headers)) {
headers[key] = response.headers[key];
}
throw new GraphqlResponseError(
requestOptions,
headers,
response.data
);
}
return response.data.data;
});
}
function withDefaults(request2, newDefaults) {
const newRequest = request2.defaults(newDefaults);
const newApi = (query, options) => {
return graphql(newRequest, query, options);
};
return Object.assign(newApi, {
defaults: withDefaults.bind(null, newRequest),
endpoint: newRequest.endpoint
});
}
withDefaults(request, {
headers: {
"user-agent": `octokit-graphql.js/${VERSION$1} ${getUserAgent()}`
},
method: "POST",
url: "/graphql"
});
function withCustomRequest(customRequest) {
return withDefaults(customRequest, {
method: "POST",
url: "/graphql"
});
}
var REGEX_IS_INSTALLATION_LEGACY = /^v1\./;
var REGEX_IS_INSTALLATION = /^ghs_/;
var REGEX_IS_USER_TO_SERVER = /^ghu_/;
async function auth(token) {
const isApp = token.split(/\./).length === 3;
const isInstallation = REGEX_IS_INSTALLATION_LEGACY.test(token) || REGEX_IS_INSTALLATION.test(token);
const isUserToServer = REGEX_IS_USER_TO_SERVER.test(token);
const tokenType = isApp ? "app" : isInstallation ? "installation" : isUserToServer ? "user-to-server" : "oauth";
return {
type: "token",
token,
tokenType
};
}
function withAuthorizationPrefix(token) {
if (token.split(/\./).length === 3) {
return `bearer ${token}`;
}
return `token ${token}`;
}
async function hook(token, request2, route, parameters) {
const endpoint2 = request2.endpoint.merge(
route,
parameters
);
endpoint2.headers.authorization = withAuthorizationPrefix(token);
return request2(endpoint2);
}
var createTokenAuth = function createTokenAuth2(token) {
if (!token) {
throw new Error("[@octokit/auth-token] No token passed to createTokenAuth");
}
if (typeof token !== "string") {
throw new Error(
"[@octokit/auth-token] Token passed to createTokenAuth is not a string"
);
}
token = token.replace(/^(token|bearer) +/i, "");
return Object.assign(auth.bind(null, token), {
hook: hook.bind(null, token)
});
};
const VERSION = "6.1.2";
const noop = () => {
};
const consoleWarn = console.warn.bind(console);
const consoleError = console.error.bind(console);
const userAgentTrail = `octokit-core.js/${VERSION} ${getUserAgent()}`;
class Octokit {
constructor(options = {}) {
// assigned during constructor
__publicField(this, "request");
__publicField(this, "graphql");
__publicField(this, "log");
__publicField(this, "hook");
// TODO: type `octokit.auth` based on passed options.authStrategy
__publicField(this, "auth");
const hook2 = new Hook.Collection();
const requestDefaults = {
baseUrl: request.endpoint.DEFAULTS.baseUrl,
headers: {},
request: Object.assign({}, options.request, {
// @ts-ignore internal usage only, no need to type
hook: hook2.bind(null, "request")
}),
mediaType: {
previews: [],
format: ""
}
};
requestDefaults.headers["user-agent"] = options.userAgent ? `${options.userAgent} ${userAgentTrail}` : userAgentTrail;
if (options.baseUrl) {
requestDefaults.baseUrl = options.baseUrl;
}
if (options.previews) {
requestDefaults.mediaType.previews = options.previews;
}
if (options.timeZone) {
requestDefaults.headers["time-zone"] = options.timeZone;
}
this.request = request.defaults(requestDefaults);
this.graphql = withCustomRequest(this.request).defaults(requestDefaults);
this.log = Object.assign(
{
debug: noop,
info: noop,
warn: consoleWarn,
error: consoleError
},
options.log
);
this.hook = hook2;
if (!options.authStrategy) {
if (!options.auth) {
this.auth = async () => ({
type: "unauthenticated"
});
} else {
const auth2 = createTokenAuth(options.auth);
hook2.wrap("request", auth2.hook);
this.auth = auth2;
}
} else {
const { authStrategy, ...otherOptions } = options;
const auth2 = authStrategy(
Object.assign(
{
request: this.request,
log: this.log,
// we pass the current octokit instance as well as its constructor options
// to allow for authentication strategies that return a new octokit instance
// that shares the same internal state as the current one. The original
// requirement for this was the "event-octokit" authentication strategy
// of https://github.com/probot/octokit-auth-probot.
octokit: this,
octokitOptions: otherOptions
},
options.auth
)
);
hook2.wrap("request", auth2.hook);
this.auth = auth2;
}
const classConstructor = this.constructor;
for (let i = 0; i < classConstructor.plugins.length; ++i) {
Object.assign(this, classConstructor.plugins[i](this, options));
}
}
static defaults(defaults) {
const OctokitWithDefaults = class extends this {
constructor(...args) {
const options = args[0] || {};
if (typeof defaults === "function") {
super(defaults(options));
return;
}
super(
Object.assign(
{},
defaults,
options,
options.userAgent && defaults.userAgent ? {
userAgent: `${options.userAgent} ${defaults.userAgent}`
} : null
)
);
}
};
return OctokitWithDefaults;
}
/**
* Attach a plugin (or many) to your Octokit instance.
*
* @example
* const API = Octokit.plugin(plugin1, plugin2, plugin3, ...)
*/
static plugin(...newPlugins) {
var _a;
const currentPlugins = this.plugins;
const NewOctokit = (_a = class extends this {
}, __publicField(_a, "plugins", currentPlugins.concat(
newPlugins.filter((plugin) => !currentPlugins.includes(plugin))
)), _a);
return NewOctokit;
}
}
__publicField(Octokit, "VERSION", VERSION);
__publicField(Octokit, "plugins", []);
class GithubService {
constructor(props) {
this.token = props.token;
}
/* ----- Computes ----- */
get client() {
return new Octokit({ auth: this.token, userAgent: "gitto/v0.0.0" });
}
// Queries
async getRepo(ownerId, repoId) {
try {
const endpoint2 = `/repos/${ownerId}/${repoId}`;
const response = await this.client.request(`GET ${endpoint2}`);
return { status: response.status, data: response.data };
} catch (e) {
return { status: e.status, message: e.message };
}
}
async getRepoZip(ownerId, repoId) {
try {
const endpoint2 = `/repos/${ownerId}/${repoId}/zipball`;
const response = await this.client.request(`GET ${endpoint2}`);
return { status: response.status, data: response.data };
} catch (e) {
return { status: e.status, message: e.message };
}
}
async getRepoVersionZip(ownerId, repoId, versionId) {
try {
const endpoint2 = `/repos/${ownerId}/${repoId}/zipball/${versionId}`;
const response = await this.client.request(`GET ${endpoint2}`);
return { status: response.status, data: response.data };
} catch (e) {
return { status: e.status, message: e.message };
}
}
async getOrgs(params = {}) {
try {
const endpoint2 = `/user/orgs`;
const response = await this.client.request(`GET ${endpoint2}`, params);
return { status: response.status, data: response.data };
} catch (e) {
return { status: e.status, message: e.message };
}
}
async createRepo(ownerId, data) {
try {
const response = await this.getOrgs();
if (response.status !== 200)
throw response;
const hasOrg = response.data.find((v) => v.login === ownerId);
return hasOrg ? await this.createOrgRepo(ownerId, data) : await this.createPersonalRepo(data);
} catch (e) {
return { status: e.status, message: e.message };
}
}
async createPersonalRepo(data) {
try {
const endpoint2 = `/user/repos`;
const response = await this.client.request(`POST ${endpoint2}`, data);
return { status: response.status, data: response.data };
} catch (e) {
return { status: e.status, message: e.message };
}
}
async createOrgRepo(ownerId, data) {
try {
const endpoint2 = `/orgs/${ownerId}/repos`;
const response = await this.client.request(`POST ${endpoint2}`, data);
return { status: response.status, data: response.data };
} catch (e) {
return { status: e.status, message: e.message };
}
}
}
const Archiver = require("archiver");
const path = require("node:path");
const Fs = require("fs-extra");
const Stream = require("fstream");
const Unzip = require("unzip-stream");
class StorageService {
constructor(props) {
this.basePath = props.basePath;
this.fileName = props == null ? void 0 : props.fileName;
this.nestedPath = props == null ? void 0 : props.nestedPath;
}
/* ----- Computed ----- */
get zipFile() {
return `${this.basePath}/${this.fileName}.zip`;
}
/* ----- Methods ----- */
async checkEmpty() {
const formattedPath = path.join(this.basePath, this.fileName);
const pathExists = await checkPath(formattedPath);
if (pathExists) {
const path2 = await fileStats(formattedPath);
if (path2.isDirectory()) {
const entries = await Fs.promises.readdir(formattedPath);
const filteredEntries = entries.filter((v) => v.slice(0, 1) !== ".");
return filteredEntries.length > 0 ? false : true;
}
return true;
}
return true;
}
async saveRepo(buffer) {
await checkOrCreatePath(this.basePath);
await writeFile(this.zipFile, Buffer.from(buffer));
}
async fetchRepoStats() {
return await fileStats(this.basePath);
}
async unzipRepo() {
await checkOrCreatePath(this.basePath);
return await new Promise((resolve, reject) => {
Fs.createReadStream(this.zipFile).pipe(Unzip.Parse()).on("entry", (entry) => {
const type = entry.type;
const fileName = entry.path;
const slicedPath = fileName.slice(fileName.indexOf("/"), fileName.length);
const formattedPath = path.join(this.fileName, slicedPath);
const updatedPath = formattedPath.replace("/" + this.nestedPath, "/");
const finalPath = path.join(this.basePath, updatedPath);
if (type === "Directory")
return entry.autodrain();
if (this.nestedPath) {
const rootPath = slicedPath.split("/")[1];
const rootFile = rootPath.toLowerCase();
if (fileName.includes(this.nestedPath) || rootFile === "license.md") {
const writer = Stream.Writer({ path: finalPath });
return entry.pipe(writer);
}
return entry.autodrain();
} else {
const writer = Stream.Writer({ path: finalPath });
return entry.pipe(writer);
}
}).on("close", (v) => resolve(v)).on("error", (e) => reject(e));
});
}
async cleanRepo() {
await removePath(this.basePath + "/.git");
await removePath(this.basePath + "/.github");
}
async zipRepo() {
await checkOrCreatePath(this.basePath);
const archive = Archiver("zip", { zlib: { level: 9 } });
const stream = Fs.createWriteStream(this.zipFile);
await new Promise((resolve, reject) => {
stream.on("close", (v) => resolve(v));
archive.directory(this.basePath, false);
archive.on("error", (err) => reject(err));
archive.pipe(stream);
archive.finalize();
});
}
async removeRepo() {
await removePath(this.basePath);
}
async removeZip() {
await removePath(this.zipFile);
}
}
const formatError = (e) => {
switch (e.status || e.code) {
case 401:
return apiError(e);
case 403:
return apiError(e);
case 422:
return apiError(e);
case 500:
return apiError(e);
default:
return cliError(e);
}
};
const apiError = (e) => {
const data = JSON.parse(e.response.text);
const error2 = data.errors[0];
const { status, title, detail, framework } = error2;
return { status, title, detail, framework };
};
const cliError = (e) => {
return { status: e.status || e.code || 500, title: e.name, detail: e.message, framework: e.stack };
};
const sleep = (ms) => {
return new Promise((resolve) => setsleep(resolve, ms));
};
const chalk = require("chalk");
const log = (title, ...data) => {
return;
};
const success = (title, ...data) => {
return console.log(chalk.green(`${title} :`, ...data));
};
const error = (title, ...data) => {
return console.log(chalk.red(`${title} :`, ...data));
};
const ora$2 = require("ora");
const cloneCommand = async (url, options) => {
try {
const env = options.config;
const account = options.account || null;
const name = options.name || null;
const version2 = options.version || null;
log("options: ", { env, account, name, version: version2 });
const configService = new ConfigService({ basePath: env });
const configSpinner = ora$2("Setting up...\n").start();
await sleep(300);
const configValid = await configService.checkEmpty();
if (!configValid)
return configSpinner.fail("Please provide a valid config file.");
const config = await configService.readContents();
log("config: ", config);
const githubPath = url.includes("github.com") ? url.split("https://github.com/")[1] : url;
const githubFragments = githubPath.split("/");
const ownerId = githubFragments[0];
const repoId = githubFragments[1];
const nestedPath = githubFragments.includes("tree") ? githubFragments.slice(4, githubPath.length).join("/") : githubFragments.slice(2, githubPath.length).join("/");
log("url: ", { ownerId, repoId, nestedPath });
const basePath = process.cwd();
const fileName = name || githubFragments[githubFragments.length - 1];
log("destination: ", { basePath, fileName });
configSpinner.succeed("Setup complete!");
const githubService = new GithubService({ token: config.GITHUB_TOKEN });
const githubSpinner = ora$2("Fetching repo...\n").start();
await sleep(300);
const zipResponse = version2 ? await githubService.getRepoVersionZip(ownerId, repoId, version2) : await githubService.getRepoZip(ownerId, repoId);
if (zipResponse.status !== 200) {
githubSpinner.fail("Fetch failed!");
return error("Github: ", JSON.stringify(zipResponse));
}
githubSpinner.succeed("Fetch complete!");
const storageService = new StorageService({ basePath, fileName, nestedPath });
const storageSpinner = ora$2("Storing repo...\n").start();
await sleep(300);
const storageEmpty = await storageService.checkEmpty();
if (!storageEmpty)
return storageSpinner.fail(`Please clear directory: ${basePath}/${fileName}`);
await storageService.saveRepo(zipResponse.data);
await storageService.unzipRepo();
await storageService.cleanRepo();
await storageService.removeZip();
storageSpinner.succeed("Storage complete!");
const cloneSpinner = ora$2("Checking github...\n").start();
await sleep(300);
const repoResponse = await githubService.getRepo(account, fileName);
if (repoResponse.status !== 404) {
cloneSpinner.fail("Repo already exists!");
return error("github: ", JSON.stringify(repoResponse));
}
cloneSpinner.text = "Creating repo...";
const createResponse = await githubService.createRepo(account, {
name: fileName,
private: false
});
if (createResponse.status !== 201) {
cloneSpinner.fail("Create failed!");
return error("github: ", JSON.stringify(createResponse));
}
cloneSpinner.text = "Cloning repo...";
const gitService = new GitService({ basePath, token: config.GITHUB_TOKEN });
await gitService.create(account, fileName);
cloneSpinner.succeed("Clone complete!");
} catch (e) {
error(formatError(e));
process.exit();
}
};
const ora$1 = require("ora");
const configCommand = async (options) => {
const spinner = ora$1("Fetching config...");
try {
const config = options.c || options.config;
log("options: ", { config });
spinner.start();
await sleep(300);
const service = new ConfigService({ basePath: config });
await service.setup();
const contents = await service.readContents();
const data = {};
Object.keys(contents).map((key) => {
if (key === "token")
return data[key] = "********";
data[key] = contents[key];
});
const formattedResponse = Object.keys(data).length > 0 ? JSON.stringify(data, null, 2) : "Nothing to see yet.";
spinner.succeed();
success(`Config [${config}]: `, formattedResponse);
} catch (e) {
spinner.fail();
error(e);
process.exit();
}
};
const ora = require("ora");
const downloadCommand = async (url, options) => {
try {
const env = options.config;
const path2 = options.path || null;
const unzip = options.unzip || null;
const clean = options.clean || null;
const remove = options.remove || null;
const version2 = options.version || null;
log("options: ", { env, path: path2, unzip, clean, remove, version: version2 });
const configService = new ConfigService({ basePath: env });
const configSpinner = ora("Setting up...\n").start();
await sleep(300);
const configValid = await configService.checkEmpty();
if (!configValid)
return configSpinner.fail("Please provide a valid config file.");
const config = await configService.readContents();
log("config: ", config);
const githubPath = url.includes("github.com") ? url.split("https://github.com/")[1] : url;
const githubFragments = githubPath.split("/");
const ownerId = githubFragments[0];
const repoId = githubFragments[1];
const nestedPath = githubFragments.includes("tree") ? githubFragments.slice(4, githubPath.length).join("/") : githubFragments.slice(2, githubPath.length).join("/");
log("url: ", { ownerId, repoId, nestedPath });
const basePath = path2 || process.cwd();
const fileName = githubFragments[githubFragments.length - 1];
log("destination: ", { basePath, fileName });
configSpinner.succeed("Setup complete!");
const githubService = new GithubService({ token: config.GITHUB_TOKEN });
const githubSpinner = ora("Fetching repo...\n").start();
await sleep(300);
const zipResponse = version2 ? await githubService.getRepoVersionZip(ownerId, repoId, version2) : await githubService.getRepoZip(ownerId, repoId);
if (zipResponse.status !== 200) {
githubSpinner.fail("Fetch failed!");
return error("Github: ", JSON.stringify(zipResponse));
}
githubSpinner.succeed("Fetch complete!");
const storageService = new StorageService({ basePath, fileName, nestedPath });
const storageSpinner = ora("Storing repo...\n").start();
await sleep(300);
const storageEmpty = await storageService.checkEmpty();
if (!storageEmpty)
return storageSpinner.fail(`Please clear directory: ${basePath}/${fileName}`);
await storageService.saveRepo(zipResponse.data);
if (unzip)
await storageService.unzipRepo();
if (clean)
await storageService.cleanRepo();
if (remove)
await storageService.removeZip();
storageSpinner.succeed("Storage complete!");
} catch (e) {
error(formatError(e));
process.exit();
}
};
const os = require("os");
const sade = require("sade");
const cli = sade("gitty");
cli.version(version).describe("A simple CLI to fetch, store and clone Github repositories.").option("--config", "Path to your config file.", `${os.homedir()}/.gittyrc`);
cli.command("config").describe("Display your config file.").example("gitty config").action(configCommand);
cli.command("clone <url>").describe("Clone a Github repo or subdirectory to your account.").option("--path", "Specify path to a local directory (defaults to CWD).").option("--account", "Specify the account where we should add this clone.").option("--name", "Specify the name for cloned repository.").option("--version", "Specify tarball version (optional).").example("gitty clone https://github.com/vercel/next.js").example("gitty clone https://github.com/vercel/next.js --account ehubbell").example("gitty clone https://github.com/vercel/next.js --account ehubbell --name vercel-copy").action(cloneCommand);
cli.command("download <url>").describe("Download a Github repo or subdirectory to your local file system.").option("--path", "Specify path to a local directory (defaults to CWD).").option("--unzip", "Automatically unzip the binary file", false).option("--clean", "Automatically remove the .git directory", false).option("--remove", "Automatically remove the binary file", false).option("--version", "Specify tarball version (optional).").example("gitty download https://github.com/vercel/next.js").example("gitty download https://github.com/vercel/next.js/tree/main/examples/angular --path ~/templates/angular").example("gitty download https://github.com/vercel/next.js/tree/main/examples/angular --unzip --clean --remove").action(downloadCommand);
cli.parse(process.argv);
//# sourceMappingURL=index.cjs.js.map