Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 69 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# LeanKit Github Actions
These Github Actions provide an easy way to interact with your LeanKit account during your build or deployment lifecycle. For more information on using Github Actions in general, see https://docs.github.com/en/actions.
# AgilePlace Github Actions
These Github Actions provide an easy way to interact with your AgilePlace account during your build or deployment lifecycle. For more information on using Github Actions in general, see https://docs.github.com/en/actions.

To consume, reference this repository, and the action. All available LeanKit Actions are in this repository only. For example: `use: leankit/github-actions/blockCard@v1.4`. See specific examples with input parameters below.
To consume, reference this repository, and the action. All available AgilePlace Actions are in this repository only. For example: `use: leankit/github-actions/blockCard@v1.4`. See specific examples with input parameters below.

## Usage Notes

### API Tokens
All LeanKit Actions require an "API Token". This token is created in your LeanKit app. In the app, click on your user avatar, then select "API Tokens" and follow prompts to create and copy your token. **Keep this token secure!** This api token can be used to access anything in LeanKit to which you have access, and perform actions in your name. Do not store the token in a workflow file directly -- create a 'secret' and store it in the protected secret file. See https://docs.github.com/en/actions/security-guides/encrypted-secrets for specific instructions on creating and using secrets. Consider creating a "service account" in LeanKit with access limited to only the boards necessary to perform the required actions.
All AgilePlace Actions require an "API Token". This token is created in your AgilePlace app. In the app, click on your user avatar, then select "API Tokens" and follow prompts to create and copy your token. **Keep this token secure!** This api token can be used to access anything in AgilePlace to which you have access, and perform actions in your name. Do not store the token in a workflow file directly -- create a 'secret' and store it in the protected secret file. See https://docs.github.com/en/actions/security-guides/encrypted-secrets for specific instructions on creating and using secrets. Consider creating a "service account" in AgilePlace with access limited to only the boards necessary to perform the required actions.

### Error Messages
When any of these actions fail, an error message will be set on the action's output 'error' property. For example, if a 'validateCustomFields' step whose id was 'validation', you could access the error message in a subsequent step (that included the `if: failed()` qualifier) using `${{ steps.validation.outputs.error }}`. We also set an environment variable when one of these actions fails, so the same error would be available at `${{ env.LK_ERROR_MESSAGE }}`.

### Using with LeanKit Card Automation
LeanKit includes automation tools that can trigger a Github Workflow using a `repository_dispatch`. When triggering a workflow, we provide the following as the `client_payload`:
### Using with AgilePlace Card Automation
AgilePlace includes automation tools that can trigger a Github Workflow using a `repository_dispatch`. When triggering a workflow, we provide the following as the `client_payload`:
```json
{
"automation": {
Expand Down Expand Up @@ -64,8 +64,8 @@ Add a comment to a card
#### Input Params
|name|description|required|
|----|-----------|--------|
|host|LeanKit Url (https://mycompany.leankit.com)|yes|
|apiToken|API token with read access to your LeanKit board|yes|
|host|AgilePlace Url (https://mycompany.leankit.com)|yes|
|apiToken|API token with read access to your AgilePlace board|yes|
|cardId|Id of the card|yes|
|comment|Comment text|yes|

Expand All @@ -88,8 +88,8 @@ Block or unblock a card
#### Input Params
|name|description|required|
|----|-----------|--------|
|host|LeanKit Url (https://mycompany.leankit.com)|yes|
|apiToken|API token with read access to your LeanKit board|yes|
|host|AgilePlace Url (https://mycompany.leankit.com)|yes|
|apiToken|API token with read access to your AgilePlace board|yes|
|cardId|Id of the card|yes|
|isBlocked|Whether to block or unblock the card|yes|
|blockReason|Block reason||
Expand All @@ -114,8 +114,8 @@ Create a new card
#### Input Params
|name|description|required|
|----|-----------|--------|
|host|LeanKit Url (https://mycompany.leankit.com)|yes|
|apiToken|API token with write access to your LeanKit board|yes|
|host|AgilePlace Url (https://mycompany.leankit.com)|yes|
|apiToken|API token with write access to your AgilePlace board|yes|
|boardId|Board Id for the new card|yes|
|title|Title of the new card|yes|
|laneId|Optionally specify lane id for the new card. Default drop lane will be used when not set.||
Expand Down Expand Up @@ -144,8 +144,8 @@ Create a new card
#### Input Params
|name|description|required|
|----|-----------|--------|
|host|LeanKit Url (https://mycompany.leankit.com)|yes|
|apiToken|API token for your LeanKit board|yes|
|host|AgilePlace Url (https://mycompany.leankit.com)|yes|
|apiToken|API token for your AgilePlace board|yes|
|cardId|Id of the card|yes|
|laneId|Lane to move the card to|yes|
|wipOverrideComment|WIP Override reason to provide, in case lane is at WIP||
Expand All @@ -166,8 +166,8 @@ Create a new card
#### Input Params
|name|description|required|
|----|-----------|--------|
|host|LeanKit Url (https://mycompany.leankit.com)|yes|
|apiToken|API token for your LeanKit board|yes|
|host|AgilePlace Url (https://mycompany.leankit.com)|yes|
|apiToken|API token for your AgilePlace board|yes|
|cardIds|Comma-separated list of cardIds to update|yes|
|assignUserIds|Comma-separated list of userIds to that will be assigned to the cards||
|unassignUserIds|Comma-separated list of userIds to that will be unassigned from the cards||
Expand All @@ -193,13 +193,13 @@ Fail if specified custom fields do not have a value on a particular card
#### Input Params
|name|description|required|
|----|-----------|--------|
|host|LeanKit Url (https://mycompany.leankit.com)|yes|
|apiToken|API token with read access to your LeanKit board|yes|
|host|AgilePlace Url (https://mycompany.leankit.com)|yes|
|apiToken|API token with read access to your AgilePlace board|yes|
|cardId|Id of the card|yes|
|requiredCustomFields|The labels or ids of the custom fields to validate|yes|
|customFields|Custom fields values, if available already||

Note: the `customFields` input is available to receive custom field information that was either provided in a previous step or received from the event payload. The example below demonstrates how customFields may be set if your workflow was started from a LeanKit 'Trigger Github Action` integration step.
Note: the `customFields` input is available to receive custom field information that was either provided in a previous step or received from the event payload. The example below demonstrates how customFields may be set if your workflow was started from a AgilePlace 'Trigger Github Action` integration step.
#### Example workflow step
```
- name: validate required fields
Expand Down Expand Up @@ -241,6 +241,56 @@ Valid formats that will be identified as a card id:
* error; error message if failed
* cardId; card id identified in the inputText

-----------
### Initiate Board Event
Initiate external automation event on a board
#### Input Params
| name | description | required |
|-----------|---------------------------------------------|----------|
| host | AgilePlace Url (https://mycompany.leankit.com) | yes |
| apiToken | API token for your AgilePlace account | yes |
| boardId | Board Id to initiate the event on | yes |
| eventName | Name of the event to initiate | yes |

#### Example workflow step
```
- name: initiate board event
id: initiateBoardEvent
uses: leankit/github-actions/initiateBoardEvent@v1.4
with:
host: https://YOUR-ACCOUNT.leankit.com/
apiToken: ${{ secrets.MY_API_TOKEN }}
boardId: 123456
eventName: my-event
```
#### Outputs
* error; error message if failed

-----------
### Initiate Card Event
Initiate external automation event on a card
#### Input Params
| name | description | required |
|-----------|---------------------------------------------|----------|
| host | AgilePlace Url (https://mycompany.leankit.com) | yes |
| apiToken | API token for your AgilePlace account | yes |
| cardId | Card Id to initiate the event on | yes |
| eventName | Name of the event to initiate | yes |

#### Example workflow step
```
- name: initiate card event
id: initiateCardEvent
uses: leankit/github-actions/initiateCardEvent@v1.4
with:
host: https://YOUR-ACCOUNT.leankit.com/
apiToken: ${{ secrets.MY_API_TOKEN }}
cardId: 123456
eventName: my-event
```
#### Outputs
* error; error message if failed


## Dev notes
### Running Build on Windows
Expand Down
4 changes: 2 additions & 2 deletions addComment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: "Add a comment to a card"
description: "Add a comment to a card"
inputs:
host:
description: LeanKit Url (https://mycompany.leankit.com)
description: AgilePlace Url (https://mycompany.leankit.com)
required: true
apiToken:
description: Api token with read access to your LeanKit board
description: Api token with read access to your AgilePlace board
required: true
cardId:
description: Id of the card
Expand Down
2 changes: 1 addition & 1 deletion addComment/dist/index.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions assignUsers/action.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: "Assign users to LeanKit cards"
name: "Assign users to AgilePlace cards"
description: "Assign or unassign users to cards"
inputs:
host:
description: LeanKit Url (https://mycompany.leankit.com)
description: AgilePlace Url (https://mycompany.leankit.com)
required: true
apiToken:
description: Api token for your LeanKit board
description: Api token for your AgilePlace board
required: true
cardIds:
description: List of card ids
Expand Down
2 changes: 1 addition & 1 deletion assignUsers/dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assignUsers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ function parseList( list ) {
const { assignUsers } = leankitApiFactory( host, apiToken );
await assignUsers( cardIdList, userIdsToAssign, userIdsToUnassign, wipOverrideComment );
} )().catch( ex => {
reportError( "assignUsers", ex.message );
reportError( "assignUsers", ex );
} );
13 changes: 8 additions & 5 deletions assignUsers/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ describe( "assignUsers", () => {

describe( "validation", () => {
describe( "when validation fails", () => {
const error = new Error( "Input required and not supplied: SOME PARAM ERROR" );
beforeEach( async () => {
init();
getInputParams.throws( new Error( "Input required and not supplied: SOME PARAM" ) );
getInputParams.throws( error);
await action();
} );

Expand All @@ -62,7 +63,7 @@ describe( "assignUsers", () => {
} );

it( "should report error", async () => {
reportError.should.be.calledOnce.and.calledWith( "assignUsers", "Input required and not supplied: SOME PARAM" );
reportError.should.be.calledOnce.and.calledWith( "assignUsers", error );
} );
} );

Expand All @@ -74,15 +75,17 @@ describe( "assignUsers", () => {
} );

it( "should report error", async () => {
reportError.should.be.calledOnce.and.calledWith( "assignUsers", "Either assignUserIds or unassignUserIds must be specified" );
reportError.should.be.calledOnce.and.calledWith( "assignUsers",
sinon.match({ message: "Either assignUserIds or unassignUserIds must be specified" }));
} );
});

describe( "with invalid host", () => {
const error = new Error( "Expected a leankit url for 'host' action parameter" );
beforeEach( async () => {
init();
getInputParams.returns( [ "INVALID_HOST" ] );
validateLeankitUrl.throws( new Error( "Expected a leankit url for 'host' action parameter" ))
validateLeankitUrl.throws( error )
await action();
} );

Expand All @@ -91,7 +94,7 @@ describe( "assignUsers", () => {
} );

it( "should report error", () => {
reportError.should.be.calledOnce.and.calledWith( "assignUsers", "Expected a leankit url for 'host' action parameter" );
reportError.should.be.calledOnce.and.calledWith( "assignUsers", error);
} );
} );
} );
Expand Down
4 changes: 2 additions & 2 deletions blockCard/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: "Block or unblock a card"
description: "Block or unblock a card"
inputs:
host:
description: LeanKit Url (https://mycompany.leankit.com)
description: AgilePlace Url (https://mycompany.leankit.com)
required: true
apiToken:
description: Api token with read access to your LeanKit board
description: Api token with read access to your AgilePlace board
required: true
cardId:
description: Id of the card
Expand Down
2 changes: 1 addition & 1 deletion blockCard/dist/index.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions createCard/action.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: "Create Card"
description: "Create a LeanKit card"
description: "Create an AgilePlace card"
inputs:
host:
description: LeanKit Url (https://mycompany.leankit.com)
description: AgilePlace Url (https://mycompany.leankit.com)
required: true
apiToken:
description: Api token with write access to your LeanKit board
description: Api token with write access to your AgilePlace board
required: true
boardId:
description: Board Id for the new card
Expand Down
2 changes: 1 addition & 1 deletion createCard/dist/index.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions extractCardId/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ inputs:
description: "Text string containing a numeric value"
required: true
outputs:
error:
description: Status message for errors
cardId:
description: Numeric Card Id identified
runs:
Expand Down
2 changes: 1 addition & 1 deletion extractCardId/dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion extractCardId/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ const { getInputParams, setOutput, reportError } = require( "../leankit/helpers"
setOutput( "cardId", matches[ 1 ] );
}
} )().catch( ex => {
reportError( "extractCardId", ex.message );
reportError( "extractCardId", ex );
} );
5 changes: 3 additions & 2 deletions extractCardId/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ describe( "extractCardId", () => {

describe( "validation", () => {
describe( "when validation fails", () => {
const error = new Error( "Input required and not supplied: SOME PARAM" );
beforeEach( async () => {
getInputParams.throws( new Error( "Input required and not supplied: SOME PARAM" ) );
getInputParams.throws( error );
await action();
} );

Expand All @@ -41,7 +42,7 @@ describe( "extractCardId", () => {
} );

it( "should report error", async () => {
reportError.should.be.calledOnce.and.calledWith( "extractCardId", "Input required and not supplied: SOME PARAM" );
reportError.should.be.calledOnce.and.calledWith( "extractCardId", error );
} );
} );
} );
Expand Down
21 changes: 21 additions & 0 deletions initiateBoardEvent/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: "Initiate external automation event on Board"
description: "Initiate external automation event on an AgilePlace Board"
inputs:
host:
description: AgilePlace Url (https://mycompany.leankit.com)
required: true
apiToken:
description: Api token with write access to your AgilePlace board
required: true
boardId:
description: Board Id to initiate the event on
required: true
eventName:
description: Name of the event to initiate
required: true
outputs:
error:
description: Status message for errors
runs:
using: "node20"
main: "dist/index.js"
1 change: 1 addition & 0 deletions initiateBoardEvent/dist/index.js

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions initiateBoardEvent/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use strict";
const leankitApiFactory = require( "../leankit/api" );
const { getInputParams, reportError, validateLeankitUrl } = require( "../leankit/helpers" );

( async () => {
const [
host,
apiToken,
boardId,
eventName
] = getInputParams( { required: [ "host", "apiToken", "boardId", "eventName" ] } );

validateLeankitUrl( "host", host );

const { initiateBoardEvent } = leankitApiFactory( host, apiToken );

await initiateBoardEvent( boardId, eventName );
} )().catch( ex => {
reportError( "initiateBoardEvent", ex );
} );
Loading