diff --git a/README.md b/README.md index a7ea02c..09314e2 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,6 @@ npx gitarist setup - comment on issues - Run cron job using `Github Action` -## Contribution - -[Contribution guide](./CONTRIBUTING.md) - ## Contributing 1. [Fork it](https://help.github.com/articles/fork-a-repo/) diff --git a/src/cli.ts b/src/cli.ts index ca3f707..159778b 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -11,12 +11,15 @@ import { StartCommandOptions, branchPrefixes, } from './github'; +import { CreateEnvExampleOptions, createEnvExample } from './libs'; const program = new Command().name(name).description(description).version(version); program .command('setup') - .description('setup') + .description( + 'It sets up gitarist suite. It will create a new GitHub workflow file and `.env` file, adds environment variables to .env file, and opens a browser to create a new GitHub token.', + ) .addOption(new Option('--remote ', 'the name of remote').default(DEFAULT.remote)) .action(async (options: SetupCommandOptions) => { await Gitarist.setup({ remote: options.remote }); @@ -24,7 +27,9 @@ program program .command('start') - .description('start gitarist suite') + .description( + 'It starts gitarist suite. It simulates an active user on a GitHub repository to create issues, commits, create a pull request, and merge it.', + ) .addOption(new Option('-o,--owner ', 'Repository owner').env('GITHUB_OWNER')) .addOption(new Option('-r,--repo ', 'GitHub repository').env('GITHUB_REPO')) .addOption( @@ -66,7 +71,9 @@ program new Option('--stale ', 'A number of days before closing an issue').default(DEFAULT.stale), ) .action(async (options: Partial) => { - dotenv.config({ path: '.env' }); + ['.env'].forEach((file) => { + dotenv.config({ path: file }); + }); options = { ...options, @@ -106,7 +113,6 @@ program repo: validOptions.repo, token: validOptions.token, }); - await gitarist.simulateActiveUser({ mainBranch: validOptions.mainBranch, maxCommits: validOptions.maxCommits, @@ -119,4 +125,31 @@ program }); }); +program + .command('env-example') + .description('Create an example of .env file based on the current .env file(s)') + .addOption( + new Option( + '-f,--filename ', + 'Read given env file such as .env.local, .env.test etc.', + ).default('.env'), + ) + .addOption(new Option('-c,--comments', 'Preserve comments').default(true)) + .addOption(new Option('-m,--merge', 'Merge all env files into one').default(true)) + .action(async (options: CreateEnvExampleOptions) => { + const validOptions = z + .object({ + comments: z.boolean(), + filename: z + .string() + .min(1) + .refine((arg) => arg !== '.env.example', { + message: 'filename should not be .env.example', + }), + merge: z.boolean(), + }) + .parse(options); + await createEnvExample(validOptions); + }); + program.parse(); diff --git a/src/libs/env-example.spec.ts b/src/libs/env-example.spec.ts new file mode 100644 index 0000000..96c48d7 --- /dev/null +++ b/src/libs/env-example.spec.ts @@ -0,0 +1,75 @@ +describe('env-example', () => { + it('should create an example .env file', async () => { + // // Arrange + // const filename = '.env.sample'; + // const comments = true; + // const merge = true; + // const expectedOutput = `# GITHUB_OWNER= + // GITHUB_REPO= + // GITHUB_TOKEN= + // `; + // const fs = { + // readFileSync: jest.fn().mockReturnValue(`GITHUB_OWNER= + // GITHUB_REPO + // GITHUB_TOKEN + // `), + // writeFileSync: jest.fn(), + // }; + // const path = { + // join: jest.fn().mockReturnValue('.env'), + // }; + // const process = { + // cwd: jest.fn().mockReturnValue('/path/to/cwd'), + // }; + // await createEnvExample({ filename, comments, merge }); + // expect(fs.readFileSync).toHaveBeenCalledWith('/path/to/cwd/.env', 'utf8'); + // expect(fs.writeFileSync).toHaveBeenCalledWith('/path/to/cwd/.env.example', expectedOutput, { + // encoding: 'utf-8', + // flag: 'w+', + // }); + }); + + afterAll(async () => { + // execSync('rm -f .env.invalid'); + // execSync('rm -f .env.valid'); + }); + + it('should throw an error if a line does not have a valid config', async () => { + const envContent = `GITHUB_OWNER="foo" +GITHUB_REPO="bar" +GITHUB_REPO="bar" +GITHUB_REPO="bar" +GITHUB_REPO="bar" +GITHUB_TOKEN="baz" + +######################################################## + +GITLAB_HOST="foo" +GITLAB_HOST="foo" +GITLAB_HOST="foo" +GITLAB_HOST="foo" +GITLAB_HOST="foo" +GITLAB_TOKEN="bar" +# project token +GITLAB_TOKEN="baz" +GITLAB_PROJECT_ID="1234" + +######################################################## + +# Profile > Personal Access Tokens > Create API Token +JIRA_TOKEN="foo" +JIRA_PROJECT_KEY="bar" +JIRA_HOST="baz" +JIRA_HOST="baz" +JIRA_HOST="baz" +JIRA_HOST="baz" +`; + // execSync(`echo ${envContent} > .env.valid`); + + // createEnvExample({ + // filename: ' .env.valid', + // comments: true, + // merge: true, + // }); + }); +}); diff --git a/src/libs/env-example.ts b/src/libs/env-example.ts new file mode 100644 index 0000000..08edff3 --- /dev/null +++ b/src/libs/env-example.ts @@ -0,0 +1,44 @@ +import fs from 'fs'; +import path from 'path'; + +export type CreateEnvExampleOptions = { + filename: string; + comments: boolean; + merge: boolean; +}; + +export async function createEnvExample(options: CreateEnvExampleOptions) { + const keySet = new Set(); + const output = fs + .readFileSync(path.join(process.cwd(), options.filename), 'utf8') + .split('\r') + .join('') + .split('\n') + .map((line) => line.trim()) + .map((line, index) => { + if (line === '') { + return ''; + } + if (line.startsWith('#')) { + return options.comments ? line : null; + } + if (line.indexOf('=') === -1) { + throw new Error(`Line ${index} does not have a valid config (i.e. no equals sign).`); + } + const key = line.split('=')[0]; + if (options.merge && keySet.has(key)) { + return null; + } else { + keySet.add(key); + return key + '='; + } + }) + .filter((line) => line !== null) + .join('\n'); + + fs.writeFileSync(path.join(process.cwd(), '.env.example'), output, { + encoding: 'utf-8', + flag: 'w+', + }); + console.log('✨ .env.example successfully generated.'); +} diff --git a/src/libs/index.ts b/src/libs/index.ts new file mode 100644 index 0000000..6af2fd0 --- /dev/null +++ b/src/libs/index.ts @@ -0,0 +1 @@ +export * from './env-example';