diff --git a/README.md b/README.md index 5b1125e..5c1fa72 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Note: The `GH_TOKEN` environment variable is **required** for GitHub API request | `files` | **YES** | Multi-line string of file paths to be committed, relative to the current workspace.| | `workspace` | **NO** | Directory containing files to be committed. **DEFAULT:** GitHub workspace directory (root of the repository). | | `commit-message` | **YES** | Commit message for the file changes. | +| `repository` | **NO** | Repository name including owner (e.g. owner/repo). **DEFAULT:** Workflow triggered repository | | `branch-name` | **NO** | Branch to commit, it must already exist in the remote. **DEFAULT:** Workflow triggered branch | | `branch-push-force` | **NO** | `--force` flag when running `git push `. | | `tag` | **NO** | Push tag for the new/current commit. | diff --git a/__tests__/git.test.ts b/__tests__/git.test.ts index 4182e9b..560c398 100644 --- a/__tests__/git.test.ts +++ b/__tests__/git.test.ts @@ -11,7 +11,7 @@ import { describe('Git CLI', () => { beforeEach(() => { - jest.clearAllMocks() + jest.restoreAllMocks() }) describe('git checkout', () => { @@ -89,11 +89,12 @@ describe('Git CLI', () => { }) describe('git push', () => { + beforeEach(() => { + jest.spyOn(core, 'getBooleanInput').mockReturnValue(false) + }) + it('should push new branch', async () => { const execMock = jest.spyOn(exec, 'exec').mockResolvedValue(0) - const getInput = jest - .spyOn(core, 'getBooleanInput') - .mockReturnValue(false) await pushCurrentBranch() expect(execMock).toHaveBeenCalledWith( diff --git a/__tests__/github/client.test.ts b/__tests__/github/client.test.ts index 1e7279e..c1816f0 100644 --- a/__tests__/github/client.test.ts +++ b/__tests__/github/client.test.ts @@ -12,7 +12,7 @@ describe('GitHub Client', () => { let replacedEnv: jest.Replaced | undefined beforeEach(() => { - jest.clearAllMocks() + jest.restoreAllMocks() replacedEnv = jest.replaceProperty( process, 'env', diff --git a/__tests__/github/graphql.test.ts b/__tests__/github/graphql.test.ts index b743ca8..264dc48 100644 --- a/__tests__/github/graphql.test.ts +++ b/__tests__/github/graphql.test.ts @@ -22,7 +22,7 @@ import { describe('GitHub API', () => { beforeEach(() => { - jest.clearAllMocks() + jest.restoreAllMocks() fetchMock.clearHistory() fetchMock.removeRoutes() }) diff --git a/__tests__/github/repo.test.ts b/__tests__/github/repo.test.ts index dc8e1e5..0ed245c 100644 --- a/__tests__/github/repo.test.ts +++ b/__tests__/github/repo.test.ts @@ -5,13 +5,13 @@ import { getContext } from '../../src/github/repo' describe('getContext', () => { beforeEach(() => { - jest.clearAllMocks() - }) - - it('extract owner and repo', () => { + jest.restoreAllMocks() jest .spyOn(github.context, 'repo', 'get') .mockReturnValue({ repo: 'my-repo', owner: 'my-user' }) + }) + + it('extract owner and repo', () => { jest.replaceProperty(github.context, 'ref', 'refs/heads/main') const context = getContext() expect(context).toHaveProperty('owner', 'my-user') diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 6ab6674..70b9410 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -14,7 +14,7 @@ import exp from 'constants' describe('action', () => { beforeEach(() => { - jest.clearAllMocks() + jest.restoreAllMocks() jest.spyOn(core, 'debug').mockReturnValue() jest.spyOn(core, 'info').mockReturnValue() jest.spyOn(core, 'group').mockImplementation(async (name, fn) => { @@ -125,7 +125,7 @@ describe('action', () => { describe('input branch same as current branch', () => { beforeEach(() => { - jest.spyOn(core, 'getInput').mockImplementationOnce((name, option) => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { if (name == 'branch-name') return 'main' return '' }) @@ -140,7 +140,9 @@ describe('action', () => { await main.run() expect(switchBranchMock).not.toHaveBeenCalled() - expect(setFailedMock).not.toHaveBeenCalled() + expect(setFailedMock).toHaveBeenCalledWith( + 'Neither files nor tag input has been configured' + ) }) it('does not push branch', async () => { @@ -156,13 +158,15 @@ describe('action', () => { expect(switchBranchMock).not.toHaveBeenCalled() expect(pushBranchMock).not.toHaveBeenCalled() - expect(setFailedMock).not.toHaveBeenCalled() + expect(setFailedMock).toHaveBeenCalledWith( + 'Neither files nor tag input has been configured' + ) }) }) describe('input branch not the same as current branch', () => { beforeEach(() => { - jest.spyOn(core, 'getInput').mockImplementationOnce((name, option) => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { if (name == 'branch-name') return 'another-branch' return '' }) @@ -195,7 +199,45 @@ describe('action', () => { expect(switchBranchMock).toHaveBeenCalled() expect(pushBranchMock).toHaveBeenCalled() - expect(setFailedMock).not.toHaveBeenCalled() + expect(setFailedMock).toHaveBeenCalledWith( + 'Neither files nor tag input has been configured' + ) + }) + }) + + describe('input repository is given', () => { + describe('valid format', () => { + beforeEach(() => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { + if (name == 'repository') return 'the-user/the-repo' + return '' + }) + }) + + it('succeed', async () => { + const setFailedMock = jest.spyOn(core, 'setFailed').mockReturnValue() + await main.run() + expect(setFailedMock).toHaveBeenCalledWith( + 'Neither files nor tag input has been configured' + ) + }) + }) + + describe('invalid format', () => { + beforeEach(() => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { + if (name == 'repository') return 'the-user-the-repo' + return '' + }) + }) + + it('fails', async () => { + const setFailedMock = jest.spyOn(core, 'setFailed').mockReturnValue() + await main.run() + expect(setFailedMock).toHaveBeenCalledWith( + 'Input "the-user-the-repo" is invalid' + ) + }) }) }) @@ -210,10 +252,11 @@ describe('action', () => { describe('exists in remote', () => { beforeEach(() => { - jest.spyOn(core, 'getInput').mockImplementationOnce((name, option) => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { if (name == 'branch-name') return 'existing-branch' return '' }) + jest.spyOn(core, 'getBooleanInput').mockReturnValue(true) }) it('succeed', async () => { @@ -246,7 +289,7 @@ describe('action', () => { describe('does not exist in remote', () => { beforeEach(() => { - jest.spyOn(core, 'getInput').mockImplementationOnce((name, option) => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { if (name == 'branch-name') return 'new-branch' return '' }) @@ -355,7 +398,7 @@ describe('action', () => { }) it('commit files and output commit sha', async () => { - jest.spyOn(core, 'getInput').mockImplementationOnce((name, option) => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { if (name == 'branch-name') return 'custom-branch' return '' }) @@ -404,7 +447,7 @@ describe('action', () => { }) it('push tag only', async () => { - jest.spyOn(core, 'getInput').mockImplementation((name, option) => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { if (name == 'branch-name') return 'tag-branch' if (name == 'tag') return 'fake-tag' return '' @@ -450,7 +493,7 @@ describe('action', () => { }) it('commit file and push tag', async () => { - jest.spyOn(core, 'getInput').mockImplementation((name, option) => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { if (name == 'branch-name') return 'file-tag-branch' if (name == 'tag') return 'fake-file-tag' return '' @@ -507,7 +550,7 @@ describe('action', () => { }) it('commit file fails woukd skip push tag', async () => { - jest.spyOn(core, 'getInput').mockImplementationOnce((name, option) => { + jest.spyOn(core, 'getInput').mockImplementation((name, _option) => { if (name == 'branch-name') return 'file-fail-tag-branch' if (name == 'tag') return 'unreachable-tag' return '' diff --git a/__tests__/utils/cwd.test.ts b/__tests__/utils/cwd.test.ts index 3cf3e91..205da5c 100644 --- a/__tests__/utils/cwd.test.ts +++ b/__tests__/utils/cwd.test.ts @@ -11,7 +11,7 @@ import { getCwd, getWorkspace } from '../../src/utils/cwd' describe('Current Working Directory', () => { beforeEach(() => { - jest.clearAllMocks() + jest.restoreAllMocks() jest.spyOn(core, 'debug').mockReturnValue() }) @@ -26,7 +26,7 @@ describe('Current Working Directory', () => { describe('Current Workspace', () => { beforeEach(() => { - jest.clearAllMocks() + jest.restoreAllMocks() jest.spyOn(core, 'debug').mockReturnValue() }) diff --git a/action.yml b/action.yml index d3c9df1..67225ee 100644 --- a/action.yml +++ b/action.yml @@ -19,6 +19,11 @@ inputs: description: | Commit message for the file changes. required: false + repository: + description: | + Repository name including owner (e.g. owner/repo). Default: Workflow triggered repository. + required: false + default: '' branch-name: description: | Branch to commit to. Default: Workflow triggered branch. diff --git a/dist/index.js b/dist/index.js index 258dd67..5f09fa0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -29910,13 +29910,19 @@ function getBlob(filePath) { Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.BranchCommitNotFound = exports.BranchNotFound = exports.InputBranchNotFound = exports.NoFileChanges = void 0; +exports.BranchCommitNotFound = exports.BranchNotFound = exports.InputBranchNotFound = exports.InputRepositoryInvalid = exports.NoFileChanges = void 0; class NoFileChanges extends Error { constructor() { super('No files changes'); } } exports.NoFileChanges = NoFileChanges; +class InputRepositoryInvalid extends Error { + constructor(repository) { + super(`Input "${repository}" is invalid`); + } +} +exports.InputRepositoryInvalid = InputRepositoryInvalid; class InputBranchNotFound extends Error { constructor(branchName) { super(`Input "${branchName}" not found`); @@ -30388,15 +30394,22 @@ function run() { var _a, _b, _c, _d, _e, _f; try { const { owner, repo, branch } = (0, repo_1.getContext)(); + const inputRepository = (0, input_1.getInput)('repository'); const inputBranch = (0, input_1.getInput)('branch-name'); if (inputBranch && inputBranch !== branch) { yield (0, git_1.switchBranch)(inputBranch); yield (0, git_1.pushCurrentBranch)(); } + const repositoryParts = inputRepository ? inputRepository.split('/') : []; + if (repositoryParts.length && repositoryParts.length != 2) { + throw new errors_1.InputRepositoryInvalid(inputRepository); + } + const currentOwner = repositoryParts.length ? repositoryParts[0] : owner; + const currentRepository = repositoryParts.length ? repositoryParts[1] : repo; const currentBranch = inputBranch ? inputBranch : branch; - const repository = yield core.group(`fetching repository info for owner: ${owner}, repo: ${repo}, branch: ${currentBranch}`, () => __awaiter(this, void 0, void 0, function* () { + const repository = yield core.group(`fetching repository info for owner: ${currentOwner}, repo: ${currentRepository}, branch: ${currentBranch}`, () => __awaiter(this, void 0, void 0, function* () { const startTime = Date.now(); - const repositoryData = yield (0, graphql_1.getRepository)(owner, repo, currentBranch); + const repositoryData = yield (0, graphql_1.getRepository)(currentOwner, currentRepository, currentBranch); const endTime = Date.now(); core.debug(`time taken: ${(endTime - startTime).toString()} ms`); return repositoryData; diff --git a/src/errors.ts b/src/errors.ts index 4bee9f4..4d5c558 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -4,6 +4,12 @@ export class NoFileChanges extends Error { } } +export class InputRepositoryInvalid extends Error { + constructor(repository: string) { + super(`Input "${repository}" is invalid`) + } +} + export class InputBranchNotFound extends Error { constructor(branchName: string) { super(`Input "${branchName}" not found`) diff --git a/src/main.ts b/src/main.ts index 4014982..8731e3c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,23 +18,37 @@ import { NoFileChanges, BranchNotFound, BranchCommitNotFound, + InputRepositoryInvalid, InputBranchNotFound, } from './errors' export async function run(): Promise { try { const { owner, repo, branch } = getContext() + const inputRepository = getInput('repository') const inputBranch = getInput('branch-name') if (inputBranch && inputBranch !== branch) { await switchBranch(inputBranch) await pushCurrentBranch() } + + const repositoryParts = inputRepository ? inputRepository.split('/') : [] + if (repositoryParts.length && repositoryParts.length != 2) { + throw new InputRepositoryInvalid(inputRepository) + } + + const currentOwner = repositoryParts.length ? repositoryParts[0] : owner + const currentRepository = repositoryParts.length ? repositoryParts[1] : repo const currentBranch = inputBranch ? inputBranch : branch const repository = await core.group( - `fetching repository info for owner: ${owner}, repo: ${repo}, branch: ${currentBranch}`, + `fetching repository info for owner: ${currentOwner}, repo: ${currentRepository}, branch: ${currentBranch}`, async () => { const startTime = Date.now() - const repositoryData = await getRepository(owner, repo, currentBranch) + const repositoryData = await getRepository( + currentOwner, + currentRepository, + currentBranch + ) const endTime = Date.now() core.debug(`time taken: ${(endTime - startTime).toString()} ms`) return repositoryData