[
  {
    "path": ".dockerignore",
    "content": "node_modules\ndist\nesm\nexample\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/workflows/nothing.yml",
    "content": "name: Nothing\n\non:\n    push:\n        branches:\n            - master\n\njobs:\n    build:\n        runs-on: ubuntu-latest\n        steps:\n            - uses: actions/checkout@v1\n            - uses: actions/setup-node@v1\n              with:\n                  node-version: 12\n                  registry-url: https://registry.npmjs.org/\n            - run: yarn\n            - run: yarn test\n            - run: tsc"
  },
  {
    "path": ".github/workflows/package.yml",
    "content": "name: Npm Package\n\non:\n    push:\n        branches:\n            - master\n\njobs:\n    build:\n        runs-on: ubuntu-latest\n        steps:\n            - uses: actions/checkout@v1\n            - uses: actions/setup-node@v1\n              with:\n                  node-version: 12\n                  registry-url: https://registry.npmjs.org/\n            - run: yarn\n            - run: yarn test\n            - run: tsc\n            - run: tsc -m es6 --outDir esm\n            - name: Bump version\n              uses: remorses/bump-version@js\n              with:\n                  version_file: VERSION\n              env:\n                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n            - run: yarn publish\n              env:\n                  NODE_AUTH_TOKEN: ${{ secrets.npm_token }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.DS_Store\nplay*\ndist/*\nesm\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n\n# next.js build output\n.next\n\n# nuxt.js build output\n.nuxt\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless\n\n# FuseBox cache\n.fusebox/\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"type\": \"node\",\n            \"request\": \"launch\",\n            \"name\": \"Launch Program\",\n            \"skipFiles\": [\n                \"<node_internals>/**\"\n            ],\n            \"program\": \"${workspaceFolder}/dist/index.js\",\n            \"args\": [\"-w\", \"package.yml\", \"-n\"],\n            \"outFiles\": [\n                \"${workspaceFolder}/**/*.js\"\n            ]\n        }\n    ]\n}"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:12-alpine\n\nRUN apk add --no-cache dumb-init # build-base\n\nWORKDIR /workdir\n\nCOPY *.json *.lock /workdir/\n\nRUN yarn\n\nCOPY . /workdir/\n\nENTRYPOINT [\"dumb-init\", \"--\"]\nCMD yarn dev\n"
  },
  {
    "path": "README.md",
    "content": "<div align='center'>\n    <br/>\n    <br/>\n    <h1>actions-cli</h1>\n    <h3>Monitor your GitHub Actions from the command line</h3>\n    <br/>\n    <img width='500px' src='https://media.giphy.com/media/JUYF1dCf2qQ1T63VFU/giphy.gif'>\n    <br/>\n    <br/>\n</div>\n\n```\nnpm install -g actions-cli\n```\n\n## Usage\n\n```\n$ actions-cli login\n$ actions-cli ./\n```\n\n```\nactions-cli\n\nFetch the current hash job status and logs\n\nCommands:\n  actions-cli login  Logins to cli\n  actions-cli        Fetch the current hash job status and logs    [predefinito]\n\nPositionals:\n  path  The github repo path                         [stringa] [predefinito: \"\"]\n\nOptions:\n  --version       Mostra il numero di versione                        [booleano]\n  --verbose, -v                                  [booleano] [predefinito: false]\n  -h              Mostra la schermata di aiuto                        [booleano]\n  --sha           The sha to look for actions, at least 7 characters long\n                                         [stringa] [richiesto] [predefinito: \"\"]\n  --workflow, -w  The workflow file name             [stringa] [predefinito: \"\"]\n  --job, -j       The job name, defaults to the first listed job\n                                                     [stringa] [predefinito: \"\"]\n```\n\n### TODOS\n\n-   multiple jobs per workflow\n-   multiple workflows\n-   ~~use latest commit pushed instead of latest commit~~ use -p\n-   ~~don't use the commit pushed from github actions~~ use -n\n\n\n"
  },
  {
    "path": "VERSION",
    "content": "0.0.36"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"actions-cli\",\n    \"_\": \"[bump]\",\n    \"version\": \"0.0.36\",\n    \"description\": \"\",\n    \"main\": \"dist/index.js\",\n    \"types\": \"dist/index.d.ts\",\n    \"bin\": {\n        \"actions-cli\": \"./dist/index.js\"\n    },\n    \"mocha\": {\n        \"require\": \"tests/init.js\",\n        \"spec\": \"tests/**.ts\",\n        \"timeout\": 9999999999\n    },\n    \"files\": [\n        \"/dist/*\",\n        \"/esm/*\"\n    ],\n    \"scripts\": {\n        \"test\": \"NODE_ENV=test mocha --colors --exit\",\n        \"example\": \"parcel serve --no-autoinstall example/index.html\",\n        \"compile\": \"sucrase -q ./src -d ./dist --transforms typescript,imports\",\n        \"dev\": \"yarn compile && node dist\",\n        \"play\": \"tsc --incremental && node dist\"\n    },\n    \"keywords\": [],\n    \"author\": \"Tommaso De Rossi, morse <beats.by.morse@gmail.com>\",\n    \"license\": \"ISC\",\n    \"devDependencies\": {\n        \"@types/mocha\": \"^5.2.7\",\n        \"@types/node\": \"^12.0.7\",\n        \"dotenv\": \"^8.2.0\",\n        \"mocha\": \"^6.1.4\",\n        \"sucrase\": \"^3.12.1\",\n        \"typescript\": \"^3.8.3\"\n    },\n    \"dependencies\": {\n        \"@octokit/rest\": \"^17.10.0\",\n        \"@types/chalk\": \"^2.2.0\",\n        \"@types/lodash\": \"^4.14.150\",\n        \"@types/memoizee\": \"^0.4.4\",\n        \"@types/node-fetch\": \"^2.5.7\",\n        \"@types/yargs\": \"^15.0.4\",\n        \"appdata-path\": \"^1.0.0\",\n        \"await-to-js\": \"^2.1.1\",\n        \"chalk\": \"^4.0.0\",\n        \"cli-social-login\": \"^1.0.1\",\n        \"cli-spinners\": \"^2.3.0\",\n        \"conf\": \"^6.2.4\",\n        \"encoding\": \"^0.1.12\",\n        \"git-remote-origin-url\": \"^3.1.0\",\n        \"lodash\": \"^4.17.15\",\n        \"log-symbols\": \"^4.0.0\",\n        \"log-update\": \"^4.0.0\",\n        \"memoizee\": \"^0.4.14\",\n        \"multispinner\": \"^0.2.1\",\n        \"node-fetch\": \"^2.6.0\",\n        \"ora\": \"^4.0.4\",\n        \"parse-github-url\": \"^1.0.2\",\n        \"simple-git\": \"^2.2.0\",\n        \"winston\": \"^3.2.1\",\n        \"yargs\": \"^15.3.1\"\n    }\n}\n"
  },
  {
    "path": "src/constants.ts",
    "content": "import getAppDataPath from 'appdata-path'\nimport path from 'path'\n\nconst APP_DATA_FOLDER = 'actions-cli'\nexport const USER_TOKEN_CONFIG_KEY = 'token'\nexport const APP_DATA_PATH = path.resolve(getAppDataPath(APP_DATA_FOLDER))\nexport const firebaseConfig = {\n    apiKey: 'AIzaSyAdM1fGOP_2bxM72M-Tf91wI76WiSe1ajE',\n    authDomain: 'actions-cli-85f6f.firebaseapp.com',\n    databaseURL: 'https://actions-cli-85f6f.firebaseio.com',\n    projectId: 'actions-cli-85f6f',\n    storageBucket: 'actions-cli-85f6f.appspot.com',\n    messagingSenderId: '846622072078',\n    appId: '1:846622072078:web:d7f258caf4cc93e6090b5f',\n    measurementId: 'G-01QPNTKXC1',\n}\n"
  },
  {
    "path": "src/fetch.ts",
    "content": "import memoize from 'memoizee'\nimport { RestEndpointMethodTypes, Octokit } from '@octokit/rest'\nimport { flatten } from 'lodash'\nimport simpleGit from 'simple-git/promise'\nimport to from 'await-to-js'\nimport chalk from 'chalk'\nimport { execSync } from 'child_process'\nimport { dots as cliSpinner } from 'cli-spinners'\nimport getRepoUrl from 'git-remote-origin-url'\nimport * as logSymbols from 'log-symbols'\nimport Multispinner from 'multispinner'\nimport Spinners from 'multispinner/lib/spinners'\nimport ora from 'ora'\nimport logUpdate from 'log-update'\nimport path from 'path'\nimport { Argv } from 'yargs'\n\nimport {\n    initOctokit,\n    parseGithubUrl,\n    printGreen,\n    printRed,\n    sleep,\n    catchAll,\n    print,\n} from './support'\n\nconst DEBUG = process.env.DEBUG\n\nconst FetchCommand = {\n    command: '$0',\n    describe: 'Fetch the current hash job status and logs',\n    builder: (argv: Argv) => {\n        argv.positional('path', {\n            type: 'string',\n            default: '',\n            required: true,\n            description: 'The github repo path',\n        })\n        argv.option('sha', {\n            type: 'string',\n            default: '',\n            required: true,\n            description:\n                'The sha to look for actions, at least 7 characters long',\n        })\n        argv.option('workflow', {\n            type: 'string',\n            default: '',\n            alias: 'w',\n            description: 'The workflow file name',\n        })\n        argv.option('job', {\n            type: 'string',\n            default: '',\n            alias: 'j',\n            description: 'The job name, defaults to the first listed job',\n        })\n        argv.option('non-actions-commit', {\n            type: 'boolean',\n            default: false,\n            alias: 'n',\n            description:\n                'Double checks that the used sha was not committed by github-actions, only works for recent actions commits',\n        })\n        argv.option('pushed-commit', {\n            type: 'boolean',\n            default: false,\n            alias: 'p',\n            description:\n                'Use latest pushed commit instead of using latest commit in HEAD',\n        })\n    },\n    handler: catchAll(async (argv) => {\n        const octokit = initOctokit()\n        const jobToFetch = argv.job\n        const useLatestPushedCommit = argv['pushed-commit']\n        const skipActionsCheck = !argv['non-actions-commit']\n        const workflowToFetch = argv.workflow\n        const currentPath = path.resolve(argv.path || process.cwd())\n        const { owner, repo } = await getRepoInfo(currentPath)\n        const spinner = ora(`getting last commit sha`).start()\n        let sha =\n            argv.sha ||\n            (await getLastCommit({\n                octokit,\n                owner,\n                repo,\n                cwd: currentPath,\n                skipActionsCheck,\n                useLatestPushedCommit,\n            }))\n        const prettySha = sha.slice(0, 7)\n        changeSpinnerText({\n            text: `fetching state for sha '${prettySha}'`,\n            spinner,\n        })\n        while (true) {\n            let workflowRuns = []\n\n            if (workflowToFetch) {\n                const [err, data] = await to(\n                    octokit.actions.listWorkflowRuns({\n                        owner,\n                        repo,\n                        workflow_id: workflowToFetch,\n                    }),\n                )\n                if (err) {\n                    spinner.stop()\n                    printRed(`Workflow '${workflowToFetch}' not found`)\n                    return\n                }\n                workflowRuns = data.data.workflow_runs\n            } else {\n                const data = await octokit.actions.listWorkflowRunsForRepo({\n                    owner,\n                    repo,\n                })\n                workflowRuns = data.data.workflow_runs\n            }\n            const lastRun = workflowRuns.find((x) => {\n                const { head_sha, status, id, conclusion, workflow_url } = x\n                // console.log({ workflow_url })\n                if (head_sha.slice(0, 7) === sha.slice(0, 7)) {\n                    // console.log('found')\n\n                    return true\n                }\n                return false\n            })\n            if (!lastRun) {\n                changeSpinnerText({\n                    spinner,\n                    text: `waiting job handling last commit '${prettySha}'`,\n                })\n                await sleep(3000)\n                if (!argv.sha) {\n                    sha = await getLastCommit({\n                        octokit,\n                        owner,\n                        repo,\n                        cwd: currentPath,\n                        skipActionsCheck,\n                        useLatestPushedCommit,\n                    })\n                }\n                continue\n            }\n\n            const { head_sha, status, id, conclusion, url, html_url } = lastRun\n            // console.log(\n            //     'unexpected values',\n            //     JSON.stringify({ head_sha, status, id, conclusion }, null, 4)\n            // )\n            if (status === 'queued') {\n                changeSpinnerText({ spinner, text: 'queued' })\n                await sleep(3000)\n                continue\n            }\n            if (status === 'in_progress') {\n                changeSpinnerText({ spinner, text: 'in progress' })\n                spinner.info()\n                spinner.stop()\n                await pollJobs({ repo, owner, id, jobToFetch })\n                return\n            }\n            if (status === 'completed') {\n                changeSpinnerText({ spinner, text: 'completed' })\n                spinner.info()\n                spinner.stop()\n                // if (conclusion === 'success') {\n                //     spinner.succeed(chalk.green('Success'))\n                // }\n                // if (conclusion === 'failure') {\n                //     spinner.fail(chalk.red('Failure'))\n                // }\n                await pollJobs({ repo, owner, id, jobToFetch })\n                return\n            }\n            console.log(\n                'unexpected state',\n                JSON.stringify({ head_sha, status, id, conclusion }, null, 4),\n            )\n            spinner.fail('Wtf?')\n            return\n        }\n    }),\n} // as CommandModule\n\nexport default FetchCommand\n\nexport async function pollJobs({ owner, repo, id, jobToFetch }) {\n    DEBUG && console.log('pollJobs')\n    const octokit = initOctokit()\n    let spinners = null\n    while (true) {\n        const data = await octokit.actions.listJobsForWorkflowRun({\n            owner,\n            repo,\n            run_id: id,\n        })\n        const job = jobToFetch\n            ? data.data.jobs.find((job) => {\n                  return job.name === jobToFetch\n              })\n            : data.data.jobs?.[0]\n        if (!job) {\n            printRed(\n                `Job '${jobToFetch}' not found, make sure your yaml is valid`,\n            )\n            return\n        }\n        if (\n            spinners === null ||\n            // if the steps changed during build\n            Object.keys(spinners.spinners).length !== job.steps.length\n        ) {\n            const obj = Object.assign(\n                {},\n                ...job.steps.map((x) => ({\n                    [x.number]: x.name,\n                })),\n            )\n            if (!spinners) {\n                // init spinners\n                spinners = new Multispinner(obj, {\n                    // clear: false,\n                    // update: logUpdate,\n                    ...cliSpinner,\n                })\n            } else {\n                // add a new spinner\n                const spinnersKeys = Object.keys(spinners.spinners)\n                Object.keys(obj).map((k) => {\n                    if (!spinnersKeys.includes(k)) {\n                        addSpinner({ spinners, key: k, value: obj[k] })\n                    }\n                })\n            }\n            DEBUG && console.log(JSON.stringify(job, null, 4))\n        }\n        displayJobsTree({ spinners, job })\n        if (job.status !== 'completed') {\n            await sleep(2000)\n            continue\n        }\n        spinners.update.clear() // TODO bug on multispinners\n        if (job.conclusion === 'failure') {\n            job.steps.forEach((step) => {\n                const spinner = spinners.spinners[step.number]\n                if (spinner && spinner.state === 'incomplete') {\n                    spinners.error(step.number)\n                }\n            })\n        }\n        if (job.conclusion === 'failure') {\n            printRed(\n                `${\n                    logSymbols.error\n                } Failed, read the logs at ${chalk.whiteBright(job.html_url)}`,\n            )\n            return\n        }\n        if (job.conclusion === 'success') {\n            printGreen(\n                `${\n                    logSymbols.success\n                } Success, read the logs at ${chalk.whiteBright(job.html_url)}`,\n            )\n            return\n        }\n        console.log(JSON.stringify(job, null, 4))\n        return\n    }\n}\n\nexport function addSpinner({ spinners, key, value }) {\n    spinners.spinners[key] = Spinners.prototype.spinnerObj(value)\n}\n\nexport function displayJobsTree({\n    job = null as RestEndpointMethodTypes['actions']['listJobsForWorkflowRun']['response']['data']['jobs'][0],\n    spinners,\n}) {\n    // console.log(JSON.stringify(job, null, 4))\n    for (let step of job.steps) {\n        try {\n            if (\n                !Object.keys(spinners.spinners).includes(step.number.toString())\n            ) {\n                continue\n            }\n            if (step.status === 'queued') {\n                // spinner.info(step.name)\n                // return { ok: true }\n            }\n            if (step.status === 'in_progress') {\n                // spinner.info(step.name)\n                // spinners.success(step.number)\n                // return { ok: true }\n            }\n            if (step.status === 'completed') {\n                if (step.conclusion === 'success') {\n                    // spinner.info(step.name)\n                    spinners.success(step.number)\n                    // return { ok: true, completed: true }\n                }\n                if (step.conclusion === 'failure') {\n                    spinners.error(step.number)\n                    // return { ok: false, completed: true }\n                }\n            }\n        } catch (e) {\n            // console.log('wtf', step)\n            console.log(JSON.stringify(job, null, 4))\n            throw e\n            // console.error(step.name, step.number, e)\n        }\n    }\n}\n\nfunction changeSpinnerText({ spinner, text }) {\n    if (spinner.text !== text) {\n        spinner.info()\n    }\n    spinner.start(text)\n}\n\n// export async function isCommitFromActions({ sha, repo, owner }) {\n//     const octokit = initOctokit()\n//     const data = await octokit.repos.getCommit({ owner, repo, ref: sha })\n//     console.log(JSON.stringify(data.data, null, 4))\n//     return false\n// }\n\nexport async function getRepoInfo(currentPath) {\n    const gitRepoUrl = await getRepoUrl(currentPath)\n    const { name: repo, owner } = parseGithubUrl(gitRepoUrl)\n    return { repo, owner }\n}\n\nconst GITHUB_ACTIONS_BOT_LOGIN = 'github-actions[bot]'\n\nconst getRepoCommits = memoize(\n    async ({\n        octokit,\n        owner,\n        repo,\n    }): Promise<{ actor: string; refs: string[] }[]> => {\n        const data = await octokit.activity.listRepoEvents({\n            owner,\n            repo,\n            per_page: 50,\n        })\n        const commits = data.data\n            .filter((event) => {\n                return event.type === 'PushEvent'\n            })\n            .map((event) => {\n                return {\n                    actor: event.actor.login,\n                    refs: event.payload.commits.map((x) => x.sha),\n                }\n            })\n        return commits\n    },\n)\n\nexport async function getLastCommit(args: {\n    octokit: Octokit\n    owner\n    repo\n    cwd\n    skipActionsCheck?: boolean\n    useLatestPushedCommit: boolean\n}): Promise<string> {\n    if (args.useLatestPushedCommit) {\n        return execSync(`git rev-parse origin/${getCurrentBranch()}`)\n            .toString()\n            .trim()\n    }\n    const { owner, repo, octokit } = args\n    const git = simpleGit(args.cwd)\n    const lastLocalCommits = await git.log()\n\n    if (args.skipActionsCheck) {\n        lastLocalCommits.all.find(\n            (x) =>\n                !['[no ci]', '[skip ci]', '[ci skip]'].some((m) =>\n                    x.message.toLocaleLowerCase().includes(m),\n                ),\n        )\n    }\n    // console.log(JSON.stringify(data.data, null, 4))\n    const commits = await getRepoCommits({ owner, repo, octokit })\n    const githubActionsCommits: string[] = flatten(\n        commits\n            .filter((x) => {\n                if (process.env.DEBUG) {\n                    console.log('commit actor is ' + x.actor)\n                }\n                return x.actor === GITHUB_ACTIONS_BOT_LOGIN\n            })\n            .map((x) => x.refs),\n    ).map((x) => x.slice(0, 7))\n\n    const lastNonActionsCommit = lastLocalCommits.all.find((commit) => {\n        return !githubActionsCommits.includes(commit.hash.slice(0, 7))\n    })\n    // console.log(\n    //     '\\n' +\n    //         cliSpinner.frames[0] +\n    //         ` found a non actions commit made from ` +\n    //         lastNonActionsCommit.author_name ||\n    //         lastNonActionsCommit.author_email,\n    // )\n    return lastNonActionsCommit.hash\n}\n\nfunction getCurrentBranch() {\n    return execSync('git branch --show-current').toString().trim()\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "#!/usr/bin/env node\nimport yargs from 'yargs'\nimport winston from 'winston'\nimport loginCommand from './login'\nimport logoutCommand from './logout'\nimport { winstonConf } from './support'\nimport FetchCommand from './fetch'\n\nyargs\n    .option('verbose', {\n        alias: 'v',\n        type: 'boolean',\n        default: false,\n    })\n    .middleware([\n        (argv) => {\n            if (argv.verbose) {\n                winston.configure({\n                    ...winstonConf,\n                    level: 'debug',\n                })\n                return\n            }\n            winston.configure({ ...winstonConf, silent: true, level: 'error' })\n        },\n    ])\n    .command(loginCommand as any)\n    .command(logoutCommand as any)\n    .command(FetchCommand as any)\n    // .demandCommand()\n    .help('help').argv\n"
  },
  {
    "path": "src/login.ts",
    "content": "import yargs, { CommandModule, Argv } from 'yargs'\nimport { loginOnLocalhost } from 'cli-social-login'\nimport fs from 'fs'\nimport { USER_TOKEN_CONFIG_KEY, firebaseConfig } from './constants'\nimport { initStore, printRed } from './support'\n\nconst welcomeMessage =\n    'Run `actions-cli` to see the actions status for the current commit'\n\nexport default {\n    command: 'login',\n    describe: 'Logins to cli',\n    builder: (argv: Argv) => {\n        argv.option('token', {\n            type: 'string',\n            default: '',\n            description:\n                \"Pass the token directly, necessary if you can't login via localhost and browser\",\n        })\n    },\n    handler: async (argv) => {\n        const store = initStore()\n        if (argv.token) {\n            store.set(USER_TOKEN_CONFIG_KEY, argv.token)\n            console.log(`Token Saved`)\n            console.log(welcomeMessage)\n            return\n        }\n        // starts a server on localhost to login the user\n        const { credentials, user } = await loginOnLocalhost({\n            firebaseConfig,\n            providers: ['github'],\n            scopes: {\n                github: ['notifications', 'repo'], // TODO organizations dont work\n            },\n        })\n        const githubToken = credentials.oauthAccessToken\n        if (!githubToken) {\n            printRed('cannot get token')\n            return\n        }\n        store.set(USER_TOKEN_CONFIG_KEY, githubToken)\n        console.log(`Token Saved`)\n        console.log(welcomeMessage)\n    },\n} // as CommandModule\n"
  },
  {
    "path": "src/logout.ts",
    "content": "import yargs, { CommandModule, Argv } from 'yargs'\nimport { loginOnLocalhost } from 'cli-social-login'\nimport fs from 'fs'\nimport { USER_TOKEN_CONFIG_KEY, firebaseConfig } from './constants'\nimport { initStore, printRed } from './support'\n\nexport default {\n    command: 'logout',\n    describe: 'Deletes the saved token',\n    builder: (argv: Argv) => {},\n    handler: async (argv) => {\n        const store = initStore()\n        store.delete(USER_TOKEN_CONFIG_KEY)\n        console.log(`Deleted Token`)\n    },\n} // as CommandModule\n"
  },
  {
    "path": "src/support.ts",
    "content": "import Conf from 'conf'\nimport _parseGithubUrl from 'parse-github-url'\n\nimport chalk from 'chalk'\nimport path from 'path'\nimport { APP_DATA_PATH, USER_TOKEN_CONFIG_KEY } from './constants'\nimport winston from 'winston'\nimport { LoggerOptions } from 'winston'\nimport { Octokit } from '@octokit/rest'\n\nlet conf\nexport function initStore(): Conf {\n    if (!conf) {\n        conf = new Conf({ cwd: APP_DATA_PATH })\n    }\n    return conf\n}\n\nconst { format, transports } = winston\n\nconst logFormat = format.printf((info) => {\n    const msg =\n        typeof info.message === 'object'\n            ? JSON.stringify(info.message, null, 4)\n            : info.message\n    return `${info.timestamp} ${msg}`\n})\n\nexport const winstonConf: LoggerOptions = {\n    format: format.combine(\n        format.label({\n            label: path.basename(\n                (process.mainModule && process.mainModule.filename) || '',\n            ),\n        }),\n        format.timestamp({ format: 'YYYY-MM-DD HH' }),\n        // Format the metadata object\n        format.metadata({\n            fillExcept: ['message', 'level', 'timestamp', 'label'],\n        }),\n    ),\n    transports: [\n        new transports.Console({\n            format: format.combine(format.colorize({}), logFormat),\n        }),\n    ],\n}\n\nexport const print = console.log\nexport const printRed = (x) => console.log(chalk.red(x))\nexport const printGreen = (x) => console.log(chalk.green(x))\n\nexport function getGithubToken() {\n    const store = initStore()\n    const token = store.get(USER_TOKEN_CONFIG_KEY)\n    // console.log(token)\n    if (!token) {\n        printRed('cannot find github token, run `actions-cli login` first')\n        process.exit(1)\n    }\n    return token\n}\n\nexport function initOctokit(): Octokit {\n    const token = getGithubToken()\n    const octokit = new Octokit({ auth: token })\n    return octokit\n}\n\nexport function parseGithubUrl(githubUrl): { name; owner } {\n    if (!githubUrl) {\n        throw new Error(`cannot parse null github url `)\n    }\n    const parsedUrl = _parseGithubUrl(githubUrl)\n    if (!parsedUrl) {\n        throw new Error('cannot parse github url ' + githubUrl)\n    }\n    const { owner, name: repo } = parsedUrl\n    if (!owner || !repo) {\n        throw new Error('cannot parse github url ' + githubUrl)\n    }\n    return parsedUrl\n}\n\nexport const sleep = (ms) => new Promise((r) => setTimeout(r, ms))\n\nexport function catchAll(fun) {\n    return async (...args) => {\n        try {\n            return await fun(...args)\n        } catch (e) {\n            console.log()\n            printRed(e)\n            if (process.env.DEBUG) console.log(e)\n            process.exit(1)\n        }\n    }\n}\n"
  },
  {
    "path": "tests/init.js",
    "content": "const { config } = require('dotenv')\n\nconfig({ path: 'test.env', })\n// require('ts-node/register')\nrequire('sucrase/register')\n"
  },
  {
    "path": "tests/simple.ts",
    "content": "import { getRepoInfo, getLastCommit } from '../src/fetch'\nimport { strict as assert } from 'assert'\nimport { Octokit } from '@octokit/rest'\n\nit('ready', () => {\n    assert.ok(true)\n})\nit('getLastCommit', async () => {\n    const cwd = process.cwd()\n    const res = await getLastCommit({\n        ...await getRepoInfo(cwd),\n        cwd,\n        octokit: new Octokit(),\n    })\n    console.log(res)\n})\n"
  },
  {
    "path": "tests/spinner.ts",
    "content": "import ora from 'ora'\nimport Multispinner from 'multispinner'\nimport { sleep, printRed } from '../src/support'\nimport { addSpinner } from '../src/fetch'\n\nit('spinner', async () => {\n    let spinner = ora('xxx').start()\n    await sleep(1000)\n    spinner.info()\n    spinner.start('ciao')\n    await sleep(1000)\n    spinner.info()\n    spinner.start('bye')\n})\nit('multispinner', async () => {\n    let xs = new Multispinner({ a: 'x', b: 'y' }, {})\n    await sleep(1000)\n    xs.success('a')\n    await sleep(1000)\n})\nit('addSpinner', async () => {\n    let xs = new Multispinner({ a: 'x', b: 'y' }, {})\n    await sleep(1000)\n    xs.success('a')\n    addSpinner({ spinners: xs, key: 'z', value: 'z' })\n    await sleep(1000)\n    printRed('ciao')\n})\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"target\": \"es5\",\n        \"module\": \"commonjs\",\n        \"moduleResolution\": \"Node\",\n        \"lib\": [\n            \"es2017\",\n            \"es7\",\n            \"es6\"\n            // \"dom\"\n        ],\n        \"declaration\": true,\n        \"outDir\": \"dist\",\n        \"strict\": false,\n        \"esModuleInterop\": true,\n        \"noImplicitAny\": false,\n        \"jsx\": \"react\",\n        \"sourceMap\": true,\n        \"skipLibCheck\": true\n    },\n    \"exclude\": [\"node_modules\", \"dist\", \"esm\", \"example\", \"tests\"]\n}\n"
  }
]