From 607d3270a898c2e4b800c44780f1e38effa3a2e0 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Wed, 1 Nov 2023 17:40:13 -0400 Subject: [PATCH] enhance(build): logic parsing and linking of auto cancel messaging (#730) Co-authored-by: wass3r <1301201+wass3r@users.noreply.github.com> --- cypress/fixtures/build_canceled.json | 27 +++++ cypress/integration/build.spec.js | 28 ++++++ cypress/support/commands.js | 13 +++ src/elm/Pages/Build/View.elm | 46 ++++++++- src/elm/SvgBuilder.elm | 132 +++++++++++++++++++++++-- src/elm/Vela.elm | 2 +- src/scss/_main.scss | 23 ++++- src/scss/_themes.scss | 14 ++- src/scss/_variables.scss | 1 + src/static/images/favicon-canceled.ico | Bin 0 -> 15086 bytes 10 files changed, 274 insertions(+), 12 deletions(-) create mode 100644 cypress/fixtures/build_canceled.json create mode 100644 src/static/images/favicon-canceled.ico diff --git a/cypress/fixtures/build_canceled.json b/cypress/fixtures/build_canceled.json new file mode 100644 index 000000000..86b7609cc --- /dev/null +++ b/cypress/fixtures/build_canceled.json @@ -0,0 +1,27 @@ +{ + "id": 6, + "repo_id": 1, + "number": 6, + "parent": 1, + "event": "push", + "status": "canceled", + "error": "build canceled in favor of build 7", + "enqueued": 1572980376, + "created": 1572980376, + "started": 1572980375, + "finished": 1572980375, + "deploy": "", + "clone": "https://github.com/github/octocat.git", + "source": "https://github.com/github/octocat/commit/9b1d8bded6e992ab660eaee527c5e3232d0a2441", + "title": "push received from https://github.com/github/octocat", + "message": "fixing docker params", + "commit": "9b1d8bded6e992ab660eaee527c5e3232d0a2441", + "sender": "CookieCat", + "author": "CookieCat", + "branch": "infra", + "ref": "refs/heads/infra", + "base_ref": "", + "host": "", + "runtime": "docker", + "distribution": "linux" +} diff --git a/cypress/integration/build.spec.js b/cypress/integration/build.spec.js index b32ec54ad..d8cf41281 100644 --- a/cypress/integration/build.spec.js +++ b/cypress/integration/build.spec.js @@ -295,5 +295,33 @@ context('Build', () => { cy.get('[data-test=build-error]').contains('failure authenticating'); }); }); + + context('visit canceled build', () => { + beforeEach(() => { + cy.visit('/github/octocat/6'); + cy.get('[data-test=full-build]').as('build'); + cy.get('@build').get('[data-test=build-status]').as('buildStatus'); + }); + + it('build should have canceled style', () => { + cy.get('@buildStatus').should('have.class', '-canceled'); + }); + + it('build error should show', () => { + cy.get('[data-test=build-error]').should('be.visible'); + }); + + it('build error should contain error', () => { + cy.get('[data-test=build-error]').contains('msg:'); + cy.get('[data-test=build-error]').contains( + 'build canceled in favor of build 7', + ); + }); + + it('clicking superseding build link should direct to new build page', () => { + cy.get('[data-test=new-build-link]').click({ force: true }); + cy.location('pathname').should('eq', '/github/octocat/7'); + }); + }); }); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 8528937e0..4ac1ed16c 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -62,6 +62,7 @@ Cypress.Commands.add('stubBuild', () => { cy.fixture('build_success.json').as('successBuild'); cy.fixture('build_failure.json').as('failureBuild'); cy.fixture('build_error.json').as('errorBuild'); + cy.fixture('build_canceled.json').as('cancelBuild'); cy.route({ method: 'GET', url: 'api/v1/repos/*/*/builds/1', @@ -92,6 +93,18 @@ Cypress.Commands.add('stubBuild', () => { status: 200, response: '@errorBuild', }); + cy.route({ + method: 'GET', + url: 'api/v1/repos/*/*/builds/6', + status: 200, + response: '@cancelBuild', + }); + cy.route({ + method: 'GET', + url: 'api/v1/repos/*/*/builds/7', + status: 200, + response: '@successBuild', + }); }); Cypress.Commands.add('stubBuilds', () => { diff --git a/src/elm/Pages/Build/View.elm b/src/elm/Pages/Build/View.elm index 96917e292..9508d1b88 100644 --- a/src/elm/Pages/Build/View.elm +++ b/src/elm/Pages/Build/View.elm @@ -1113,8 +1113,48 @@ viewError build = ] Vela.Canceled -> - div [ class "error", Util.testAttribute "build-canceled" ] - [ text "build was canceled" + let + message = + if String.isEmpty build.error then + text "no error message" + + else + let + tgtBuild = + String.split " " build.error + |> List.Extra.last + |> Maybe.withDefault "" + in + -- check if the last part of the error message was a digit + -- to handle auto canceled build messages which come in the + -- form of "build was auto canceled in favor of build 42" + case String.toInt tgtBuild of + -- not an auto cancel message, use the returned error msg + Nothing -> + text build.error + + -- some special treatment to turn build number + -- into a link to the respective build + Just _ -> + let + linkList = + String.split "/" build.link + |> List.reverse + + newLink = + linkList + |> List.Extra.setAt 0 tgtBuild + |> List.reverse + |> String.join "/" + + msg = + String.replace tgtBuild "" build.error + in + span [] [ text msg, a [ href newLink, Util.testAttribute "new-build-link" ] [ text tgtBuild ] ] + in + div [ class "error", Util.testAttribute "build-error" ] + [ span [ class "label" ] [ text "msg:" ] + , span [ class "message" ] [ message ] ] _ -> @@ -1150,7 +1190,7 @@ statusToClass status = class "-failure" Vela.Canceled -> - class "-failure" + class "-canceled" Vela.Error -> class "-error" diff --git a/src/elm/SvgBuilder.elm b/src/elm/SvgBuilder.elm index a0d92d005..53d8aa8ad 100644 --- a/src/elm/SvgBuilder.elm +++ b/src/elm/SvgBuilder.elm @@ -122,6 +122,41 @@ buildFailure = ] +{-| buildCanceled : produces svg icon for build status - canceled +-} +buildCanceled : Html msg +buildCanceled = + svg + [ class "build-icon -canceled" + , strokeWidth "2" + , viewBox "0 0 44 44" + , width "44" + , height "44" + , ariaHidden + ] + [ Svg.path [ d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] [] + , Svg.path [ d "M15 15L29 29" ] [] + ] + + +{-| buildError : produces svg icon for build status - error +-} +buildError : Html msg +buildError = + svg + [ class "build-icon -error" + , strokeWidth "2" + , viewBox "0 0 44 44" + , width "44" + , height "44" + , ariaHidden + ] + [ Svg.path [ d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" ] [] + , Svg.path [ d "M22 14V25" ] [] + , Svg.path [ d "M22 30V27" ] [] + ] + + {-| buildStatusAnimation : takes dashes as particles an svg meant to parallax scroll on a running build -} buildStatusAnimation : String -> String -> List String -> Html msg @@ -245,6 +280,61 @@ stepFailure = ] +{-| stepError : produces svg icon for step status - error +-} +stepError : Html msg +stepError = + svg + [ class "-icon -error" + , strokeWidth "2" + , viewBox "0 0 44 44" + , width "32" + , height "32" + , ariaHidden + ] + [ Svg.path + [ attribute "vector-effect" "non-scaling-stroke" + , d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" + ] + [] + , Svg.path + [ attribute "vector-effect" "non-scaling-stroke" + , d "M22 13V25" + ] + [] + , Svg.path + [ attribute "vector-effect" "non-scaling-stroke" + , d "M22 29V32" + ] + [] + ] + + +{-| stepCanceled : produces svg icon for step status - canceled +-} +stepCanceled : Html msg +stepCanceled = + svg + [ class "-icon -canceled" + , strokeWidth "2" + , viewBox "0 0 44 44" + , width "32" + , height "32" + , ariaHidden + ] + [ Svg.path + [ attribute "vector-effect" "non-scaling-stroke" + , d "M5.667 1h32.666A4.668 4.668 0 0143 5.667v32.666A4.668 4.668 0 0138.333 43H5.667A4.668 4.668 0 011 38.333V5.667A4.668 4.668 0 015.667 1z" + ] + [] + , Svg.path + [ attribute "vector-effect" "non-scaling-stroke" + , d "M15 15l14 14" + ] + [] + ] + + {-| stepSkipped : produces svg icon for step status - killed Note: killed/skipped are the same thing. -} @@ -455,6 +545,36 @@ buildHistoryFailure _ = [ Svg.path [ d "M8 8l12 12M20 8L8 20" ] [] ] +{-| buildHistoryError : produces svg icon for build history status - error +-} +buildHistoryError : Int -> Html msg +buildHistoryError _ = + svg + [ class "-icon -error" + , strokeWidth "2" + , viewBox "0 0 28 28" + , width "26" + , height "26" + ] + [ Svg.path [ d "M14 8v7" ] [] + , Svg.path [ d "M14 18v2" ] [] + ] + + +{-| buildHistoryError : produces svg icon for build history status - error +-} +buildHistoryCanceled : Int -> Html msg +buildHistoryCanceled _ = + svg + [ class "-icon -canceled" + , strokeWidth "2" + , viewBox "0 0 28 28" + , width "26" + , height "26" + ] + [ Svg.path [ d "M8 8l12 12" ] [] ] + + {-| buildStatusToIcon : takes build status and returns Icon from SvgBuilder -} buildStatusToIcon : Status -> Html msg @@ -476,10 +596,10 @@ buildStatusToIcon status = buildFailure Vela.Canceled -> - buildFailure + buildCanceled Vela.Error -> - buildFailure + buildError {-| recentBuildStatusToIcon : takes build status string and returns Icon from SvgBuilder @@ -503,10 +623,10 @@ recentBuildStatusToIcon status index = buildHistoryFailure index Vela.Canceled -> - buildHistoryFailure index + buildHistoryCanceled index Vela.Error -> - buildHistoryFailure index + buildHistoryError index {-| stepStatusToIcon : takes build status and returns Icon from SvgBuilder @@ -530,10 +650,10 @@ stepStatusToIcon status = stepSkipped Vela.Canceled -> - stepFailure + stepCanceled Vela.Error -> - stepFailure + stepError {-| hookStatusToIcon : takes hook status string and returns Icon from SvgBuilder diff --git a/src/elm/Vela.elm b/src/elm/Vela.elm index 416a386b8..222b1e5fc 100644 --- a/src/elm/Vela.elm +++ b/src/elm/Vela.elm @@ -1427,7 +1427,7 @@ statusToFavicon status = "-failure" Canceled -> - "-failure" + "-canceled" Error -> "-failure" diff --git a/src/scss/_main.scss b/src/scss/_main.scss index 1585ee9e7..6dbe3b364 100644 --- a/src/scss/_main.scss +++ b/src/scss/_main.scss @@ -569,6 +569,10 @@ details.build-toggle { background: var(--color-red); } +.build .status.-canceled { + background: var(--color-cyan-dark); +} + .build .info { position: relative; @@ -600,10 +604,14 @@ details.build-toggle { .build .error { max-width: 80%; - color: var(--color-red-light); font-size: 16px; } +/* canceled build messages aren't errors */ +.build:not(.-canceled) .error { + color: var(--color-red-light); +} + .build .error .message { margin-left: 0.2em; } @@ -760,6 +768,11 @@ details.build-toggle { border-bottom: 2px solid var(--color-red); } +.build.-canceled { + border-top: 2px solid var(--color-cyan-dark); + border-bottom: 2px solid var(--color-cyan-dark); +} + .-animation-dashes-1 { stroke-dasharray: 20 220 5 360; } @@ -826,6 +839,10 @@ details.build-toggle { background-color: var(--color-red); } + &.-canceled { + background-color: var(--color-cyan-dark); + } + &.-success { background-color: var(--color-green); } @@ -1296,6 +1313,10 @@ details.build-toggle { stroke: var(--color-red); } + &.-canceled { + stroke: var(--color-cyan-dark); + } + &.-pending { fill: var(--color-bg-light); stroke: var(--color-bg-light); diff --git a/src/scss/_themes.scss b/src/scss/_themes.scss index ac996ad59..f301ff2f7 100644 --- a/src/scss/_themes.scss +++ b/src/scss/_themes.scss @@ -56,6 +56,18 @@ body.theme-light { color: var(--color-red); } + .status.-canceled { + background-color: var(--color-cyan-semi-dark); + } + + .recent-build-link .-icon.-canceled { + background-color: var(--color-cyan-semi-dark); + } + + .steps .-icon.-canceled { + stroke: var(--color-cyan-semi-dark); + } + .hooks { background: var(--color-white); } @@ -96,7 +108,7 @@ body.theme-light { border-left: none; } - .build .error { + .build:not(.-canceled) .error { color: var(--color-red); } diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss index 79287a56e..edc0900cc 100644 --- a/src/scss/_variables.scss +++ b/src/scss/_variables.scss @@ -3,6 +3,7 @@ :root { // primary colors --color-cyan-dark: hsl(192, 100%, 30%); // good for text on offwhite + --color-cyan-semi-dark: hsl(192, 100%, 38%); --color-cyan: hsl(192, 100%, 50%); // good for text on coal --color-cyan-light: hsl(192, 100%, 65%); --color-lavender-dark: hsl(286, 29%, 40%); diff --git a/src/static/images/favicon-canceled.ico b/src/static/images/favicon-canceled.ico new file mode 100644 index 0000000000000000000000000000000000000000..695b2c3e5d4f120b1df7ca9d5fd49aab9aedaeb6 GIT binary patch literal 15086 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 literal 0 HcmV?d00001