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
32 changes: 22 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# workflow-timer

Workflow-timer is a GitHub action that measures the duration of the workflow and
Workflow-timer is a GitHub action that measures the duration of a workflow and
compares it to the duration of historical runs.

The action should be triggered on `pull_request` hence it will create a comment
on the PR with information like:

`🕒 Workflow "Unit tests" took 22.056s which is a decrease with 6.944s (23.94%) compared to latest run on master/main.`
`🕒 Workflow "Unit tests" took 22.056s which is a decrease with 6.944s (23.94%) compared to latest run on main.`

The purpose of this action is to make the developer aware of when feedbacks
loops get longer. Let's say that you are running unit tests as part of your
Expand All @@ -23,28 +23,40 @@ As the **very last job** in your workflow, add
uses: DeviesDevelopment/[email protected]
```

Workflow-timer compares the current workflow run with the latest run on the
master/main branch. Therefore, the same workflow needs to run when merging with
the master as well, otherwise, there will be no data to compare. We suggest
having the following definition in the workflow:
Workflow-timer compares the current workflow run with the latest run on another
branch (typically your default branch). Therefore, the same workflow needs to
run when merging with that other branch as well, otherwise, there will be no
data to compare. We suggest having the following definition in the workflow:

```yaml
```yml
on:
push:
branches: master
branches: main
pull_request:
branches: master
branches: main
```

If workflow-timer is used in a private repository, additional permissions are
required. Add the following permissions to the workflow:

```yaml
```yml
permissions:
actions: read
pull-requests: write
```

### Setting a custom branch to compare with

If you use a different branch than `main` as your default branch, you can
specify what branch to compare with.

```yml
- name: Time reporter
uses: DeviesDevelopment/[email protected]
with:
compareBranch: 'your-branch-name'
```

## How to contribute

Feel free to open a pull request! All contributions, no matter how small, are
Expand Down
19 changes: 13 additions & 6 deletions __tests__/commentManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ describe('previousCommentFor', () => {

describe('generateComment', () => {
it('returns fallback message when durationReport is undefined', () => {
const result = generateComment('My Workflow', undefined)
const result = generateComment('My Workflow', 'main', undefined)
expect(result).toBe(
'🕒 Workflow "My Workflow" has no historical runs on master/main branch. Can\'t compare.'
'🕒 Workflow "My Workflow" has no historical runs on main branch. Can\'t compare.'
)
})

Expand All @@ -56,9 +56,9 @@ describe('generateComment', () => {
diffInSeconds: 30,
diffInPercentage: -50
}
const result = generateComment('My Workflow', report)
const result = generateComment('My Workflow', 'main', report)
expect(result).toBe(
'🕒 Workflow "My Workflow" took 90s which is an increase with 30s (50.00%) compared to latest run on master/main.'
'🕒 Workflow "My Workflow" took 90s which is an increase with 30s (50.00%) compared to latest run on main.'
)
})

Expand All @@ -68,9 +68,16 @@ describe('generateComment', () => {
diffInSeconds: -20,
diffInPercentage: 25
}
const result = generateComment('Deploy', report)
const result = generateComment('Deploy', 'main', report)
expect(result).toBe(
'🕒 Workflow "Deploy" took 60s which is a decrease with 20s (25.00%) compared to latest run on master/main.'
'🕒 Workflow "Deploy" took 60s which is a decrease with 20s (25.00%) compared to latest run on main.'
)
})

it('produces comments with proper branch name', () => {
const result = generateComment('My Workflow', 'develop', undefined)
expect(result).toBe(
'🕒 Workflow "My Workflow" has no historical runs on develop branch. Can\'t compare.'
)
})
})
18 changes: 11 additions & 7 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ describe('main', () => {
it('does nothing for non-pull_request events', async () => {
await run(
{ ...DEFAULT_CONTEXT, eventName: 'not-pull_request' },
'fake-token'
'fake-token',
'main'
)
expect(createComment).not.toHaveBeenCalled()
expect(updateComment).not.toHaveBeenCalled()
Expand All @@ -90,11 +91,12 @@ describe('main', () => {
eventName: 'pull_request',
workflow: 'My workflow'
},
'fake-token'
'fake-token',
'main'
)

expect(createComment).toHaveBeenCalledWith(
'🕒 Workflow "My workflow" has no historical runs on master/main branch. Can\'t compare.'
'🕒 Workflow "My workflow" has no historical runs on main branch. Can\'t compare.'
)
})

Expand All @@ -119,12 +121,13 @@ describe('main', () => {
eventName: 'pull_request',
workflow: 'Another workflow'
},
'fake-token'
'fake-token',
'main'
)

expect(updateComment).toHaveBeenCalledWith(
42,
'🕒 Workflow "Another workflow" has no historical runs on master/main branch. Can\'t compare.'
'🕒 Workflow "Another workflow" has no historical runs on main branch. Can\'t compare.'
)
})

Expand Down Expand Up @@ -155,11 +158,12 @@ describe('main', () => {
eventName: 'pull_request',
workflow: 'Some workflow'
},
'fake-token'
'fake-token',
'main'
)

expect(createComment).toHaveBeenCalledWith(
'🕒 Workflow "Some workflow" took 180s which is an increase with 120s (200.00%) compared to latest run on master/main.'
'🕒 Workflow "Some workflow" took 180s which is an increase with 120s (200.00%) compared to latest run on main.'
)
})
})
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ inputs:
token:
description: 'GITHUB_TOKEN or a repo scoped PAT.'
default: ${{ github.token }}
compareBranch:
description:
'Branch to compare workflow durations with (typically your default
branch).'
required: false
default: 'main'
runs:
using: 'node20'
main: 'dist/index.js'
21 changes: 11 additions & 10 deletions dist/index.js

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

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions src/commentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ export function previousCommentFor(workflowName: string) {

export function generateComment(
workflowName: string,
compareBranch: string,
durationReport?: DurationReport
) {
if (!durationReport) {
return `🕒 Workflow "${workflowName}" has no historical runs on master/main branch. Can't compare.`
return `🕒 Workflow "${workflowName}" has no historical runs on ${compareBranch} branch. Can't compare.`
}
return (
'🕒 Workflow "' +
Expand All @@ -31,6 +32,6 @@ export function generateComment(
Math.abs(durationReport.diffInSeconds) +
's (' +
Math.abs(durationReport.diffInPercentage).toFixed(2) +
'%) compared to latest run on master/main.'
`%) compared to latest run on ${compareBranch}.`
)
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { run } from './main.js'

try {
const token = core.getInput('token')
run(context, token)
const compareBranch = core.getInput('compareBranch')
run(context, token, compareBranch)
} catch (error) {
if (error instanceof Error) {
core.setFailed(error.message)
Expand Down
33 changes: 22 additions & 11 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { GhActionsContext } from './types.js'

export async function run(
context: GhActionsContext,
token: string
token: string,
compareBranch: string
): Promise<void> {
const ghClient = new GitHubClient(token, context)
if (context.eventName != 'pull_request') {
Expand All @@ -16,12 +17,19 @@ export async function run(
const historical_runs = await ghClient.listWorkflowRuns(
currentRun.data.workflow_id
)
const latestRunOnMaster = historical_runs.data.workflow_runs.find(
succeededOnMainBranch
const latestRunOnCompareBranch = historical_runs.data.workflow_runs.find(
(run) => succeededOnBranch(run, compareBranch)
)

const durationReport = calculateDuration(currentRun.data, latestRunOnMaster)
const outputMessage = generateComment(context.workflow, durationReport)
const durationReport = calculateDuration(
currentRun.data,
latestRunOnCompareBranch
)
const outputMessage = generateComment(
context.workflow,
compareBranch,
durationReport
)

const existingComments = await ghClient.listComments()
const existingComment = existingComments.data
Expand All @@ -35,14 +43,17 @@ export async function run(
}
}

function succeededOnMainBranch(workflowRun: {
head_branch: string | null
status: string | null
conclusion: string | null
}) {
function succeededOnBranch(
workflowRun: {
head_branch: string | null
status: string | null
conclusion: string | null
},
target_branch: string
) {
const { head_branch, status, conclusion } = workflowRun
return (
(head_branch === 'master' || head_branch === 'main') &&
head_branch === target_branch &&
status === 'completed' &&
conclusion === 'success'
)
Expand Down