UNPKG

@ehubbell/gitty

Version:

A simple CLI that will fetch, store, and clone Github repos.

1,398 lines (1,397 loc) 48.4 kB
#!/usr/bin/env node "use strict"; 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