Skip to content

Commit

Permalink
feat: find-project action
Browse files Browse the repository at this point in the history
  • Loading branch information
dsanders11 committed Nov 2, 2023
1 parent 62650d5 commit 4ec05f4
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 44 deletions.
21 changes: 19 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,24 @@ jobs:
owner: ${{ steps.copy-project.outputs.owner }}
project-number: ${{ steps.copy-project.outputs.number }}
readme: This is the readme
title: New Title
title: New Title ${{ steps.copy-project.outputs.number }}
token: ${{ steps.get-auth-token.outputs.token }}

- name: Find Project
uses: ./find-project/
id: find-project
with:
owner: ${{ matrix.owner }}
title: New Title ${{ steps.copy-project.outputs.number }}
token: ${{ steps.get-auth-token.outputs.token }}

- name: Confirm Project Found
uses: ./github-script/
with:
script: |
if ("${{ steps.copy-project.outputs.number }}" !== "${{ steps.find-project.outputs.number }}") {
throw new Error("Could not find project by title")
}
token: ${{ steps.get-auth-token.outputs.token }}

- name: Get Project
Expand All @@ -281,7 +298,7 @@ jobs:
uses: ./github-script/
with:
script: |
if ("${{ steps.get-project.outputs.title }}" !== "New Title") {
if ("${{ steps.get-project.outputs.title }}" !== "New Title ${{ steps.copy-project.outputs.number }}") {
throw new Error("Project title is not correct")
}
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ private repositories the PAT must also have the `repo` scope.
| [`project-actions/delete-project`](delete-project) | Delete a project |
| [`project-actions/edit-item`](edit-item) | Edit an item on a project |
| [`project-actions/edit-project`](edit-project) | Edit a project |
| [`project-actions/find-project`](find-project) | Find a project |
| [`project-actions/get-item`](get-item) | Get an item on a project |
| [`project-actions/get-project`](get-project) | Get a project |
| [`project-actions/github-script`](github-script) | Modify projects programmatically |
Expand Down
123 changes: 123 additions & 0 deletions __tests__/find-project.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import * as core from '@actions/core';

import * as index from '../src/find-project';
import { findProject } from '../src/lib';
import { mockGetInput } from './utils';

jest.mock('@actions/core');
jest.mock('../src/lib');

// Spy the action's entrypoint
const findProjectActionSpy = jest.spyOn(index, 'findProjectAction');

const owner = 'dsanders11';
const projectNumber = '94';
const projectId = 'project-id';
const fieldCount = 4;
const itemCount = 50;
const shortDescription = 'Description';
const title = 'My Title';
const readme = 'README';
const url = 'url';

describe('findProjectAction', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('requires the title input', async () => {
mockGetInput({ owner });

await index.findProjectAction();
expect(findProjectActionSpy).toHaveReturned();

expect(core.setFailed).toHaveBeenCalledTimes(1);
expect(core.setFailed).toHaveBeenLastCalledWith(
'Input required and not supplied: title'
);
});

it('handles project not found', async () => {
mockGetInput({ owner, title });
jest.mocked(findProject).mockResolvedValue(null);

await index.findProjectAction();
expect(findProjectActionSpy).toHaveReturned();

expect(core.setFailed).toHaveBeenCalledTimes(1);
expect(core.setFailed).toHaveBeenLastCalledWith(
`Project not found: ${title}`
);
});

it('handles generic errors', async () => {
mockGetInput({ owner, title });
jest.mocked(findProject).mockImplementation(() => {
throw new Error('Server error');
});

await index.findProjectAction();
expect(findProjectActionSpy).toHaveReturned();

expect(core.setFailed).toHaveBeenCalledTimes(1);
expect(core.setFailed).toHaveBeenLastCalledWith('Server error');
});

it('stringifies non-errors', async () => {
mockGetInput({ owner, title });
jest.mocked(findProject).mockImplementation(() => {
throw 42; // eslint-disable-line no-throw-literal
});

await index.findProjectAction();
expect(findProjectActionSpy).toHaveReturned();

expect(core.setFailed).toHaveBeenCalledTimes(1);
expect(core.setFailed).toHaveBeenLastCalledWith('42');
});

it('sets output', async () => {
mockGetInput({ owner, title });
jest.mocked(findProject).mockResolvedValue({
id: projectId,
number: parseInt(projectNumber),
fields: {
totalCount: fieldCount
},
items: {
totalCount: itemCount
},
url,
title,
readme,
shortDescription,
public: true,
closed: false,
owner: {
type: 'Organization',
login: owner
}
});

await index.findProjectAction();
expect(findProjectActionSpy).toHaveReturned();

expect(core.setOutput).toHaveBeenCalledTimes(10);
expect(core.setOutput).toHaveBeenCalledWith('id', projectId);
expect(core.setOutput).toHaveBeenCalledWith('url', url);
expect(core.setOutput).toHaveBeenCalledWith('closed', false);
expect(core.setOutput).toHaveBeenCalledWith('public', true);
expect(core.setOutput).toHaveBeenCalledWith('field-count', fieldCount);
expect(core.setOutput).toHaveBeenCalledWith('item-count', itemCount);
expect(core.setOutput).toHaveBeenCalledWith(
'number',
parseInt(projectNumber)
);
expect(core.setOutput).toHaveBeenCalledWith('readme', readme);
expect(core.setOutput).toHaveBeenCalledWith(
'description',
shortDescription
);
expect(core.setOutput).toHaveBeenCalledWith('title', title);
});
});
23 changes: 23 additions & 0 deletions __tests__/lib.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('lib', () => {
const owner = 'dsanders11';
const projectId = 'project-id';
const projectNumber = '41';
const projectTitle = 'My Cool Project';

beforeEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -1166,6 +1167,28 @@ describe('lib', () => {
});
});

describe('findProject', () => {
const project = { id: projectId, title: projectTitle };

it('handles project not found', async () => {
jest
.mocked(execCliCommand)
.mockResolvedValue(JSON.stringify({ projects: [project] }));
await expect(
lib.findProject(owner, 'A Different Title')
).resolves.toEqual(null);
});

it('returns project details', async () => {
jest
.mocked(execCliCommand)
.mockResolvedValue(JSON.stringify({ projects: [project] }));
await expect(lib.findProject(owner, projectTitle)).resolves.toEqual(
project
);
});
});

describe('getProject', () => {
it('handles project not found', async () => {
mockProjectNotFoundError();
Expand Down
31 changes: 4 additions & 27 deletions dist/find-project.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 8 additions & 15 deletions dist/github-script/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions find-project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# `project-actions/find-project`

[![Release](https://img.shields.io/github/v/release/dsanders11/project-actions?color=blue)](https://github.com/dsanders11/project-actions/releases)

Find a GitHub project

## Inputs

| Name | Description | Required | Default |
|-------------------|----------------------------------------------------|----------|----------------------------------------------|
| `token` | A GitHub access token - either a classic PAT or a GitHub app installation token. | Yes | |
| `owner` | The owner of the project - either an organization or a user. If not provided, it defaults to the repository owner. | No | `${{ github.repository_owner }}` |
| `title` | The title of the project to find. | Yes | |

## Outputs

| Name | Description |
|-------------------|----------------------------------------------------|
| `id` | The global ID for the project. |
| `closed` | The closed state of the project. |
| `field-count` | The number of fields on the project. |
| `item-count` | The number of items in the project. |
| `number` | The project number of the project. |
| `public` | The public visibility of the project. |
| `readme` | The readme description of the project. |
| `description` | The short description of the project. |
| `title` | The title of the project. |
| `url` | The URL of the project. |

## License

MIT
41 changes: 41 additions & 0 deletions find-project/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Find Project
description: Find a GitHub project
author: David Sanders

inputs:
token:
description: A GitHub access token - either a classic PAT or a GitHub app installation token
required: true
owner:
description: The owner of the project - either an organization or a user
required: false
default: ${{ github.repository_owner }}
title:
description: The title of the project to find
required: true

outputs:
id:
description: The global ID for the project
closed:
description: The closed state of the project
field-count:
description: The number of fields on the project
item-count:
description: The number of items in the project
number:
description: The project number of the project
public:
description: The public visibility of the project
readme:
description: The readme description of the project
description:
description: The short description of the project
title:
description: The title of the project
url:
description: The URL of the project

runs:
using: node20
main: ../dist/find-project.js
4 changes: 4 additions & 0 deletions find-project/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { findProjectAction } from '../src/find-project';

// eslint-disable-next-line @typescript-eslint/no-floating-promises
findProjectAction();
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"package:delete-project": "esbuild delete-project/index.ts --bundle --outfile=dist/delete-project.js --platform=node --target=node20.2",
"package:edit-item": "esbuild edit-item/index.ts --bundle --outfile=dist/edit-item.js --platform=node --target=node20.2",
"package:edit-project": "esbuild edit-project/index.ts --bundle --outfile=dist/edit-project.js --platform=node --target=node20.2",
"package:find-project": "esbuild find-project/index.ts --bundle --outfile=dist/find-project.js --platform=node --target=node20.2",
"package:get-item": "esbuild get-item/index.ts --bundle --outfile=dist/get-item.js --platform=node --target=node20.2",
"package:get-project": "esbuild get-project/index.ts --bundle --outfile=dist/get-project.js --platform=node --target=node20.2",
"package:link-project": "esbuild link-project/index.ts --bundle --outfile=dist/link-project.js --platform=node --target=node20.2",
Expand Down
Loading

0 comments on commit 4ec05f4

Please sign in to comment.