From 0f554877c4bb5ce7c1d38422f16fcf4ea1691fe4 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 2 Jun 2025 21:12:58 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- Gemfile.checksum | 20 +-- Gemfile.lock | 2 +- Gemfile.next.checksum | 20 +-- Gemfile.next.lock | 2 +- .../components/rapid_diffs/_constants.scss | 1 + .../rapid_diffs/diff_file_component.scss | 8 +- .../rapid_diffs/text_file_viewers.scss | 45 +++--- .../stylesheets/highlight/themes/dark.scss | 14 +- .../stylesheets/highlight/themes/monokai.scss | 14 +- .../stylesheets/highlight/themes/none.scss | 4 +- .../highlight/themes/solarized-dark.scss | 14 +- .../highlight/themes/solarized-light.scss | 8 +- app/helpers/commits_helper.rb | 11 ++ app/helpers/work_items_helper.rb | 2 +- app/views/projects/commits/show.html.haml | 2 + app/views/projects/pipelines/show.html.haml | 2 +- .../use_websocket_for_k8s_watch.yml | 6 +- doc/api/graphql/reference/_index.md | 2 +- doc/user/clusters/agent/install/_index.md | 12 +- locale/gitlab.pot | 3 + .../user_views_work_items_list_spec.rb | 130 ++++++++++++++++++ spec/helpers/boards_helper_spec.rb | 2 +- spec/helpers/commits_helper_spec.rb | 35 +++++ spec/support/helpers/test_env.rb | 18 ++- .../lib/tooling/predictive_tests_spec.rb | 2 +- .../projects/commits/show.html.haml_spec.rb | 9 ++ tooling/lib/tooling/predictive_tests.rb | 2 +- workhorse/internal/redis/keywatcher.go | 9 +- workhorse/internal/redis/redis.go | 11 ++ 29 files changed, 319 insertions(+), 91 deletions(-) rename config/feature_flags/{wip => gitlab_com_derisk}/use_websocket_for_k8s_watch.yml (66%) create mode 100644 spec/features/work_items/user_views_work_items_list_spec.rb diff --git a/Gemfile.checksum b/Gemfile.checksum index 964ca2c30a7..03e90511876 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -271,16 +271,16 @@ {"name":"google-cloud-storage","version":"1.45.0","platform":"ruby","checksum":"f280abda4e608f9e91433f9dd907be4a45cdbf251ffeb275d713548e515c6300"}, {"name":"google-cloud-storage_transfer","version":"1.2.0","platform":"ruby","checksum":"132901f50889e02a0d378e6117c6408cbfc4fdbd15c9d31fabec4f4189ef1658"}, {"name":"google-cloud-storage_transfer-v1","version":"0.8.0","platform":"ruby","checksum":"9dbef80275db556e046bb24139ca6559affe641d1e38b2537b8caaf2f8896176"}, -{"name":"google-protobuf","version":"3.25.7","platform":"aarch64-linux","checksum":"acc5d3c1d9a1a92be262702b506c7f7163daa5fb8eadefdffdd13dc3ac449d96"}, -{"name":"google-protobuf","version":"3.25.7","platform":"arm64-darwin","checksum":"d5b07a90ab2b4d185c81dd62e61b0e7a375f485256a0f8862e96b50031196c69"}, -{"name":"google-protobuf","version":"3.25.7","platform":"java","checksum":"1b1061f89423828635f8fd43ea7d99214d472304c80f0fa6bb8dcf3f285a3f9b"}, -{"name":"google-protobuf","version":"3.25.7","platform":"ruby","checksum":"a860ead0c79a4598082ef2be638e23f61602524b3d48a001f48cf33d7f8cd9a9"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x64-mingw-ucrt","checksum":"351172467880c06ea98bfdd793532c2650d42a236aad040706c7608fc4def7d3"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x64-mingw32","checksum":"c398026e2e6d67adfde8be2b9a0c9ed74057775544fbd973a852253c83e78b20"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x86-linux","checksum":"7a2e142e9a064ddd25f2a61cf3c7e91e05969809300f44852978d8ce9d330923"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x86-mingw32","checksum":"6266235687035eb3c08548aa5cc7d1cc04054b84cbce54386146df2638e7895c"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x86_64-darwin","checksum":"9dd1a8fde13e2b2f399cd9df95bc72851e28a71a70211145f02ec32f0569cd1c"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x86_64-linux","checksum":"f4bf387f7aa782377ca3ac6ef3ff612867dfe8d061fd31f756082038f37df727"}, +{"name":"google-protobuf","version":"3.25.8","platform":"aarch64-linux","checksum":"5869d1a31f39ee3361e85f3ef3db0512c19f0e0c75cd69d7303c177e17590044"}, +{"name":"google-protobuf","version":"3.25.8","platform":"arm64-darwin","checksum":"e294affc4fb25c8bc7edd264f0ba490d42dce3afff08db1e08fb7bb44cc57488"}, +{"name":"google-protobuf","version":"3.25.8","platform":"java","checksum":"1b8dd949116795653347f95d7975ce2897de2adf721647c10bf54d30ab87fd1e"}, +{"name":"google-protobuf","version":"3.25.8","platform":"ruby","checksum":"102c8500502e224a5daa7447bdd2c458a25a6c7b0bf5d8496a559ada131952b7"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x64-mingw-ucrt","checksum":"e99e7a19c958235e4a4f1407ec7af3b420542ee50bb1f46a9c5ed1cc2e4068b5"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x64-mingw32","checksum":"9703ed3454f9bea1fa733b3a670006e430e1418c7ddf0e9874636e77f0ddf009"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x86-linux","checksum":"288ffaaec53c9f666582cd0a5d42395fea6b7c15458c1f9d64a295ca174c0233"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x86-mingw32","checksum":"e322e20487a037760937df9086c9720e60eb050ace7eb66cedd99c2fc2e96072"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x86_64-darwin","checksum":"9c9e8c1c760eeb314fd6bcba1b0fb1e64c473fc9e2040c8992df61874974c108"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x86_64-linux","checksum":"07783d910635e2a60eaf929fe3e45ea4d657d023be3816bda99a21c14f7be959"}, {"name":"googleapis-common-protos","version":"1.4.0","platform":"ruby","checksum":"da2380fb5ab1563580816c74e8d684ac17512c3654c829a3ee84f6d6139de382"}, {"name":"googleapis-common-protos-types","version":"1.20.0","platform":"ruby","checksum":"5e374b06bcfc7e13556e7c0d87b99f1fa3d42de6396a1de3d8fc13aefb4dd07f"}, {"name":"googleauth","version":"1.8.1","platform":"ruby","checksum":"814adadaaa1221dce72a67131e3ecbd6d23491a161ec84fb15fd353b87d8c9e7"}, diff --git a/Gemfile.lock b/Gemfile.lock index cd75fee9a84..cc4fc678791 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -914,7 +914,7 @@ GEM google-cloud-storage_transfer-v1 (0.8.0) gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) - google-protobuf (3.25.7) + google-protobuf (3.25.8) googleapis-common-protos (1.4.0) google-protobuf (~> 3.14) googleapis-common-protos-types (~> 1.2) diff --git a/Gemfile.next.checksum b/Gemfile.next.checksum index 2305c1130b8..94d0da8d2be 100644 --- a/Gemfile.next.checksum +++ b/Gemfile.next.checksum @@ -271,16 +271,16 @@ {"name":"google-cloud-storage","version":"1.45.0","platform":"ruby","checksum":"f280abda4e608f9e91433f9dd907be4a45cdbf251ffeb275d713548e515c6300"}, {"name":"google-cloud-storage_transfer","version":"1.2.0","platform":"ruby","checksum":"132901f50889e02a0d378e6117c6408cbfc4fdbd15c9d31fabec4f4189ef1658"}, {"name":"google-cloud-storage_transfer-v1","version":"0.8.0","platform":"ruby","checksum":"9dbef80275db556e046bb24139ca6559affe641d1e38b2537b8caaf2f8896176"}, -{"name":"google-protobuf","version":"3.25.7","platform":"aarch64-linux","checksum":"acc5d3c1d9a1a92be262702b506c7f7163daa5fb8eadefdffdd13dc3ac449d96"}, -{"name":"google-protobuf","version":"3.25.7","platform":"arm64-darwin","checksum":"d5b07a90ab2b4d185c81dd62e61b0e7a375f485256a0f8862e96b50031196c69"}, -{"name":"google-protobuf","version":"3.25.7","platform":"java","checksum":"1b1061f89423828635f8fd43ea7d99214d472304c80f0fa6bb8dcf3f285a3f9b"}, -{"name":"google-protobuf","version":"3.25.7","platform":"ruby","checksum":"a860ead0c79a4598082ef2be638e23f61602524b3d48a001f48cf33d7f8cd9a9"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x64-mingw-ucrt","checksum":"351172467880c06ea98bfdd793532c2650d42a236aad040706c7608fc4def7d3"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x64-mingw32","checksum":"c398026e2e6d67adfde8be2b9a0c9ed74057775544fbd973a852253c83e78b20"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x86-linux","checksum":"7a2e142e9a064ddd25f2a61cf3c7e91e05969809300f44852978d8ce9d330923"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x86-mingw32","checksum":"6266235687035eb3c08548aa5cc7d1cc04054b84cbce54386146df2638e7895c"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x86_64-darwin","checksum":"9dd1a8fde13e2b2f399cd9df95bc72851e28a71a70211145f02ec32f0569cd1c"}, -{"name":"google-protobuf","version":"3.25.7","platform":"x86_64-linux","checksum":"f4bf387f7aa782377ca3ac6ef3ff612867dfe8d061fd31f756082038f37df727"}, +{"name":"google-protobuf","version":"3.25.8","platform":"aarch64-linux","checksum":"5869d1a31f39ee3361e85f3ef3db0512c19f0e0c75cd69d7303c177e17590044"}, +{"name":"google-protobuf","version":"3.25.8","platform":"arm64-darwin","checksum":"e294affc4fb25c8bc7edd264f0ba490d42dce3afff08db1e08fb7bb44cc57488"}, +{"name":"google-protobuf","version":"3.25.8","platform":"java","checksum":"1b8dd949116795653347f95d7975ce2897de2adf721647c10bf54d30ab87fd1e"}, +{"name":"google-protobuf","version":"3.25.8","platform":"ruby","checksum":"102c8500502e224a5daa7447bdd2c458a25a6c7b0bf5d8496a559ada131952b7"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x64-mingw-ucrt","checksum":"e99e7a19c958235e4a4f1407ec7af3b420542ee50bb1f46a9c5ed1cc2e4068b5"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x64-mingw32","checksum":"9703ed3454f9bea1fa733b3a670006e430e1418c7ddf0e9874636e77f0ddf009"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x86-linux","checksum":"288ffaaec53c9f666582cd0a5d42395fea6b7c15458c1f9d64a295ca174c0233"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x86-mingw32","checksum":"e322e20487a037760937df9086c9720e60eb050ace7eb66cedd99c2fc2e96072"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x86_64-darwin","checksum":"9c9e8c1c760eeb314fd6bcba1b0fb1e64c473fc9e2040c8992df61874974c108"}, +{"name":"google-protobuf","version":"3.25.8","platform":"x86_64-linux","checksum":"07783d910635e2a60eaf929fe3e45ea4d657d023be3816bda99a21c14f7be959"}, {"name":"googleapis-common-protos","version":"1.4.0","platform":"ruby","checksum":"da2380fb5ab1563580816c74e8d684ac17512c3654c829a3ee84f6d6139de382"}, {"name":"googleapis-common-protos-types","version":"1.20.0","platform":"ruby","checksum":"5e374b06bcfc7e13556e7c0d87b99f1fa3d42de6396a1de3d8fc13aefb4dd07f"}, {"name":"googleauth","version":"1.8.1","platform":"ruby","checksum":"814adadaaa1221dce72a67131e3ecbd6d23491a161ec84fb15fd353b87d8c9e7"}, diff --git a/Gemfile.next.lock b/Gemfile.next.lock index 5e2125f5712..5f5d4b6a7af 100644 --- a/Gemfile.next.lock +++ b/Gemfile.next.lock @@ -908,7 +908,7 @@ GEM google-cloud-storage_transfer-v1 (0.8.0) gapic-common (>= 0.20.0, < 2.a) google-cloud-errors (~> 1.0) - google-protobuf (3.25.7) + google-protobuf (3.25.8) googleapis-common-protos (1.4.0) google-protobuf (~> 3.14) googleapis-common-protos-types (~> 1.2) diff --git a/app/assets/stylesheets/components/rapid_diffs/_constants.scss b/app/assets/stylesheets/components/rapid_diffs/_constants.scss index 74f0de4120a..5af989271b1 100644 --- a/app/assets/stylesheets/components/rapid_diffs/_constants.scss +++ b/app/assets/stylesheets/components/rapid_diffs/_constants.scss @@ -20,3 +20,4 @@ $diff-file-hunk-header-z-index: functions.more-than($diff-file-line-number-z-ind $diff-file-highlighted-line-number-z-index: functions.more-than($diff-file-hunk-header-z-index); $diff-file-header-z-index: functions.more-than($diff-file-highlighted-line-number-z-index); +$diff-file-header-active-z-index: functions.more-than($diff-file-header-z-index); diff --git a/app/assets/stylesheets/components/rapid_diffs/diff_file_component.scss b/app/assets/stylesheets/components/rapid_diffs/diff_file_component.scss index 21c8f2c6ee6..68c63ba5e53 100644 --- a/app/assets/stylesheets/components/rapid_diffs/diff_file_component.scss +++ b/app/assets/stylesheets/components/rapid_diffs/diff_file_component.scss @@ -37,6 +37,10 @@ container-type: scroll-state; } +.rd-diff-file-header-sticky:has([data-options-toggle] button[aria-expanded='true']) { + z-index: constants.$diff-file-header-active-z-index; +} + .rd-diff-file-header { @apply gl-text-default; display: flex; @@ -51,10 +55,6 @@ } } -.rd-diff-file-header:has([data-options-toggle] button[aria-expanded='true']) { - z-index: 10; -} - .rd-diff-file[data-collapsed] .rd-diff-file-header { border-radius: var(--rd-diff-file-border-radius); } diff --git a/app/assets/stylesheets/components/rapid_diffs/text_file_viewers.scss b/app/assets/stylesheets/components/rapid_diffs/text_file_viewers.scss index 46d5e97418a..083942be4d9 100644 --- a/app/assets/stylesheets/components/rapid_diffs/text_file_viewers.scss +++ b/app/assets/stylesheets/components/rapid_diffs/text_file_viewers.scss @@ -4,7 +4,7 @@ --rd-code-border-adjust: var(--code-border-lightness-adjust, -0.075); --rd-line-added-background-color: var(--custom-diff-addition-color, var(--code-new-diff-background-color, $line-number-new)); --rd-line-removed-background-color: var(--custom-diff-deletion-color, var(--code-old-diff-background-color, $line-number-old)); - --rd-line-link-color: var(--code-line-nubmer-color, #{$gl-color-neutral-400}); + --rd-line-link-color: var(--code-line-number-color, #{$gl-color-neutral-400}); --rd-line-theme-border-color: oklch(from var(--code-background) calc(l + var(--rd-code-border-adjust)) c h); background-color: var(--code-background, $gl-color-neutral-0); font-family: $monospace-font; @@ -41,7 +41,7 @@ --rd-row-bottom-right-radius: var(--rd-diff-file-border-radius); } -.rd-text-view-content > tr:last-child td:not(:last-child) { +.rd-text-view-content > tr:last-child > td:not(:last-child) { --rd-row-bottom-right-radius: 0; } @@ -127,12 +127,20 @@ position: relative; padding: 0 10px 0 5px; text-align: right; - background-color: var(--code-line-nubmer-background-color, $white); + background-color: var(--code-line-number-background-color, $white); scroll-margin-top: var(--rd-diff-file-header-height); z-index: constants.$diff-file-line-number-z-index; } -.rd-hunk-lines:has(.rd-line-number:target) { +.rd-line-number:target, +.rd-line-number:target ~ .rd-line-number { + --rd-adjacent-line-border-color: var(--rd-line-border-color); +} + +.rd-hunk-lines:has(> .rd-line-number:target) { + --rd-line-background-color-override: var(--code-highlighted-line-background-color); + // handle empty cells + --code-line-number-background-color: var(--code-highlighted-line-background-color); --rd-disable-extended-borders: 1; position: relative; background-color: var(--code-highlighted-line-background-color); @@ -141,42 +149,32 @@ z-index: constants.$diff-file-highlighted-line-number-z-index; } -.rd-hunk-lines:has(.rd-line-number:target) .rd-line-number, -.rd-hunk-lines:has(.rd-line-number:target) .rd-line-content { - // handle empty cells - --code-line-nubmer-background-color: var(--code-highlighted-line-background-color); - --rd-line-background-color: var(--code-highlighted-line-background-color); -} - // child combinator improves performance of the selector // duplicate .rd-hunk-lines-inline selector to override highlighted color .rd-hunk-lines-parallel:hover > .rd-line-number:not(:empty), .rd-hunk-lines-inline.rd-hunk-lines-inline:hover > .rd-line-number { - --rd-line-border-color: var(--code-line-nubmer-hover-border-color, #{$gl-color-purple-200}); + --rd-line-border-color: var(--code-line-number-hover-border-color, #{$gl-color-purple-200}); --rd-adjacent-line-border-color: var(--rd-line-border-color); - --rd-line-background-color: var(--code-line-nubmer-hover-background-color, #{$gl-color-purple-100}); + --rd-line-background-color: var(--code-line-number-hover-background-color, #{$gl-color-purple-100}); background-color: var(--rd-line-background-color); } -.rd-line-number:hover:not(:empty) .rd-line-link { - color: var(--code-line-nubmer-hover-color, $gl-color-neutral-600); +.rd-line-number:hover > .rd-line-link { + color: var(--code-line-number-hover-color, $gl-color-neutral-600); } .rd-line-number[data-change=removed] { - --rd-line-link-color: var(--code-old-diff-line-nubmer-color, #{scale-color($gl-color-neutral-300, $red: -30%, $green: -30%, $blue: -30%)}); + --rd-line-link-color: var(--code-old-diff-line-number-color, #{scale-color($gl-color-neutral-300, $red: -30%, $green: -30%, $blue: -30%)}); --rd-line-background-color: var(--custom-diff-deletion-color, var(--code-old-diff-line-number-background-color)); - background-color: var(--rd-line-background-color); } .rd-line-number[data-change=added] { - --rd-line-link-color: var(--code-new-diff-line-nubmer-color, #{scale-color($gl-color-neutral-200, $red: -30%, $green: -30%, $blue: -30%)}); + --rd-line-link-color: var(--code-new-diff-line-number-color, #{scale-color($gl-color-neutral-200, $red: -30%, $green: -30%, $blue: -30%)}); --rd-line-background-color: var(--custom-diff-addition-color, var(--code-new-diff-line-number-background-color)); - background-color: var(--rd-line-background-color); } .rd-line-number[data-change=meta] { --rd-line-background-color: var(--code-meta-diff-background-color); - background-color: var(--code-meta-diff-background-color); } .rd-line-number-border-right, @@ -191,8 +189,9 @@ // consistent border colors for changed lines .rd-line-number, .rd-line-content { - --rd-line-border-color: oklch(from var(--rd-line-background-color) calc(l + var(--rd-code-border-adjust)) c h); - background-color: var(--rd-line-background-color); + --background-color: var(--rd-line-background-color-override, var(--rd-line-background-color)); + --rd-line-border-color: oklch(from var(--background-color) calc(l + var(--rd-code-border-adjust)) c h); + background-color: var(--background-color); } .rd-line-content[data-change=removed] + .rd-line-number[data-change=added] { @@ -209,7 +208,7 @@ } // table cells can't overflow to the right, we have to reserve space on the neighbouring cell -.rd-hunk-lines-parallel > .rd-line-number[data-position=new]:empty { +.rd-hunk-lines-parallel > .rd-line-number[data-position=new]:not([data-change=meta]):empty { border-left: 0; margin-left: 1px; } diff --git a/app/assets/stylesheets/highlight/themes/dark.scss b/app/assets/stylesheets/highlight/themes/dark.scss index c8a3785e806..3352b7bfa75 100644 --- a/app/assets/stylesheets/highlight/themes/dark.scss +++ b/app/assets/stylesheets/highlight/themes/dark.scss @@ -132,14 +132,14 @@ $dark-il: #de935f; --code-border-lightness-adjust: 0.1; --diff-expansion-background-color: #{$gl-color-neutral-600}; - --code-line-nubmer-background-color: #{$dark-main-bg}; - --code-line-nubmer-color: #{$dark-line-num-color}; - --code-line-nubmer-hover-background-color: #{$gl-color-purple-800}; - --code-line-nubmer-hover-border-color: #{$gl-color-purple-700}; - --code-line-nubmer-hover-color: #{$gl-color-purple-50}; + --code-line-number-background-color: #{$dark-main-bg}; + --code-line-number-color: #{$dark-line-num-color}; + --code-line-number-hover-background-color: #{$gl-color-purple-800}; + --code-line-number-hover-border-color: #{$gl-color-purple-700}; + --code-line-number-hover-color: #{$gl-color-purple-50}; --code-old-diff-line-number-background-color: #{$dark-old-bg}; - --code-old-diff-line-nubmer-color: #{$dark-line-num-color-old}; + --code-old-diff-line-number-color: #{$dark-line-num-color-old}; --code-old-diff-sign-color: #{$dark-line-num-color-old}; --code-old-diff-background-color: #{$dark-old-bg}; --code-old-diff-border-color: #{$dark-border}; @@ -147,7 +147,7 @@ $dark-il: #de935f; --code-old-inline-diff-background-color: #{$dark-old-idiff}; --code-new-diff-line-number-background-color: #{$dark-new-bg}; - --code-new-diff-line-nubmer-color: #{$dark-line-num-color-new}; + --code-new-diff-line-number-color: #{$dark-line-num-color-new}; --code-new-diff-sign-color: #{$dark-line-num-color-new}; --code-new-diff-background-color: #{$dark-new-bg}; --code-new-diff-border-color: #{$dark-border}; diff --git a/app/assets/stylesheets/highlight/themes/monokai.scss b/app/assets/stylesheets/highlight/themes/monokai.scss index 31d8d0c3c1c..a9366fa4d2b 100644 --- a/app/assets/stylesheets/highlight/themes/monokai.scss +++ b/app/assets/stylesheets/highlight/themes/monokai.scss @@ -105,14 +105,14 @@ $monokai-gh: #75715e; --code-background: #{$monokai-bg}; - --code-line-nubmer-background-color: #{$monokai-bg}; - --code-line-nubmer-color: #{$monokai-line-num-color}; - --code-line-nubmer-hover-background-color: #{$gl-color-purple-800}; - --code-line-nubmer-hover-border-color: #{$gl-color-purple-700}; - --code-line-nubmer-hover-color: #{$gl-color-purple-50}; + --code-line-number-background-color: #{$monokai-bg}; + --code-line-number-color: #{$monokai-line-num-color}; + --code-line-number-hover-background-color: #{$gl-color-purple-800}; + --code-line-number-hover-border-color: #{$gl-color-purple-700}; + --code-line-number-hover-color: #{$gl-color-purple-50}; --code-old-diff-line-number-background-color: #{$monokai-old-bg}; - --code-old-diff-line-nubmer-color: #{$monokai-line-num-color-old}; + --code-old-diff-line-number-color: #{$monokai-line-num-color-old}; --code-old-diff-sign-color: #{$monokai-line-num-color-old}; --code-old-diff-background-color: #{$monokai-old-bg}; --code-old-diff-border-color: #{$monokai-diff-border}; @@ -120,7 +120,7 @@ $monokai-gh: #75715e; --code-old-inline-diff-background-color: #{$monokai-old-idiff}; --code-new-diff-line-number-background-color: #{$monokai-new-bg}; - --code-new-diff-line-nubmer-color: #{$monokai-line-num-color-new}; + --code-new-diff-line-number-color: #{$monokai-line-num-color-new}; --code-new-diff-sign-color: #{$monokai-line-num-color-new}; --code-new-diff-background-color: #{$monokai-new-bg}; --code-new-diff-border-color: #{$monokai-diff-border}; diff --git a/app/assets/stylesheets/highlight/themes/none.scss b/app/assets/stylesheets/highlight/themes/none.scss index c1e5fb9950c..f5af2cdff26 100644 --- a/app/assets/stylesheets/highlight/themes/none.scss +++ b/app/assets/stylesheets/highlight/themes/none.scss @@ -24,8 +24,8 @@ $none-code-mark: #d3e3f4; --code-background: #{$gl-color-neutral-0}; - --code-line-nubmer-background-color: #{$gl-color-neutral-10}; - --code-line-nubmer-color: #{$gl-color-alpha-dark-24}; + --code-line-number-background-color: #{$gl-color-neutral-10}; + --code-line-number-color: #{$gl-color-alpha-dark-24}; --code-old-diff-line-number-background-color: #{$gl-color-neutral-50}; --code-old-diff-sign-color: #{$gl-color-neutral-800}; diff --git a/app/assets/stylesheets/highlight/themes/solarized-dark.scss b/app/assets/stylesheets/highlight/themes/solarized-dark.scss index a91570e2a15..664939d4e7e 100644 --- a/app/assets/stylesheets/highlight/themes/solarized-dark.scss +++ b/app/assets/stylesheets/highlight/themes/solarized-dark.scss @@ -108,14 +108,14 @@ $solarized-dark-il: #2aa198; --code-background: #{$solarized-dark-pre-bg}; - --code-line-nubmer-background-color: #{$solarized-dark-line-bg}; - --code-line-nubmer-color: #{$solarized-dark-line-color}; - --code-line-nubmer-hover-background-color: #{$gl-color-purple-800}; - --code-line-nubmer-hover-border-color: #{$gl-color-purple-700}; - --code-line-nubmer-hover-color: #{$gl-color-purple-50}; + --code-line-number-background-color: #{$solarized-dark-line-bg}; + --code-line-number-color: #{$solarized-dark-line-color}; + --code-line-number-hover-background-color: #{$gl-color-purple-800}; + --code-line-number-hover-border-color: #{$gl-color-purple-700}; + --code-line-number-hover-color: #{$gl-color-purple-50}; --code-old-diff-line-number-background-color: #{$solarized-dark-old-bg}; - --code-old-diff-line-nubmer-color: #{$solarized-dark-line-color-old}; + --code-old-diff-line-number-color: #{$solarized-dark-line-color-old}; --code-old-diff-sign-color: #{$solarized-dark-line-color-old}; --code-old-diff-background-color: #{$solarized-dark-old-bg}; --code-old-diff-border-color: #{$solarized-dark-border}; @@ -123,7 +123,7 @@ $solarized-dark-il: #2aa198; --code-old-inline-diff-background-color: #{$solarized-dark-old-idiff}; --code-new-diff-line-number-background-color: #{$solarized-dark-new-bg}; - --code-new-diff-line-nubmer-color: #{$solarized-dark-line-color-new}; + --code-new-diff-line-number-color: #{$solarized-dark-line-color-new}; --code-new-diff-sign-color: #{$solarized-dark-line-color-new}; --code-new-diff-background-color: #{$solarized-dark-new-bg}; --code-new-diff-border-color: #{$solarized-dark-border}; diff --git a/app/assets/stylesheets/highlight/themes/solarized-light.scss b/app/assets/stylesheets/highlight/themes/solarized-light.scss index a4c58d9878d..23af1d60110 100644 --- a/app/assets/stylesheets/highlight/themes/solarized-light.scss +++ b/app/assets/stylesheets/highlight/themes/solarized-light.scss @@ -114,11 +114,11 @@ $solarized-light-il: #2aa198; --code-background: #{$solarized-light-pre-bg}; - --code-line-nubmer-background-color: #{$solarized-light-line-bg}; - --code-line-nubmer-color: #{$solarized-light-line-color}; + --code-line-number-background-color: #{$solarized-light-line-bg}; + --code-line-number-color: #{$solarized-light-line-color}; --code-old-diff-line-number-background-color: #{$solarized-light-old-bg}; - --code-old-diff-line-nubmer-color: #{$solarized-light-line-color-old}; + --code-old-diff-line-number-color: #{$solarized-light-line-color-old}; --code-old-diff-sign-color: #{$solarized-light-line-color-old}; --code-old-diff-background-color: #{$solarized-light-old-bg}; --code-old-diff-border-color: #{$solarized-light-border}; @@ -126,7 +126,7 @@ $solarized-light-il: #2aa198; --code-old-inline-diff-background-color: #{$solarized-light-old-idiff}; --code-new-diff-line-number-background-color: #{$solarized-light-new-bg}; - --code-new-diff-line-nubmer-color: #{$solarized-light-line-color-new}; + --code-new-diff-line-number-color: #{$solarized-light-line-color-new}; --code-new-diff-sign-color: #{$solarized-light-line-color-new}; --code-new-diff-background-color: #{$solarized-light-new-bg}; --code-new-diff-border-color: #{$solarized-light-border}; diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 0881e3d223c..233b6f105ce 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -124,6 +124,17 @@ module CommitsHelper end end + def path_to_browse_file_or_directory(project, ref, path) + return project_tree_path(project, ref) if path.blank? + + path_to_item = tree_join(ref, path) + if commit_blob.present? + project_blob_path(project, path_to_item) + else + project_tree_path(project, path_to_item) + end + end + def commit_options_dropdown_data(project, commit) can_collaborate = current_user && can_collaborate_with_project?(project) diff --git a/app/helpers/work_items_helper.rb b/app/helpers/work_items_helper.rb index 1bc370c81ff..960daca8c37 100644 --- a/app/helpers/work_items_helper.rb +++ b/app/helpers/work_items_helper.rb @@ -23,7 +23,7 @@ module WorkItemsHelper default_branch: resource_parent.is_a?(Project) ? resource_parent.default_branch_or_main : nil, initial_sort: current_user&.user_preference&.issues_sort, is_signed_in: current_user.present?.to_s, - show_new_work_item: can?(current_user, :create_work_item, group).to_s, + show_new_work_item: can?(current_user, :create_work_item, resource_parent).to_s, can_create_projects: can?(current_user, :create_projects, group).to_s, new_project_path: new_project_path(namespace_id: group&.id), group_id: group&.id, diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 8d16d9eb2e7..eb411de5fa2 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -18,6 +18,8 @@ = commits_breadcrumbs #js-author-dropdown{ data: { 'commits_path': project_commits_path(@project), 'project_id': @project.id } } .tree-controls + .control + = link_button_to _('Browse files'), path_to_browse_file_or_directory(@project, @ref, @path) - if @merge_request.present? .control.gl-hidden.md:gl-block = link_button_to _("View open merge request"), project_merge_request_path(@project, @merge_request) diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index 617ae8e59d3..38568224b46 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -1,7 +1,7 @@ - @force_fluid_layout = true - add_to_breadcrumbs _('Pipelines'), project_pipelines_path(@project) - breadcrumb_title "##{@pipeline.id}" -- page_title _('Pipeline') +- page_title sprintf(s_("Pipeline|Pipeline #%{pipeline_id}"), {pipeline_id: @pipeline.id}) - pipeline_has_errors = @pipeline.builds.empty? && @pipeline.error_messages.any? - add_page_specific_style 'page_bundles/pipeline' - add_page_specific_style 'page_bundles/reports' diff --git a/config/feature_flags/wip/use_websocket_for_k8s_watch.yml b/config/feature_flags/gitlab_com_derisk/use_websocket_for_k8s_watch.yml similarity index 66% rename from config/feature_flags/wip/use_websocket_for_k8s_watch.yml rename to config/feature_flags/gitlab_com_derisk/use_websocket_for_k8s_watch.yml index 600eec13a34..1aa764b6a9d 100644 --- a/config/feature_flags/wip/use_websocket_for_k8s_watch.yml +++ b/config/feature_flags/gitlab_com_derisk/use_websocket_for_k8s_watch.yml @@ -2,8 +2,8 @@ name: use_websocket_for_k8s_watch feature_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/13380 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168399 -rollout_issue_url: +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/547269 milestone: '17.5' group: group::environments -type: wip -default_enabled: false +type: gitlab_com_derisk +default_enabled: false \ No newline at end of file diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index ade938a65b3..84d1ff3e231 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -21666,7 +21666,7 @@ Analyzer status (success/fail) for projects. | Name | Type | Description | | ---- | ---- | ----------- | | `analyzerType` | [`AnalyzerTypeEnum!`](#analyzertypeenum) | Analyzer type. | -| `buildId` | [`Int`](#int) | Build ID. | +| `buildId` | [`JobID`](#jobid) | Build ID. | | `lastCall` | [`Time!`](#time) | Last time analyzer was called. | | `projectId` | [`Int!`](#int) | Project ID. | | `status` | [`AnalyzerStatusEnum!`](#analyzerstatusenum) | Analyzer status. | diff --git a/doc/user/clusters/agent/install/_index.md b/doc/user/clusters/agent/install/_index.md index fcb25a4636c..e57587118fc 100644 --- a/doc/user/clusters/agent/install/_index.md +++ b/doc/user/clusters/agent/install/_index.md @@ -46,12 +46,18 @@ Prerequisites: with the `--path` option, you must pass the same value to the `--manifest-path` option of the `glab cluster agent bootstrap` command. -To install the agent: +To install the agent, either: -- Run `glab cluster agent bootstrap`: +- Run `glab cluster agent bootstrap` within the directory of your Git repository of your target project: ```shell - glab cluster agent bootstrap --manifest-path + glab cluster agent bootstrap --manifest-path + ``` + +- Run `glab -R path-with-namespace cluster agent bootstrap` if you must run the command outside of the Git repo of your target project: + + ```shell + glab -R cluster agent bootstrap --manifest-path ``` By default, the command: diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1c585abbe37..bcbb26a6374 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -45818,6 +45818,9 @@ msgstr "" msgid "Pipeline|Pipeline" msgstr "" +msgid "Pipeline|Pipeline #%{pipeline_id}" +msgstr "" + msgid "Pipeline|Pipeline cannot be run." msgstr "" diff --git a/spec/features/work_items/user_views_work_items_list_spec.rb b/spec/features/work_items/user_views_work_items_list_spec.rb new file mode 100644 index 00000000000..441a4ea2fae --- /dev/null +++ b/spec/features/work_items/user_views_work_items_list_spec.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Work Items List', :js, feature_category: :team_planning do + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group, :public) } + let_it_be(:project) { create(:project, :public, group: group) } + + context 'when user is signed in as owner' do + before_all do + group.add_owner(user) + end + + before do + sign_in(user) + + visit project_work_items_path(project) + + wait_for_all_requests + end + + it 'shows message when there are no items in the list' do + expect(page).to have_content("No results found") + end + + context 'when the work items list page renders' do + let_it_be(:issue) { create(:work_item, :issue, project: project, title: 'There is an issue in the list') } + let_it_be(:task) { create(:work_item, :task, project: project, title: 'The task belongs to the issue') } + let_it_be(:incident) do + create(:work_item, :incident, project: project, title: 'An incident happened while loading the list') + end + + let!(:closed_issue) { create(:work_item, :closed, project: project) } + + it 'show actions based on user permissions' do + expect(page).to have_link('New item') + expect(page).to have_button('Bulk edit') + end + + it 'display the recent history widget when configured' do + within_testid('issuable-search-container') do + expect(page).to have_selector('[data-testid="history-icon"]') + end + end + + it 'show default sort order' do + within_testid('issuable-search-container') do + expect(page).to have_button('Created date') + expect(page).to have_button('Sort direction: Descending') + end + end + + it 'load the open items in the project' do + within('.issuable-list') do + expect(page).to have_link(issue.title) + .and have_link(task.title) + .and have_link(incident.title) + .and have_no_content(closed_issue.title) + end + end + + it 'load the closed items in the project' do + visit project_work_items_path(project, state: :closed) + + wait_for_all_requests + + within('.issuable-list') do + expect(page).to have_link(closed_issue.title) + .and have_no_link(issue.title) + end + end + + context 'with all the metadata' do + let_it_be(:label) { create(:label, title: 'Label 1', project: project) } + let_it_be(:milestone) { create(:milestone, project: project, title: 'v1') } + let_it_be(:task) do + create( + :work_item, + :task, + project: project, + labels: [label], + assignees: [user], + milestone: milestone, + due_date: '2025-12-31', + time_estimate: 12.hours + ) + end + + let_it_be(:award_emoji_upvote) { create(:award_emoji, :upvote, user: user, awardable: task) } + let_it_be(:award_emoji_downvote) { create(:award_emoji, :downvote, user: user, awardable: task) } + + it 'display available metadata' do + within(all('[data-testid="issuable-container"]')[0]) do + expect(page).to have_link(milestone.title) + .and have_link(label.name) + + expect(find_by_testid('issuable-due-date-title').text).to have_text('Dec 31, 2025') + + expect(page).to have_link(user.name, href: user_path(user)) + expect(find_by_testid('time-estimate-title').text).to have_text('4h') + expect(page).to have_text(%r{created .* by #{task.author.name}}) + expect(page).to have_selector('.issuable-meta [data-testid="issuable-upvotes"]') + end + end + end + end + end + + context 'when user is not signed in' do + let_it_be(:confidential_issue) do + create(:work_item, :issue, :confidential, project: project, title: 'Confidential issue') + end + + before do + visit project_work_items_path(project) + + wait_for_all_requests + end + + it 'shows actions based on user permissions' do + expect(page).not_to have_button('New item') + expect(page).not_to have_button('Bulk edit') + end + + it 'does not show confidential items' do + expect(page).not_to have_content(confidential_issue.title) + end + end +end diff --git a/spec/helpers/boards_helper_spec.rb b/spec/helpers/boards_helper_spec.rb index c1edef447aa..aa8c604f8c3 100644 --- a/spec/helpers/boards_helper_spec.rb +++ b/spec/helpers/boards_helper_spec.rb @@ -106,7 +106,7 @@ RSpec.describe BoardsHelper do allow(helper).to receive(:can?).with(user, :admin_label, project).and_return(false) allow(helper).to receive(:can?).with(user, :create_saved_replies, project.group).and_return(false) allow(helper).to receive(:can?).with(user, :create_saved_replies, project).and_return(false) - allow(helper).to receive(:can?).with(user, :create_work_item, project.group).and_return(false) + allow(helper).to receive(:can?).with(user, :create_work_item, project).and_return(false) allow(helper).to receive(:can?).with(user, :bulk_admin_epic, project).and_return(false) allow(helper).to receive(:can?).with(user, :admin_issue, project).and_return(false) allow(helper).to receive(:can?).with(user, :create_projects, project.group).and_return(false) diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb index d9d32303bec..351414c9a0a 100644 --- a/spec/helpers/commits_helper_spec.rb +++ b/spec/helpers/commits_helper_spec.rb @@ -399,4 +399,39 @@ RSpec.describe CommitsHelper do end end end + + describe '#path_to_browse_file_or_directory' do + let_it_be(:project) { create(:project, :repository) } + let(:ref) { 'my-branch' } + + before do + assign(:repo, project.repository) + end + + context 'if path is empty' do + let(:path) { '' } + + it 'links to tree at root directory' do + expect(helper.path_to_browse_file_or_directory(project, ref, path)).to eq("/#{project.full_path}/-/tree/#{ref}") + end + end + + context 'if the path is a directory' do + let(:path) { 'path/to/directory' } + + it 'links to tree at path' do + allow(helper).to receive(:commit_blob).and_return(nil) + expect(helper.path_to_browse_file_or_directory(project, ref, path)).to eq("/#{project.full_path}/-/tree/#{ref}/#{path}") + end + end + + context 'if the path is a file' do + let(:path) { 'path/to/file.txt' } + + it 'links to blob at path' do + allow(helper).to receive(:commit_blob).and_return(true) + expect(helper.path_to_browse_file_or_directory(project, ref, path)).to eq("/#{project.full_path}/-/blob/#{ref}/#{path}") + end + end + end end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 1a6b3dc1868..528a8d07f4a 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'parallel' +require 'timeout' require_relative 'gitaly_setup' require_relative '../../../lib/gitlab/setup_helper' @@ -186,9 +187,22 @@ module TestEnv return if gdk_path.empty? Bundler.with_unbundled_env do - success = system(gdk_path, 'send-telemetry', 'rspec_setup_duration', duration.to_s) - warn "Failed to send RSpec setup time via telemetry command." unless success + gitlab_sha = begin + result = IO.popen(%w[git rev-parse HEAD], chdir: Rails.root) { |p| p.read.strip } + result.empty? ? 'unknown' : result + rescue StandardError => e + warn "Failed to get GitLab SHA: #{e.message}" + 'unknown' + end + + # Add timeout in case GDK command hangs unexpectedly + Timeout.timeout(2) do + success = system(gdk_path, 'send-telemetry', 'rspec_setup_duration', duration.to_s, "--extra=gitlab_sha:#{gitlab_sha}") + warn "Failed to send RSpec setup time via telemetry command." unless success + end end + rescue Timeout::Error + warn "Sending telemetry timed out." rescue StandardError => e warn "Failed to send telemetry: #{e.message}" end diff --git a/spec/tooling/lib/tooling/predictive_tests_spec.rb b/spec/tooling/lib/tooling/predictive_tests_spec.rb index cc3de8c5e1b..bd7308febf7 100644 --- a/spec/tooling/lib/tooling/predictive_tests_spec.rb +++ b/spec/tooling/lib/tooling/predictive_tests_spec.rb @@ -145,7 +145,7 @@ RSpec.describe Tooling::PredictiveTests, feature_category: :tooling do expect(event_tracker).to have_received(:send_event).with( "glci_predictive_tests_count", label: "test-count", - value: File.read(matching_tests.path).lines.length, + value: File.read(matching_tests.path).split(" ").length, property: "described_class" ) end diff --git a/spec/views/projects/commits/show.html.haml_spec.rb b/spec/views/projects/commits/show.html.haml_spec.rb index 9393ba046dc..96806aada13 100644 --- a/spec/views/projects/commits/show.html.haml_spec.rb +++ b/spec/views/projects/commits/show.html.haml_spec.rb @@ -8,11 +8,15 @@ RSpec.describe 'projects/commits/show.html.haml' do let(:commits) { [commit] } let(:commit) { project.commit } let(:path) { 'path/to/doc.md' } + let(:ref) { "master" } before do assign(:project, project) + assign(:path, path) assign(:id, path) + assign(:ref, ref) assign(:repository, project.repository) + assign(:repo, project.repository) assign(:commits, commits) assign(:hidden_commit_count, 0) @@ -23,6 +27,7 @@ RSpec.describe 'projects/commits/show.html.haml' do allow(view).to receive(:current_user).and_return(nil) allow(view).to receive(:namespace_project_signatures_path).and_return("/") + allow(view).to receive(:commit_blob).and_return(true) end context 'tree controls' do @@ -33,6 +38,10 @@ RSpec.describe 'projects/commits/show.html.haml' do it 'renders atom feed button with matching path' do expect(rendered).to have_link(href: "#{project_commits_path(project, path)}?format=atom") end + + it 'renders "Browse files" button with link to blob or tree at path' do + expect(rendered).to have_link("Browse files", href: "/#{project.full_path}/-/blob/#{ref}/#{path}") + end end context 'commits date headers' do diff --git a/tooling/lib/tooling/predictive_tests.rb b/tooling/lib/tooling/predictive_tests.rb index fcc7ad52334..fc9bb3d47dd 100644 --- a/tooling/lib/tooling/predictive_tests.rb +++ b/tooling/lib/tooling/predictive_tests.rb @@ -68,7 +68,7 @@ module Tooling :predictive_tests_strategy def record_selected_test_count - test_count = File.read(rspec_matching_tests_path).lines.length + test_count = File.read(rspec_matching_tests_path).split(" ").length Tooling::Events::TrackPipelineEvents.new.send_event( "glci_predictive_tests_count", diff --git a/workhorse/internal/redis/keywatcher.go b/workhorse/internal/redis/keywatcher.go index 6c6c472b30b..5fb5c65536e 100644 --- a/workhorse/internal/redis/keywatcher.go +++ b/workhorse/internal/redis/keywatcher.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "io" "strings" "sync" "time" @@ -117,7 +118,13 @@ func (kw *KeyWatcher) receivePubSubStream(ctx context.Context, pubsub *redis.Pub for { msg, err := kw.conn.Receive(ctx) if err != nil { - log.WithError(fmt.Errorf("keywatcher: pubsub receive: %v", err)).Error() + // EOF errors can happen if the Redis server terminates an idle connection. + // These are expected if the server received no subscriptions during the + // Redis TIMEOUT period. The EOF message will have been reported by the + // go-redis logger. + if err != io.EOF { + log.WithError(fmt.Errorf("keywatcher: pubsub receive: %v", err)).Error() + } return nil } diff --git a/workhorse/internal/redis/redis.go b/workhorse/internal/redis/redis.go index 484ce7b5478..6b338927be8 100644 --- a/workhorse/internal/redis/redis.go +++ b/workhorse/internal/redis/redis.go @@ -15,6 +15,7 @@ import ( redis "github.com/redis/go-redis/v9" "gitlab.com/gitlab-org/gitlab/workhorse/internal/config" + workhorselog "gitlab.com/gitlab-org/gitlab/workhorse/internal/log" ) var ( @@ -69,6 +70,16 @@ type SentinelOptions struct { SentinelTLSConfig *tls.Config } +type redisClientLogger struct{} + +func (w *redisClientLogger) Printf(_ context.Context, format string, v ...interface{}) { + workhorselog.Info(fmt.Sprintf(format, v...)) +} + +func init() { + redis.SetLogger(&redisClientLogger{}) +} + // createDialer references https://github.com/redis/go-redis/blob/b1103e3d436b6fe98813ecbbe1f99dc8d59b06c9/options.go#L214 // it intercepts the error and tracks it via a Prometheus counter func createDialer(sentinels []string, tlsConfig *tls.Config) func(ctx context.Context, network, addr string) (net.Conn, error) {