Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-01-14 03:10:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-14 03:10:35 +0300
commite7cc427bc63dccac50cc3ccda2976befcea1ecf5 (patch)
tree975f48cc89009a9877c305ddf4dbea2a3e89d233
parentacba9e99b4f8bfeaaed143b72b07170a8506f893 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/issue_templates/Design Sprint.md203
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/navigation_tabs.vue37
-rw-r--r--app/models/user.rb9
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml4
-rw-r--r--changelogs/unreleased/215309-improve-the-error-message-username-has-already-been-taken-on-user-.yml5
-rw-r--r--changelogs/unreleased/230728-nav-tabs-migration.yml5
-rw-r--r--changelogs/unreleased/290008-fix-background-migration-arguments.yml5
-rw-r--r--changelogs/unreleased/yo-move-issue-gl-button.yml5
-rw-r--r--config/locales/en.yml1
-rw-r--r--db/post_migrate/20201207165956_remove_duplicate_services.rb15
-rw-r--r--db/post_migrate/20210112143418_remove_duplicate_services2.rb29
-rw-r--r--db/schema_migrations/202101121434181
-rw-r--r--doc/user/application_security/coverage_fuzzing/index.md2
-rw-r--r--lib/gitlab/background_migration/remove_duplicate_services.rb2
-rw-r--r--spec/frontend/deploy_keys/components/app_spec.js15
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js19
-rw-r--r--spec/frontend/vue_shared/components/navigation_tabs_spec.js82
-rw-r--r--spec/lib/gitlab/background_migration/remove_duplicate_services_spec.rb6
-rw-r--r--spec/migrations/20210112143418_remove_duplicate_services2_spec.rb (renamed from spec/migrations/20201207165956_remove_duplicate_services_spec.rb)4
-rw-r--r--spec/models/user_spec.rb2
-rw-r--r--spec/services/users/update_service_spec.rb2
23 files changed, 353 insertions, 104 deletions
diff --git a/.gitlab/issue_templates/Design Sprint.md b/.gitlab/issue_templates/Design Sprint.md
new file mode 100644
index 00000000000..787c4469b88
--- /dev/null
+++ b/.gitlab/issue_templates/Design Sprint.md
@@ -0,0 +1,203 @@
+<!-- Title: Design Sprint -->
+
+## Design Sprint Focus
+* [ ] Have you [determined that a Design Sprint is appropriate for this project](#anchor-tag-to-handbook-page)?
+<!-- What is the focus of the [Design Sprint](https://about.gitlab.com/handbook/product/product-processes/#design-sprint)? What problem area will you be solving for and who is the target user? -->
+
+## Objectives
+
+<!-- Try to describe the objectives of the Sprint in detail. eg "We want to introduce a new feature but we are unsure that we are thinking about the solution from the customer's perspective and through the Sprint we want to rethink the solution, prototype it and validate it with our customers" or "We are unhappy with the direction of one of our categories and we want to explore new directions with different stakeholders, reach to one solution and test it with users" or "Among the team we have different visions for a specific category and we want to work towards a solution we all support and test it with users". -->
+
+## Outputs
+
+- [ ] A User testing flow.
+- [ ] A Prototype to be tested with users.
+- [ ] User testing analysis.
+- [ ] (If the solution is viable) An epic or issue that describes the direction in details and the next steps
+- [ ] Necessary updates to the Handbook.
+
+## Design Sprint Details
+
+| Start | End |
+| ------ | ------ |
+| YYYY-MM-DD | YYYY-MM-DD |
+| TT:TT PST | TT:TT PST |
+
+
+### WHEN
+
+**Start date:**
+
+**End date:**
+
+**Reference time zone:**
+
+### WHERE
+
+**Zoom link:**
+
+### WHO
+
+ - `Name` `gitlab handle` - Facilitator
+ - `Name` `gitlab handle` - Decider (usually the Product Manager)
+ - `Name` `gitlab handle` - Co-decider (optional)
+ - `Name` `gitlab handle` - Sprint team member
+ - `Name` `gitlab handle` - Sprint team member
+ - `Name` `gitlab handle` - Sprint team member
+ - `Name` `gitlab handle` - Sprint team member
+ - `Name` `gitlab handle` - Sprint team member
+ - `Name` `gitlab handle` - Sprint team member
+ - `Name` `gitlab handle` - Co-facilitator (optional)
+
+## Tools
+Here is the list of tools for the Sprint preparation, collaboration and documentation. Prior to the Sprint make sure you have access to all of the following:
+
+* **GitLab**<br/>
+Each Sprint day outcomes and material will be documented in a separate issue under the Design Sprint epic.
+
+* **Mural** (You can join as anonymous but we need to be able to identify input against names, so please create an account beforehand.<br/>
+We will use Mural for most of the Sprint collaboration. Some of the things we will do in Mural:
+ * Create artifacts like affinity diagrams from participants' input
+ * Use post-its to comment on each other's points and to add notes
+ * Vote on ideas and solutions
+ * Create the first draft of the prototype.
+The Mural link to the collaboration project will be provided in the issue before the start of the Design Sprint.
+
+* **Video and/or screen recording tool** (Loom, Quicktime, Zoom or another tool you are using).<br/>
+As part of the pre-Sprint homework, you will be asked to record a short Lightning Walkthrough video. You can use any tool you feel comfortable with as long as it can capture your screen, mouse pointer and your audio.
+
+* **A4/Letter sized paper (preferably white blank), Sharpies/Pens** (please don't use a pencil because it doesn't create enough contrast for photos).<br/>
+Day 2 of the sprint involves some (async) ideation via sketching so you will need a writing utensil (Sharpies are preferred because they force you to draw at a lower fidelity because the small details aren't necessary at this point) and some paper. This is the most fun part of the Sprint where you get into a design thinking mindset and can appeal to your creative self. Don't worry, it's not about artistry, it's about ideas and collaboration.
+
+* **Camera (phone or other) or scanner**<br/>
+You will need to upload sketches as images for the facilitator to prepare the material before the next sync meeting. You can take a photo with your phone or use a scanner if available.
+
+* **Post-it notes (Optional)**<br/>
+If you enjoy taking notes using post-it notes make sure you have available some of them as well. The upside is that they will make you feel more like you are in a workshop and will help the ideas flow (I find that typing is distracting while ideating). The downside is that you will have to digitalise the ones you want to share with the team in Mural.
+
+## Artefacts & Pre-Read Material
+
+<!-- If there is material that will be useful for the participants to read before the Design Sprint add here. -->
+
+### Handbook pages
+<!-- Add a link to the category vision from the handbook -->
+
+### Competitor resources
+<!-- Add any solutions by competitors that are relevant to the Design Sprint topic and could be used as source of inspiration. -->
+
+
+### Articles on Design Sprints
+ * [The Design Sprint](https://www.gv.com/sprint/)
+ * [The Ultimate Guide To Remote Design Sprints](https://drive.google.com/file/d/16bwrAqHVf8qxovd87Q7LdzqwAgy7a6Rx/view?usp=sharing)
+
+## Asyncronus tasks
+
+### Design Sprint preparation
+
+<!-- Replace the roles with GitLab handles to assign to specific participants -->
+
+- [ ] Finalise participant list - `decider` and `facilitator`
+- [ ] Create [participation form](https://docs.google.com/forms/d/e/1FAIpQLSc0_BNltvRW8yXXaJd8sIKzgDmrSGqILMfkoCJrAj6sFcsMcg/viewform?usp=sf_link) and send to participants (**deadline**: [date]) - `facilitator`
+- [ ] Promote this issue to an epic - `facilitator`
+- [ ] Create issues under the epic for the pre-workshop tasks: Expert interviews ([example](https://gitlab.com/groups/gitlab-org/configure/-/epics/3#note_332412524)), Lightning walkthroughs and How might we.. notetaking assignment ([example](https://gitlab.com/gitlab-org/configure/general/-/issues/52)), Voting How might we... notes assignment ([example](https://gitlab.com/gitlab-org/configure/general/-/issues/54)) - `facilitator`
+- [ ] Create sync meetings in calendar and invite all participants (**deadline**: [date]) - `decider` or `facilitator`
+- [ ] Block 2 hours for Sprint activities in calendar for the Sprint week - `all participants`
+- [ ] Prepare material and tools (eg. presentation templates, Google folders, Instructions videos etc) and Mural board from the [Mural template ](https://app.mural.co/invitation/mural/gitlab2474/1586990879319?sender=jmandell0210&key=03c25e92-9a43-4a3d-8907-6f0c3b094ab8) - facilitator
+- [ ] Finalize Agenda - `facilitator`
+- [ ] Run a test with material and tools - `facilitator`
+- [ ] Start user recruiting for prototype user testing (EOD 1) - `facilitator` or `decider`
+
+### Pre-Sprint activities (Homework exercises)
+
+Each exercise should be explained and documented in a separate issue. You can use the example issues above as templates.
+
+- [ ] Fill form and submit (**deadline**: [date]) - `all participants except the facilitator`
+- [ ] Expert interview analysis - `facilitator`
+- [ ] Lightning walkthrough videos (**deadline**: [date]) - `all participants except the facilitator`
+- [ ] How might we... notetaking assignment (**deadline**: [date]) - `all participants except the facilitator`
+- [ ] Voting How might we... notes assignment (**deadline**: [date]) - `all participants except the facilitator`
+- [ ] Add all required material to the Mural board (**deadline**: [date]) - `facilitator`
+
+### During Sprint activities
+
+- [ ] Organise user testing sessions - `facilitator` or `decider`
+- [ ] Create the Prototype to be tested and task list (End of Day 3) - `Product designer` or `Front end developer`
+- [ ] Run user testing sessions - `facilitator` or `decider`
+
+### Post-Sprint activities
+
+- [ ] Create a feedback issue for the Design Sprint - `facilitator` or `decider`
+- [ ] Analyse user testing results - `facilitator` or `decider`
+- [ ] Create report and share with the Design Sprint participants and wider team - `facilitator` or `decider`
+
+## Personas
+
+Deciding which persona we are focusing on will be part of the Day 1 discussions in the workshop. The personas we are going to consider are:
+
+<!-- Choose which personas could be target users so that you choose from this list during the Sprint. Personas are described at https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/
+
+* [Cameron (Compliance Manager)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#cameron-compliance-manager)
+* [Parker (Product Manager)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#parker-product-manager)
+* [Delaney (Development Team Lead)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#delaney-development-team-lead)
+* [Presley (Product Designer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#presley-product-designer)
+* [Sasha (Software Developer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sasha-software-developer)
+* [Devon (DevOps Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#devon-devops-engineer)
+* [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sidney-systems-administrator)
+* [Sam (Security Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sam-security-analyst)
+* [Rachel (Release Manager)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#rachel-release-manager)
+* [Alex (Security Operations Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#alex-security-operations-engineer)
+* [Simone (Software Engineer in Test)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#simone-software-engineer-in-test)
+* [Allison (Application Ops)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#allison-application-ops)
+* [Priyanka (Platform Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#priyanka-platform-engineer)
+* [Dana (Data Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#dana-data-analyst)
+* [Eddie (Content Editor)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#eddie-content-editor)
+-->
+
+## Agenda
+### Day 1
+
+ | Activity | Duration | Tool | Description |
+|---|---|---|---|
+| Warm-up exercise | 5 mins | Mural | Write 1 post-it answering the questions: <br/>"My name is…"<br/>"My role is…"<br/>“Something about myself you may not know is…”<br/>"My wish for this workshop is…" |
+| Summarise the async activities & complete Map | 20 mins | Mural | The Map is intended to show the focus of the Sprint and doesn't need to be complete or detailed. Steps:<br/> Go through the Map and the top voted How might we’s tree as a warm-up/reminder. <br/> • Make appropriate adjustments and additions to the map based on the reviews from the team. <br/> • Add the most voted HMWs to the most relevant area on the Map. If a HMW can go to more than one place, add it to the most left area. |
+| Long term goals/Deciding the Sprint goal | 15 mins | Mural | • Long term goal: Everyone spends 5 minutes in silence and writes one (max 2) long term goal post-it note for the Sprint. (5 mins ) <br/> • One by one everyone will read their goal aloud to the team. (5 mins) <br/> Everyone besides the decider will vote on the goal of the Sprint (1 dot). (4 minutes) <br/> The decider then makes the final decision on the long term goal of the Sprint. (1 min) |
+ | Sprint questions | 20 mins | Mural | • Referencing the Long Term goal, everyone will write 2-3 post-it note Sprint questions for the biggest challenges they think might stop us from achieving our long term goal (what might hold us back or hinder us from achieving this goal). The questions should start with “Can we...” (similarly to the HMW). (7 mins) <br/> • One by one everyone will read their Sprint questions aloud to the team. (5 mins) <br/> • Everyone (including the decider) votes on the top 3 questions they think we should focus on as Sprint challenges (3 dots). (5 mins) <br/> • Separate the 3 most voted questions and keep them on the side. (1 min) <br/> • Finally, the decider chooses one Sprint question that will be the question we will focus on more during the Sprint by placing a green smiley sticker on it. (1 min) <br/> • Move the long term goal and the Sprint questions to the dedicated Mural space, highlighting the ultimate Sprint question that the decider chose. (1 min) |
+| Recap day. <br/> Short intro to next day and share the video with the next day exercise instructions. | 5 mins | Mural, Zoom | Summarise activities of the day and decisions. Brief walkthrough of the next day's activities and wrap up the day. |
+
+### Day 2
+ | Activity | Duration | Tool | Description |
+|---|---|---|---|
+| Summary of Day 1 outcomes | 5 mins | Mural | Go through the previous day's activities, the Long term goal and the top voted Sprint questions, highlighting the ultimate Sprint question, and summarise the concept solution sketching homework exercise. |
+| Concept gallery review | 20 mins | Mural | • Everyone takes some time to read through and look at every aspect of each of the sketches in the Concept Gallery. The concept sketches are anonymous to avoid bias (15 mins). <br/> • The team will then vote on their favorite concepts and/or components of a concept via the red dots. When they see something that interests them and they think it will help solve the long term goal/challenges they can add one or more dots. They can use as many red dots as they want. Be frivolous when adding dots but if you really like or think something is important, add more to draw attention to it (5 mins).<br/> Note: If anyone has any questions about a concept sketch create a red sticky and write that question down placing it under the concept sketches. |
+| Speed critique | 5 mins | Mural | • The facilitator walks through each of the concepts, briefly summarizing each concept (to the best of their ability) with a focus on the areas that have been dotted. <br/> • During this time the facilitator will also try to answer any of the red post-it questions. <br/> • When the facilitator believes they’ve reached the end of their summary for that concept, ask the team if there was a concept that was voted on but not discussed or if the point of the red dot vote was missed in the discussion.
+| Straw Poll | ~15 mins | Mural | • All the participants, besides the Decider, vote using the larger green dot by adding their initials to it and placing it on the concept sketch they believe is the best one that will best fulfill the long term goal and challenges of this sprint and is worthy of being prototyped (2 mins) <br/> • Each participant will create a post-it note that explains their reasoning for choosing the concept. (5 mins) <br/> • Each participant will then get 1 minute to read through their post-it and attempt to sell their preferred concept to the Decider and the other participants. (5-10 mins) |
+| Super Vote (The Decider) | 10 mins | Mural | • The Decider makes their final decision of which of the concepts is the one to move forward with. <br/> • The decider can discuss their thought process and any questions with the rest of the participants. <br/> • They will get 2 green smiley stickers to vote with. Placing one on the concept they want to move forward with and the second, optional smiley, can be used to mark any other area of any other concept they think should also be incorporated into the prototype. |
+
+
+### Day 3
+ | Activity | Duration | Tool | Description |
+|---|---|---|---|
+| User test flow | 25 mins | Mural | • Each participant writes (on separate post-its) 6 steps/actions that represent a step of a flow (you can think of a high-level prototype flow) from start to finish. Place them in the appropriate location on the User Test section. (10 mins) <br/> • Each participant takes 1 minute to walk the team through their flows one-by-one (5-10 mins total). Note: It's best to have the Decider go last. <br/> • Voting: All the Sprint participants get one red dot (the Decider gets 2) to vote on the flow row they think is the best foundation for the prototype. <br/> • After everyone has voted the Decider will vote on the row they think is best with one dot using the second dot to, optionally, vote on an element of another flow they think should be incorporated into the prototype. (5 mins) <br/> • If the second dot is used copy the specific sticky into the main flow voted by the Decider. |
+| Storyboard | 45 mins | Mural | • Copy the winning flow from the User Test Flow exercise to the Storyboard/Prototype section placing each individual post-it note into its own container. <br/> • Look at the sketch concepts and move over any relevant screens that fulfill the needs of the sticky note in that container. You can move the entire concept or screen capture cut/paste parts of concepts. Note: Don’t add any unnecessary details or ideas that aren’t needed for the end result prototype <br/> • Fill in the details that are required for each step described in the sticky. |
+| Recap day | 5 mins | Mural, Zoom | Summarise activities of the day and decisions. Brief walkthrough of the next day's activities and wrap up the day. |
+
+### Day 4
+ | Activity | Duration | Tool | Description |
+|---|---|---|---|
+| Validate Prototype | 30 mins | Mural | • Go through the Prototype created by the Product designer or Front end developer and discuss any inaccuracies or missing content. |
+| Wrap up the Sprint | 15 mins | Zoom, GitLab | • Recap the Sprint and discuss next steps. Create user testing issues. |
+
+### Day 5
+ | Activity | Duration | Tool | Description |
+|---|---|---|---|
+| Prototype testing with 5 users | ~45 mins | Figma or code/Zoom | • Test the prototype with users. |
+
+## Ground Rules
+* Honor the Facilitator's directions. They're the guide for the entire process.
+* Minimise distractions: During the week you will need to dedicate some hours to the Sprint for async tasks and sync video conferences. During this time we recommend blocking time in your calendar and having devices or apps with notifications turned off during that time.
+* All opinions are valid and are equally important, however, the Decider has the ultimate, final decision.
+* Everyone is an active participant in a sync activity (with the exception of the Observers).
+* One conversation at a time.
+* Document as much as you can: We should have concrete outputs to share with broader team. Also interesting ideas or fixes should be documented to be transferred in issues for our backlog.
+* Stick to scheduled breaks during sync calls. The Facilitator will guide each session and set break times.
+* The Sprint is one of the few chances we get to work so closely together. Have fun!
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
index d6884ae121f..21993e2120a 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
@@ -126,7 +126,7 @@ export default {
return this.$apollo.queries.content.loading;
},
isBlobContentError() {
- return this.failureType === LOAD_FAILURE_NO_FILE || this.failureType === LOAD_FAILURE_UNKNOWN;
+ return this.failureType === LOAD_FAILURE_NO_FILE;
},
isCiConfigDataLoading() {
return this.$apollo.queries.ciConfigData.loading;
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
index 396fbd0a834..ec7c5764be1 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
@@ -279,7 +279,7 @@ export default {
<div class="pipelines-container">
<div
v-if="shouldRenderTabs || shouldRenderButtons"
- class="top-area scrolling-tabs-container inner-page-scroll-tabs"
+ class="top-area scrolling-tabs-container inner-page-scroll-tabs gl-border-none"
>
<div class="fade-left"><gl-icon name="chevron-lg-left" :size="12" /></div>
<div class="fade-right"><gl-icon name="chevron-lg-right" :size="12" /></div>
diff --git a/app/assets/javascripts/vue_shared/components/navigation_tabs.vue b/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
index 3749888ee36..653ee7f20e9 100644
--- a/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
+++ b/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+import { GlBadge, GlTabs, GlTab } from '@gitlab/ui';
/**
* Given an array of tabs, renders non linked bootstrap tabs.
@@ -23,6 +24,11 @@ import $ from 'jquery';
*/
export default {
name: 'NavigationTabs',
+ components: {
+ GlBadge,
+ GlTabs,
+ GlTab,
+ },
props: {
tabs: {
type: Array,
@@ -50,24 +56,21 @@ export default {
};
</script>
<template>
- <ul class="nav-links scrolling-tabs separator">
- <li
+ <gl-tabs class="gl-display-flex gl-w-full" nav-class="gl-border-0!">
+ <gl-tab
v-for="(tab, i) in tabs"
:key="i"
- :class="{
- active: tab.isActive,
- }"
+ :title-link-class="`js-${scope}-tab-${tab.scope} gl-display-inline-flex`"
+ :title-link-attributes="{ 'data-testid': `${scope}-tab-${tab.scope}` }"
+ :active="tab.isActive"
+ @click="onTabClick(tab)"
>
- <a
- :class="`js-${scope}-tab-${tab.scope}`"
- :data-testid="`${scope}-tab-${tab.scope}`"
- role="button"
- @click="onTabClick(tab)"
- >
- {{ tab.name }}
-
- <span v-if="shouldRenderBadge(tab.count)" class="badge badge-pill"> {{ tab.count }} </span>
- </a>
- </li>
- </ul>
+ <template #title>
+ <span class="gl-mr-2"> {{ tab.name }} </span>
+ <gl-badge v-if="shouldRenderBadge(tab.count)" size="sm" class="gl-tab-counter-badge">{{
+ tab.count
+ }}</gl-badge>
+ </template>
+ </gl-tab>
+ </gl-tabs>
</template>
diff --git a/app/models/user.rb b/app/models/user.rb
index c6be3d6a839..b4ec6064ff8 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1378,7 +1378,14 @@ class User < ApplicationRecord
def set_username_errors
namespace_path_errors = self.errors.delete(:"namespace.path")
- self.errors[:username].concat(namespace_path_errors) if namespace_path_errors
+
+ return unless namespace_path_errors&.any?
+
+ if namespace_path_errors.include?('has already been taken') && !User.exists?(username: username)
+ self.errors.add(:base, :username_exists_as_a_different_namespace)
+ else
+ self.errors[:username].concat(namespace_path_errors)
+ end
end
def username_changed_hook
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 8fe278a9695..f69af45af9d 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -154,7 +154,7 @@
.sidebar-collapsed-icon{ data: { toggle: 'tooltip', placement: 'left', container: 'body', boundary: 'viewport' }, title: _('Move issue') }
= custom_icon('icon_arrow_right')
.dropdown.sidebar-move-issue-dropdown.hide-collapsed
- %button.btn.btn-default.btn-block.js-sidebar-dropdown-toggle.js-move-issue{ type: 'button',
+ %button.gl-button.btn.btn-default.btn-block.js-sidebar-dropdown-toggle.js-move-issue{ type: 'button',
data: { toggle: 'dropdown', display: 'static', track_label: "right_sidebar", track_property: "move_issue", track_event: "click_button", track_value: "" } }
= _('Move issue')
.dropdown-menu.dropdown-menu-selectable.dropdown-extended-height
@@ -163,7 +163,7 @@
= dropdown_content
= dropdown_loading
= dropdown_footer add_content_class: true do
- %button.btn.btn-success.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ type: 'button', disabled: true }
+ %button.gl-button.btn.btn-success.sidebar-move-issue-confirmation-button.js-move-issue-confirmation-button{ type: 'button', disabled: true }
= _('Move')
= loading_icon(css_class: 'gl-vertical-align-text-bottom sidebar-move-issue-confirmation-loading-icon')
diff --git a/changelogs/unreleased/215309-improve-the-error-message-username-has-already-been-taken-on-user-.yml b/changelogs/unreleased/215309-improve-the-error-message-username-has-already-been-taken-on-user-.yml
new file mode 100644
index 00000000000..b966d28a6ec
--- /dev/null
+++ b/changelogs/unreleased/215309-improve-the-error-message-username-has-already-been-taken-on-user-.yml
@@ -0,0 +1,5 @@
+---
+title: Improve error message when username and namespace conflict
+merge_request: 47537
+author:
+type: changed
diff --git a/changelogs/unreleased/230728-nav-tabs-migration.yml b/changelogs/unreleased/230728-nav-tabs-migration.yml
new file mode 100644
index 00000000000..969e805ef2b
--- /dev/null
+++ b/changelogs/unreleased/230728-nav-tabs-migration.yml
@@ -0,0 +1,5 @@
+---
+title: Convert navigation_tabs.vue to gl-tabs
+merge_request: 47841
+author:
+type: other
diff --git a/changelogs/unreleased/290008-fix-background-migration-arguments.yml b/changelogs/unreleased/290008-fix-background-migration-arguments.yml
new file mode 100644
index 00000000000..a9b5a8a2b75
--- /dev/null
+++ b/changelogs/unreleased/290008-fix-background-migration-arguments.yml
@@ -0,0 +1,5 @@
+---
+title: Fix argument type for background migration
+merge_request: 51475
+author:
+type: fixed
diff --git a/changelogs/unreleased/yo-move-issue-gl-button.yml b/changelogs/unreleased/yo-move-issue-gl-button.yml
new file mode 100644
index 00000000000..618b1455b8f
--- /dev/null
+++ b/changelogs/unreleased/yo-move-issue-gl-button.yml
@@ -0,0 +1,5 @@
+---
+title: Add `gl-button` to move issue button in issue sidebar
+merge_request: 51285
+author: Yogi (@yo)
+type: other
diff --git a/config/locales/en.yml b/config/locales/en.yml
index fb024b7ba2a..4615c9a7390 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -169,6 +169,7 @@ en:
format: "%{attribute} %{message}"
messages:
label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
+ username_exists_as_a_different_namespace: A user, alias, or group already exists with that username.
wrong_size: "is the wrong size (should be %{file_size})"
size_too_small: "is too small (should be at least %{file_size})"
size_too_big: "is too big (should be at most %{file_size})"
diff --git a/db/post_migrate/20201207165956_remove_duplicate_services.rb b/db/post_migrate/20201207165956_remove_duplicate_services.rb
index 7f0bd969ce5..1659b9a2095 100644
--- a/db/post_migrate/20201207165956_remove_duplicate_services.rb
+++ b/db/post_migrate/20201207165956_remove_duplicate_services.rb
@@ -1,25 +1,12 @@
# frozen_string_literal: true
class RemoveDuplicateServices < ActiveRecord::Migration[6.0]
- include Gitlab::Database::MigrationHelpers
-
DOWNTIME = false
- INTERVAL = 2.minutes
- BATCH_SIZE = 5_000
- MIGRATION = 'RemoveDuplicateServices'
disable_ddl_transaction!
def up
- project_ids_with_duplicates = Gitlab::BackgroundMigration::RemoveDuplicateServices::Service.project_ids_with_duplicates
-
- project_ids_with_duplicates.each_batch(of: BATCH_SIZE, column: :project_id) do |batch, index|
- migrate_in(
- INTERVAL * index,
- MIGRATION,
- batch.pluck(:project_id)
- )
- end
+ # noop, replaced by 20210112143418_remove_duplicate_services.rb
end
def down
diff --git a/db/post_migrate/20210112143418_remove_duplicate_services2.rb b/db/post_migrate/20210112143418_remove_duplicate_services2.rb
new file mode 100644
index 00000000000..83d92a78473
--- /dev/null
+++ b/db/post_migrate/20210112143418_remove_duplicate_services2.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+# This replaces the previous post-deployment migration 20201207165956_remove_duplicate_services_spec.rb,
+# we have to run this again due to a bug in how we were receiving the arguments in the background migration.
+class RemoveDuplicateServices2 < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INTERVAL = 2.minutes
+ BATCH_SIZE = 5_000
+ MIGRATION = 'RemoveDuplicateServices'
+
+ disable_ddl_transaction!
+
+ def up
+ project_ids_with_duplicates = Gitlab::BackgroundMigration::RemoveDuplicateServices::Service.project_ids_with_duplicates
+
+ project_ids_with_duplicates.each_batch(of: BATCH_SIZE, column: :project_id) do |batch, index|
+ migrate_in(
+ INTERVAL * index,
+ MIGRATION,
+ batch.pluck(:project_id)
+ )
+ end
+ end
+
+ def down
+ end
+end
diff --git a/db/schema_migrations/20210112143418 b/db/schema_migrations/20210112143418
new file mode 100644
index 00000000000..3183c8cf220
--- /dev/null
+++ b/db/schema_migrations/20210112143418
@@ -0,0 +1 @@
+05d45e25ab9ef1565c04ca6515e0b01f2f98c5e98b1eeb09fa9dd43ebbe3c4d0 \ No newline at end of file
diff --git a/doc/user/application_security/coverage_fuzzing/index.md b/doc/user/application_security/coverage_fuzzing/index.md
index 2eefc1bffc1..469945246c1 100644
--- a/doc/user/application_security/coverage_fuzzing/index.md
+++ b/doc/user/application_security/coverage_fuzzing/index.md
@@ -29,8 +29,8 @@ Docker image with the fuzz engine to run your app.
| GoLang | [go-fuzz (libFuzzer support)](https://github.com/dvyukov/go-fuzz) | [go-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/go-fuzzing-example) |
| Swift | [libfuzzer](https://github.com/apple/swift/blob/master/docs/libFuzzerIntegration.md) | [swift-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/swift-fuzzing-example) |
| Rust | [cargo-fuzz (libFuzzer support)](https://github.com/rust-fuzz/cargo-fuzz) | [rust-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/rust-fuzzing-example) |
-| Java | [JQF](https://github.com/rohanpadhye/JQF) | [java-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/java-fuzzing-example) |
| Java | [javafuzz](https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/javafuzz) (recommended) | [javafuzz-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/javafuzz-fuzzing-example) |
+| Java | [JQF](https://github.com/rohanpadhye/JQF) (not preferred) | [jqf-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/java-fuzzing-example) |
| JavaScript | [jsfuzz](https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/jsfuzz)| [jsfuzz-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/jsfuzz-fuzzing-example) |
| Python | [pythonfuzz](https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/pythonfuzz)| [pythonfuzz-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/pythonfuzz-fuzzing-example) |
diff --git a/lib/gitlab/background_migration/remove_duplicate_services.rb b/lib/gitlab/background_migration/remove_duplicate_services.rb
index 528c3a87536..59fb9143a72 100644
--- a/lib/gitlab/background_migration/remove_duplicate_services.rb
+++ b/lib/gitlab/background_migration/remove_duplicate_services.rb
@@ -29,7 +29,7 @@ module Gitlab
end
end
- def perform(project_ids)
+ def perform(*project_ids)
types_with_duplicates = Service.types_with_duplicates(project_ids).pluck(:project_id, :type)
types_with_duplicates.each do |project_id, type|
diff --git a/spec/frontend/deploy_keys/components/app_spec.js b/spec/frontend/deploy_keys/components/app_spec.js
index 8d37b5ce38f..479320f92f2 100644
--- a/spec/frontend/deploy_keys/components/app_spec.js
+++ b/spec/frontend/deploy_keys/components/app_spec.js
@@ -35,7 +35,7 @@ describe('Deploy keys app component', () => {
});
const findLoadingIcon = () => wrapper.find('.gl-spinner');
- const findKeyPanels = () => wrapper.findAll('.deploy-keys .nav-links li');
+ const findKeyPanels = () => wrapper.findAll('.deploy-keys .gl-tabs-nav li');
it('renders loading icon while waiting for request', () => {
mock.onGet(TEST_ENDPOINT).reply(() => new Promise());
@@ -54,17 +54,14 @@ describe('Deploy keys app component', () => {
});
it.each`
- selector | label | count
- ${'.js-deployKeys-tab-enabled_keys'} | ${'Enabled deploy keys'} | ${1}
- ${'.js-deployKeys-tab-available_project_keys'} | ${'Privately accessible deploy keys'} | ${0}
- ${'.js-deployKeys-tab-public_keys'} | ${'Publicly accessible deploy keys'} | ${1}
- `('$selector title is $label with keys count equal to $count', ({ selector, label, count }) => {
+ selector
+ ${'.js-deployKeys-tab-enabled_keys'}
+ ${'.js-deployKeys-tab-available_project_keys'}
+ ${'.js-deployKeys-tab-public_keys'}
+ `('$selector title exists', ({ selector }) => {
return mountComponent().then(() => {
const element = wrapper.find(selector);
expect(element.exists()).toBe(true);
- expect(element.text().trim()).toContain(label);
-
- expect(element.find('.badge').text().trim()).toBe(count.toString());
});
});
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index 50434a83ffe..9728064a42d 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -448,16 +448,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
const expectedAlertMsg =
'There is no .gitlab-ci.yml file in this repository, please add one and visit the Pipeline Editor again.';
- it('does not show editor or commit form', async () => {
- mockBlobContentData.mockRejectedValueOnce(new Error('My error!'));
- createComponentWithApollo();
- await waitForPromises();
-
- expect(findEditorLite().exists()).toBe(false);
- expect(findTextEditor().exists()).toBe(false);
- });
-
- it('shows a 404 error message', async () => {
+ it('shows a 404 error message and does not show editor or commit form', async () => {
mockBlobContentData.mockRejectedValueOnce({
response: {
status: httpStatusCodes.NOT_FOUND,
@@ -468,9 +459,11 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
await waitForPromises();
expect(findAlert().text()).toBe(expectedAlertMsg);
+ expect(findEditorLite().exists()).toBe(false);
+ expect(findTextEditor().exists()).toBe(false);
});
- it('shows a 400 error message', async () => {
+ it('shows a 400 error message and does not show editor or commit form', async () => {
mockBlobContentData.mockRejectedValueOnce({
response: {
status: httpStatusCodes.BAD_REQUEST,
@@ -481,6 +474,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
await waitForPromises();
expect(findAlert().text()).toBe(expectedAlertMsg);
+ expect(findEditorLite().exists()).toBe(false);
+ expect(findTextEditor().exists()).toBe(false);
});
it('shows a unkown error message', async () => {
@@ -489,6 +484,8 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
await waitForPromises();
expect(findAlert().text()).toBe('The CI configuration was not loaded, please try again.');
+ expect(findEditorLite().exists()).toBe(true);
+ expect(findTextEditor().exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/vue_shared/components/navigation_tabs_spec.js b/spec/frontend/vue_shared/components/navigation_tabs_spec.js
index 561456d614e..b1119bfb150 100644
--- a/spec/frontend/vue_shared/components/navigation_tabs_spec.js
+++ b/spec/frontend/vue_shared/components/navigation_tabs_spec.js
@@ -1,64 +1,68 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import navigationTabs from '~/vue_shared/components/navigation_tabs.vue';
+import { mount } from '@vue/test-utils';
+import { GlTab } from '@gitlab/ui';
+import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
describe('navigation tabs component', () => {
- let vm;
- let Component;
- let data;
+ let wrapper;
- beforeEach(() => {
- data = [
- {
- name: 'All',
- scope: 'all',
- count: 1,
- isActive: true,
- },
- {
- name: 'Pending',
- scope: 'pending',
- count: 0,
- isActive: false,
- },
- {
- name: 'Running',
- scope: 'running',
- isActive: false,
+ const data = [
+ {
+ name: 'All',
+ scope: 'all',
+ count: 1,
+ isActive: true,
+ },
+ {
+ name: 'Pending',
+ scope: 'pending',
+ count: 0,
+ isActive: false,
+ },
+ {
+ name: 'Running',
+ scope: 'running',
+ isActive: false,
+ },
+ ];
+
+ const createComponent = () => {
+ wrapper = mount(NavigationTabs, {
+ propsData: {
+ tabs: data,
+ scope: 'pipelines',
},
- ];
+ });
+ };
- Component = Vue.extend(navigationTabs);
- vm = mountComponent(Component, { tabs: data, scope: 'pipelines' });
+ beforeEach(() => {
+ createComponent();
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
+ wrapper = null;
});
it('should render tabs', () => {
- expect(vm.$el.querySelectorAll('li').length).toEqual(data.length);
+ expect(wrapper.findAll(GlTab)).toHaveLength(data.length);
});
it('should render active tab', () => {
- expect(vm.$el.querySelector('.active .js-pipelines-tab-all')).toBeDefined();
+ expect(wrapper.find('.js-pipelines-tab-all').classes('active')).toBe(true);
});
it('should render badge', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-all .badge').textContent.trim()).toEqual('1');
- expect(vm.$el.querySelector('.js-pipelines-tab-pending .badge').textContent.trim()).toEqual(
- '0',
- );
+ expect(wrapper.find('.js-pipelines-tab-all').text()).toContain('1');
+ expect(wrapper.find('.js-pipelines-tab-pending').text()).toContain('0');
});
it('should not render badge', () => {
- expect(vm.$el.querySelector('.js-pipelines-tab-running .badge')).toEqual(null);
+ expect(wrapper.find('.js-pipelines-tab-running .badge').exists()).toBe(false);
});
- it('should trigger onTabClick', () => {
- jest.spyOn(vm, '$emit').mockImplementation(() => {});
- vm.$el.querySelector('.js-pipelines-tab-pending').click();
+ it('should trigger onTabClick', async () => {
+ await wrapper.find('.js-pipelines-tab-pending').trigger('click');
- expect(vm.$emit).toHaveBeenCalledWith('onChangeTab', 'pending');
+ expect(wrapper.emitted('onChangeTab')).toEqual([['pending']]);
});
});
diff --git a/spec/lib/gitlab/background_migration/remove_duplicate_services_spec.rb b/spec/lib/gitlab/background_migration/remove_duplicate_services_spec.rb
index fffec391205..391b27b28e6 100644
--- a/spec/lib/gitlab/background_migration/remove_duplicate_services_spec.rb
+++ b/spec/lib/gitlab/background_migration/remove_duplicate_services_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe Gitlab::BackgroundMigration::RemoveDuplicateServices, :migration,
end
expect do
- subject.perform([project2.id, project3.id])
+ subject.perform(project2.id, project3.id)
end.to change { services.count }.from(21).to(12)
services1 = services.where(project_id: project1.id)
@@ -109,13 +109,13 @@ RSpec.describe Gitlab::BackgroundMigration::RemoveDuplicateServices, :migration,
it 'does not delete services without duplicates' do
expect do
- subject.perform([project1.id, project4.id])
+ subject.perform(project1.id, project4.id)
end.not_to change { services.count }
end
it 'only deletes duplicate services for the current batch' do
expect do
- subject.perform([project2.id])
+ subject.perform(project2.id)
end.to change { services.count }.by(-3)
end
end
diff --git a/spec/migrations/20201207165956_remove_duplicate_services_spec.rb b/spec/migrations/20210112143418_remove_duplicate_services2_spec.rb
index b45deeb70f3..9a57d9bf84d 100644
--- a/spec/migrations/20201207165956_remove_duplicate_services_spec.rb
+++ b/spec/migrations/20210112143418_remove_duplicate_services2_spec.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20201207165956_remove_duplicate_services.rb')
+require Rails.root.join('db', 'post_migrate', '20210112143418_remove_duplicate_services2.rb')
-RSpec.describe RemoveDuplicateServices do
+RSpec.describe RemoveDuplicateServices2 do
let_it_be(:namespaces) { table(:namespaces) }
let_it_be(:projects) { table(:projects) }
let_it_be(:services) { table(:services) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index c5e942e673b..0935d3576a4 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -4280,7 +4280,7 @@ RSpec.describe User do
it 'adds the namespace errors to the user' do
user.update(username: new_username)
- expect(user.errors.full_messages.first).to eq('Username has already been taken')
+ expect(user.errors.full_messages.first).to eq('A user, alias, or group already exists with that username.')
end
end
end
diff --git a/spec/services/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb
index 274c44394f3..b30b7e6eb56 100644
--- a/spec/services/users/update_service_spec.rb
+++ b/spec/services/users/update_service_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe Users::UpdateService do
result = update_user(user, { username: 'taken' })
end.not_to change { user.reload.username }
expect(result[:status]).to eq(:error)
- expect(result[:message]).to eq('Username has already been taken')
+ expect(result[:message]).to eq('A user, alias, or group already exists with that username.')
end
it 'updates the status if status params were given' do