@7i7o/git-remote-proland
Version: 
Git Remote Helper
175 lines (157 loc) • 5.25 kB
text/typescript
import { getAddress } from './arweaveHelper';
import type { Repo, Tag } from '../types';
import { execSync } from 'child_process';
import { accessSync, constants, readFileSync } from 'fs';
import type { JsonWebKey } from 'crypto';
import path from 'path';
const ANSI_RESET = '\x1b[0m';
const ANSI_RED = '\x1b[31m';
const ANSI_GREEN = '\x1b[32m';
const DIRTY_EXT = ".tmp"
export const PL_TMP_PATH = '.protocol.land';
export const GIT_CONFIG_KEYFILE = 'protocol.land.keyfile';
export const getWarpContractTxId = () =>
    'w5ZU15Y2cLzZlu3jewauIlnzbKw-OAxbN9G5TbuuiDQ';
export const log = (message: any, options?: { color: 'red' | 'green' }) => {
    if (!options) console.error(` [PL] ${message}`);
    else {
        const { color } = options;
        console.error(
            `${
                color === 'red' ? ANSI_RED : ANSI_GREEN
            } [PL] ${message}${ANSI_RESET}`
        );
    }
};
let wallet: JsonWebKey | null = null;
export const getJwkPath = () => {
    try {
        return execSync(`git config --get ${GIT_CONFIG_KEYFILE}`)
            .toString()
            .trim();
    } catch (error) {
        return '';
    }
};
export const getWallet = (params: { warn: boolean } = { warn: false }) => {
    if (wallet) return wallet;
    const jwkPath = getJwkPath();
    if (!jwkPath) return walletNotFoundMessage(params);
    try {
        const jwk = readFileSync(jwkPath, { encoding: 'utf-8' })
            .toString()
            .trim();
        if (!jwk) return walletNotFoundMessage();
        return JSON.parse(jwk);
    } catch (error) {
        return walletNotFoundMessage();
    }
};
export const walletNotFoundMessage = (
    params: { warn: boolean } = { warn: false }
) => {
    const { warn } = params;
    if (warn) {
        log(
            `If you need to push to the repo, please set up the path to your Arweave JWK.`,
            { color: 'green' }
        );
    } else {
        log(`Failed to get wallet keyfile path from git config.`);
        log(
            `You need an owner or contributor wallet to have write access to the repo.`,
            { color: 'red' }
        );
    }
    log(
        `Run 'git config --add ${GIT_CONFIG_KEYFILE} YOUR_WALLET_KEYFILE_FULL_PATH' to set it up`,
        { color: 'green' }
    );
    log(
        `Use '--global' to have a default keyfile for all Protocol Land repos`,
        { color: 'green' }
    );
    return null;
};
export const ownerOrContributor = async (
    repo: Repo,
    wallet: JsonWebKey,
    options: { pushing: boolean } = { pushing: false }
) => {
    const { pushing } = options;
    const address = await getAddress(wallet);
    const ownerOrContrib =
        repo.owner === address ||
        repo.contributors.some((contributor) => contributor === address);
    if (!ownerOrContrib) notOwnerOrContributorMessage({ warn: !pushing });
    return ownerOrContrib;
};
export const notOwnerOrContributorMessage = (
    params: { warn: boolean } = { warn: false }
) => {
    const { warn } = params;
    if (warn) {
        log(
            `You are not the repo owner nor a contributor. You will need an owner or contributor jwk to push to this repo.`,
            { color: 'green' }
        );
    } else {
        log(
            `You are not the repo owner nor a contributor. You can't push to this repo.`,
            { color: 'red' }
        );
    }
    return null;
};
export async function getTags(title: string, description: string) {
    return [
        { name: 'App-Name', value: 'Protocol.Land' },
        { name: 'Content-Type', value: 'application/zip' },
        { name: 'Creator', value: await getAddress() },
        { name: 'Title', value: title },
        { name: 'Description', value: description },
        { name: 'Type', value: 'repo-update' },
    ] as Tag[];
}
export function clearCache(
    cachePath: string,
    options: { keepFolders: string[] }
) {
    const { keepFolders = [] } = options;
    const ommitedFolders = keepFolders.map((v) => `! -name "${v}"`).join(' ');
    execSync(
        `find ${cachePath} -mindepth 1 -maxdepth 1 -type d ${ommitedFolders} -exec rm -rf {} \\;`
    );
}
export function setCacheDirty(cachePath: string, remoteName: string) {
    if (!cachePath || !remoteName)
        throw new Error('Cache and MutexName are required');
    execSync(`touch ${path.join(cachePath, remoteName, DIRTY_EXT)}`);
}
export function unsetCacheDirty(cachePath: string, remoteName: string) {
    if (!cachePath || !remoteName)
        throw new Error('Cache and MutexName are required');
    execSync(`rm -f ${path.join(cachePath, remoteName, DIRTY_EXT)}`);
}
export function isCacheDirty(cachePath: string, remoteName: string) {
    if (!cachePath || !remoteName)
        throw new Error('Cache and MutexName are required');
    // Check if the file exists
    try {
        accessSync(
            path.join(cachePath, remoteName, DIRTY_EXT),
            constants.R_OK | constants.W_OK
        );
        return true;
    } catch {
        return false;
    }
}
export const waitFor = (delay: number) =>
    new Promise((res) => setTimeout(res, delay));
export const exitWithError = (message: string) => {
    log(``);
    log(`${message}`);
    log(``);
    process.exit(1);
};