Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix [circleci] badge for projects that use workflows #1995

Closed
wants to merge 11 commits into from

Conversation

chris48s
Copy link
Member

Well this escalated in complexity quite sharply...

In an attempt to solve #1792 I've had a go at grouping related builds based on workflow_id. There are a number of changes in this PR which are useful to discuss 'inline', so I will mark up the diff with specific line comments, but just to give a high-level summary this is how the new approach works:

  • Attempt to work out if this project uses workflows
  • If it doesn't use workflows, just grab the outcome of the latest build and return that
  • If it does use workflows,
    • Find the most recent workflow where all the builds with that workflow_id have finished
    • Grab all the builds with that workflow_id
    • Summarise all the build outcomes into a single outcome for the workflow

In general, this seems to work pretty well. Unfortunately there are 3 edge cases (that I can think of) which could be problematic:

  • If a workflow with multiple builds is currently in progress and some builds have finished and some have not yet been started/queued, we might summarise an incomplete build. I'm not sure exactly how likely it is that we would hit this as testing it requires some quite precise timing but even if this is possible I don't think there is any way we could fix or work around it using this API.
  • We've hard-coded a limit on the number of builds, but if a project has a large build matrix there could potentially be a situation (e.g: if multiple workflows currently have builds in progress and we have to summarise the third or fourth workflow back in time), we might end up summarising only part of a build because some of the builds in the wrokflow are beyond our hard-coded limit. If this did happen it would fail by producing an incorrect/unexpected value and be very hard to detect. Again, I see no sensible way to fix or work around this using this API.
  • If a project has a mix of recent builds that do use workflows and older builds that don't use workflows in its history (I actually have a good example of this: https://circleci.com/api/v1.1/project/github/DemocracyClub/ec2-tag-conditional/tree/master ), we will fail with an InvalidResponse() until every build in the build history specified by the limit param either does or doesn't use workflows. This case fails with an error rather than unexpected output (so is less serious). Also it may be possible to account for, but I've not attempted to do so.

@shields-ci
Copy link

shields-ci commented Aug 28, 2018

Messages
📖 ✨ Thanks for your contribution to Shields, @chris48s!

Generated by 🚫 dangerJS

})

describe('circleci: schema validation', function() {
const validate = function(data, schema) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may not be the best place to get bogged down in it, but it seems like writing tests for non-trivial validators (like this one) is something we might want to do sometimes. I definitely found I needed to write tests to get this one correct. Lets have a think about a good pattern for this.

const emptyArraySchema = Joi.array()
.min(0)
.max(0)
.required() // [] is also a valid response from Circle CI
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out sometimes [] is a legit response example: https://circleci.com/api/v1.1/project/github/chris48s/shields/tree/issue1827


module.exports = class CircleCi extends BaseJsonService {
async fetch({ token, vcsType, userRepo, branch }) {
let url = `https://circleci.com/api/v1.1/project/${vcsType}/${userRepo}`
if (branch != null) {
url += `/tree/${branch}`
}
const query = { filter: 'completed', limit: 1 }
const query = { limit: 50 }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't filter on 'completed' any more here. We need to know about in-progress/queued builds so that if the first workflow we come across has some builds which are not complete we know to ignore that one and move on to the next workflow to try and find one where all the builds are complete. If we filter on complete here, sometimes we will summarise an incomplete workflow.

In terms of limit, I've picked 50 to balance 2 properties:

  • We need a fairly large number of records so that on a project with a large build matrix where we can't take the first workflow_id (e.g: because some builds are still in progress) we've got enough records
  • If we fetch loads of records the badge takes a long time to render because we must wait a long time for the fetch() stage

} else if (counts.timedout >= 1) {
return 'timed out'
} else if (counts.failed >= 1) {
return 'failed'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've taken a stab at a sensible order here, but I'm not committed to it. If you're reading this thinking "'canceled' obviously takes precedence over 'infrastructure fail'", feel free to make suggestions and I'll change it.

I've also switched to using 'outcome' here instead of status (outcome only applies to finished builds and we only want to summarise workflows where all the builds are finished). This also gives as smaller number of values to try and make sense of.

) {
color = 'red'
} else if (status === 'no tests') {
color = 'yellow'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. I've attempted to do something sensible-ish, but again if you're thinking "'canceled' should obviously be grey" or whatever, feel free to make suggestions and I'll change them. Once we've got this nailed I'll also add tests for the colour logic. There is relatively little consistency about this on other services. Prior art to maybe consider:

value: 'invalid json response',
colorB: '#9f9f9f',
})
.expectJSON({ name: 'build', value: 'could not summarize build status' })
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've implemented a lot of the special-case testing for this badge logic in circleci.helpers.spec.js instead of using a lot of mocked responses here. I've kept the service tests pretty high-level

- use lodash.countby in countOutcomes()
  - use lodash.countby in countOutcomes()
  - move validation from countOutcomes() into schema
  - update tests

- switch getLatestCompleteBuildOutcome() to use filter()
resolve conflict: keep ours
@chris48s
Copy link
Member Author

chris48s commented Sep 9, 2018

Based on chat in discord, I've updated this PR to use a more functional style in a couple more places in b6cda72 .

I think I'm going to leave getBuildsForLatestCompleteWorkflow() where it is. I find the mutable state approach easier to read here than the proposed uniq(builds.map(getWorkflowId)).find(workflowId => builds.filter(…).every(…))) alternative, although 'readable' is a matter of taste :)

Using lodash.countby in countOutcomes() is probably the big readabiliy gain, particularly as refactoring that function has also prompted me to move more validation logic into the schema - which is the correct place for it 👍

@chris48s chris48s added the service-badge New or updated service badge label Sep 25, 2018
@paulmelnikow paulmelnikow added the bug Bugs in badges and the frontend label Nov 15, 2018
@chris48s
Copy link
Member Author

I've resolved the conflicts on this if you want to pick up the review. I've slightly forgotten what was going on here but usefully I seem to have left a bunch of notes on the diff so hopefully it shouldn't be too hard to resume. Well done me from the past :)

@paulmelnikow
Copy link
Member

It sounds like we're thinking #1064 is a better way to go because it will let us rely on Circle's own logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Bugs in badges and the frontend service-badge New or updated service badge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants