From 619eb9dac1049232ed0a01df82cac1ea41e98511 Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Sun, 3 Nov 2024 13:33:21 +0900 Subject: [PATCH] test: improve reliability --- .github/workflows/test.yml | 8 ++-- package.json | 2 +- pnpm-lock.yaml | 10 ++--- src/index.ts | 17 ++++---- tests/index.ts | 81 ++++++++++---------------------------- tests/utils/create-git.ts | 33 ++++++++++++++++ tests/utils/git-publish.ts | 16 ++++++++ 7 files changed, 88 insertions(+), 79 deletions(-) create mode 100644 tests/utils/create-git.ts create mode 100644 tests/utils/git-publish.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e876f3..ba0c295 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [master, develop] +permissions: + contents: write + jobs: test: name: Test @@ -34,11 +37,6 @@ jobs: - name: Type check run: pnpm type-check - - name: Setup Git # Only used by the last test - run: | - git config --global user.name "GitHub Actions" - git config --global user.email "<>" - - name: Build run: pnpm build diff --git a/package.json b/package.json index caf4afb..eb290ee 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "clean-pkg-json": "^1.2.0", "cleye": "^1.3.2", "execa": "^8.0.1", - "fs-fixture": "^2.5.0", + "fs-fixture": "^2.6.0", "lintroll": "^1.10.0", "manten": "^1.3.0", "npm-packlist": "^5.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40ccc50..bd8f9c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,8 +28,8 @@ importers: specifier: ^8.0.1 version: 8.0.1 fs-fixture: - specifier: ^2.5.0 - version: 2.5.0 + specifier: ^2.6.0 + version: 2.6.0 lintroll: specifier: ^1.10.0 version: 1.10.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.12.2(eslint@9.14.0)(typescript@5.6.3))(eslint@9.14.0))(typescript@5.6.3) @@ -1213,8 +1213,8 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} - fs-fixture@2.5.0: - resolution: {integrity: sha512-AzCSCaChZORdNcSa9w9IBnGWhDP2Bv/lOso0P9mAl5nJw2s7pjSEoDaHtzZ9VGIHQlSXR2fuTPO7ln1dM4HTNQ==} + fs-fixture@2.6.0: + resolution: {integrity: sha512-XQNHBGYgth08BSgThjQNrUuzE9XUmr7CDgGFStKaAeB9Oq+kxUHd1zS6hp1YO11B501Sjv9Jq+B2VFn+CdwmIg==} engines: {node: '>=18.0.0'} fs.realpath@1.0.0: @@ -3658,7 +3658,7 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fs-fixture@2.5.0: {} + fs-fixture@2.6.0: {} fs.realpath@1.0.0: {} diff --git a/src/index.ts b/src/index.ts index b5acb63..76f9010 100644 --- a/src/index.ts +++ b/src/index.ts @@ -78,6 +78,15 @@ const { stringify } = JSON; const localTemporaryBranch = `git-publish/${publishBranch}-${Date.now()}`; let success = false; + // Validate remote exists + let remoteUrl; + try { + const getRemoteUrl = await execa('git', ['remote', 'get-url', remote]); + remoteUrl = getRemoteUrl.stdout.trim(); + } catch { + throw new Error(`Git remote ${stringify(remote)} does not exist`); + } + // In the try-finally block in case it modifies the working tree // On failure, they will be reverted by the hard reset try { @@ -258,14 +267,6 @@ const { stringify } = JSON; } if (success) { - let remoteUrl = remote; - - // If the "remote" flag contains an alias, resolve it to a URL - try { - const { stdout } = await execa('git', ['remote', 'get-url', remoteUrl]); - remoteUrl = stdout.trim(); - } catch {} - const parsedGitUrl = remoteUrl.match(/github\.com:(.+)\.git$/); if (parsedGitUrl) { diff --git a/tests/index.ts b/tests/index.ts index b9ff609..da7cb0e 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -1,100 +1,61 @@ -import path from 'path'; -import { execa, type Options } from 'execa'; +import path from 'node:path'; +import fs from 'node:fs/promises'; import { describe, expect } from 'manten'; import { createFixture } from 'fs-fixture'; - -const gitPublish = path.resolve('./dist/index.js'); - -const createGit = async (cwd: string) => { - const git = ( - command: string, - args?: string[], - options?: Options, - ) => ( - execa( - 'git', - [command, ...(args || [])], - { - cwd, - ...options, - }, - ) - ); - - await git( - 'init', - [ - // In case of different default branch name - '--initial-branch=master', - ], - ); - - await git('config', ['user.name', 'name']); - await git('config', ['user.email', 'email']); - - return git; -}; +import { createGit } from './utils/create-git.js'; +import { gitPublish } from './utils/git-publish.js'; describe('git-publish', ({ describe, test }) => { describe('Error cases', ({ test }) => { test('Fails if not in git repository', async () => { - const fixture = await createFixture(); + await using fixture = await createFixture(); - const gitPublishProcess = await execa(gitPublish, { - cwd: fixture.path, - reject: false, - }); + const gitPublishProcess = await gitPublish(fixture.path); expect(gitPublishProcess.exitCode).toBe(1); expect(gitPublishProcess.stderr).toBe('Error: Not in a git repository'); - - await fixture.rm(); }); test('Fails if no package.json found', async () => { - const fixture = await createFixture(); + await using fixture = await createFixture(); await createGit(fixture.path); - const gitPublishProcess = await execa(gitPublish, { - cwd: fixture.path, - reject: false, - }); + const gitPublishProcess = await gitPublish(fixture.path); expect(gitPublishProcess.exitCode).toBe(1); expect(gitPublishProcess.stderr).toBe('Error: No package.json found in current working directory'); - - await fixture.rm(); }); test('Dirty working tree', async () => { - const fixture = await createFixture({ + await using fixture = await createFixture({ 'package.json': '{}', }); const git = await createGit(fixture.path); await git('add', ['package.json']); - const gitPublishProcess = await execa(gitPublish, { - cwd: fixture.path, - reject: false, - }); + const gitPublishProcess = await gitPublish(fixture.path); expect(gitPublishProcess.exitCode).toBe(1); expect(gitPublishProcess.stderr).toBe('Error: Working tree is not clean'); - - await fixture.rm(); }); }); test('Publishes', async ({ onTestFail }) => { - // Requires git config author to be set - // This is set on the GitHub Action because locally, an author already exists - - const gitPublishProcess = await execa(gitPublish, { - reject: false, + const fixture = await createFixture(process.cwd(), { + templateFilter: cpPath => !( + cpPath.endsWith(`${path.sep}node_modules`) + || path.basename(cpPath) === '.git' + || path.basename(cpPath) === 'dist' + ), }); + await fs.symlink(path.resolve('node_modules'), fixture.getPath('node_modules'), 'dir'); + await fs.symlink(path.resolve('.git'), fixture.getPath('.git'), 'dir'); + + const gitPublishProcess = await gitPublish(fixture.path); + onTestFail(() => { console.log(gitPublishProcess); }); diff --git a/tests/utils/create-git.ts b/tests/utils/create-git.ts new file mode 100644 index 0000000..259955c --- /dev/null +++ b/tests/utils/create-git.ts @@ -0,0 +1,33 @@ +import { execa, type Options } from 'execa'; + +export const createGit = async ( + cwd: string, +) => { + const git = ( + command: string, + args?: string[], + options?: Options, + ) => ( + execa( + 'git', + [command, ...(args || [])], + { + cwd, + ...options, + }, + ) + ); + + await git( + 'init', + [ + // In case of different default branch name + '--initial-branch=master', + ], + ); + + await git('config', ['user.name', 'name']); + await git('config', ['user.email', 'email']); + + return git; +}; diff --git a/tests/utils/git-publish.ts b/tests/utils/git-publish.ts new file mode 100644 index 0000000..ad845e8 --- /dev/null +++ b/tests/utils/git-publish.ts @@ -0,0 +1,16 @@ +import path from 'node:path'; +import { execa } from 'execa'; + +const gitPublishPath = path.resolve('./dist/index.js'); + +export const gitPublish = ( + cwd: string, +) => execa(gitPublishPath, { + cwd, + reject: false, + // Remove CI env var which prevents Ink from rendering + env: { + PATH: process.env.PATH, + }, + extendEnv: false, +});