From 5d9eecab8159a9e87a59d38e34343dead0e2cd8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=A4mmerle?= Date: Fri, 1 Jun 2018 19:36:51 +0000 Subject: Update template name via sentence case --- .gitlab/issue_templates/Research Proposal.md | 17 ----------------- .gitlab/issue_templates/Research proposal.md | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 .gitlab/issue_templates/Research Proposal.md create mode 100644 .gitlab/issue_templates/Research proposal.md diff --git a/.gitlab/issue_templates/Research Proposal.md b/.gitlab/issue_templates/Research Proposal.md deleted file mode 100644 index 5676656793d..00000000000 --- a/.gitlab/issue_templates/Research Proposal.md +++ /dev/null @@ -1,17 +0,0 @@ -### Background: - -(Include problem, use cases, benefits, and/or goals) - -**What questions are you trying to answer?** - -**Are you looking to verify an existing hypothesis or uncover new issues you should be exploring?** - -**What is the backstory of this project and how does it impact the approach?** - -**What do you already know about the areas you are exploring?** - -**What does success look like at the end of the project?** - -### Links / references: - -/label ~"UX research" diff --git a/.gitlab/issue_templates/Research proposal.md b/.gitlab/issue_templates/Research proposal.md new file mode 100644 index 00000000000..5676656793d --- /dev/null +++ b/.gitlab/issue_templates/Research proposal.md @@ -0,0 +1,17 @@ +### Background: + +(Include problem, use cases, benefits, and/or goals) + +**What questions are you trying to answer?** + +**Are you looking to verify an existing hypothesis or uncover new issues you should be exploring?** + +**What is the backstory of this project and how does it impact the approach?** + +**What do you already know about the areas you are exploring?** + +**What does success look like at the end of the project?** + +### Links / references: + +/label ~"UX research" -- cgit v1.2.1 From d8b87f3ef911e27c88886741c5c40f7a1d345161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=A4mmerle?= Date: Fri, 1 Jun 2018 19:39:38 +0000 Subject: Update template name via sentence case (security) --- .../issue_templates/Security Developer Workflow.md | 70 --------------------- .../issue_templates/Security developer workflow.md | 71 ++++++++++++++++++++++ 2 files changed, 71 insertions(+), 70 deletions(-) delete mode 100644 .gitlab/issue_templates/Security Developer Workflow.md create mode 100644 .gitlab/issue_templates/Security developer workflow.md diff --git a/.gitlab/issue_templates/Security Developer Workflow.md b/.gitlab/issue_templates/Security Developer Workflow.md deleted file mode 100644 index 0c878dbf64c..00000000000 --- a/.gitlab/issue_templates/Security Developer Workflow.md +++ /dev/null @@ -1,70 +0,0 @@ - - -### Prior to the security release - -- [ ] Read the [security process for developers] if you are not familiar with it. -- [ ] Link to the original issue adding it to the [links section](#links) -- [ ] Run `scripts/security-harness` in the CE, EE, and/or Omnibus to prevent pushing to any remote besides `dev.gitlab.org` -- [ ] Create an MR targetting `org` `master`, prefixing your branch with `security-` -- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]` -- [ ] Add a link to the MR to the [links section](#links) -- [ ] Add a link to an EE MR if required -- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**. -- [ ] Assign the MR to a RM once is reviewed and ready to be merged. Check the [RM list] to see who to ping. - -#### Backports - -- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases - - [ ] At this point, it might be easy to squash the commits from the MR into one - - You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [seckpick documentation] - - [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable) - - [ ] Create each MR targetting the security branch `security-X-Y` - - [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR -- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager. - -[seckpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script - -#### Documentation and final details - -- [ ] Check the topic on #security to see when the next release is going to happen and add a link to the [links section](#links) -- [ ] Find out the versions affected (the Git history of the files affected may help you with this) and add them to the [details section](#details) -- [ ] Fill in any upgrade notes that users may need to take into account in the [details section](#details) -- [ ] Add Yes/No and further details if needed to the migration and settings columns in the [details section](#details) -- [ ] Add the nickname of the external user who found the issue (and/or HackerOne profile) to the Thanks row in the [details section](#details) - -### Summary -#### Links - -| Description | Link | -| -------- | -------- | -| Original issue | #TODO | -| Security release issue | #TODO | -| `master` MR | !TODO | -| `master` MR (EE) | !TODO | -| `Backport X.Y` MR | !TODO | -| `Backport X.Y` MR | !TODO | -| `Backport X.Y` MR | !TODO | -| `Backport X.Y` MR (EE) | !TODO | -| `Backport X.Y` MR (EE) | !TODO | -| `Backport X.Y` MR (EE) | !TODO | - -#### Details - -| Description | Details | Further details| -| -------- | -------- | -------- | -| Versions affected | X.Y | | -| Upgrade notes | | | -| GitLab Settings updated | Yes/No| | -| Migration required | Yes/No | | -| Thanks | | | - -[security process for developers]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md -[RM list]: https://about.gitlab.com/release-managers/ - -/label ~security diff --git a/.gitlab/issue_templates/Security developer workflow.md b/.gitlab/issue_templates/Security developer workflow.md new file mode 100644 index 00000000000..c1f702e9385 --- /dev/null +++ b/.gitlab/issue_templates/Security developer workflow.md @@ -0,0 +1,71 @@ + + +### Prior to the security release + +- [ ] Read the [security process for developers] if you are not familiar with it. +- [ ] Link to the original issue adding it to the [links section](#links) +- [ ] Run `scripts/security-harness` in the CE, EE, and/or Omnibus to prevent pushing to any remote besides `dev.gitlab.org` +- [ ] Create an MR targetting `org` `master`, prefixing your branch with `security-` +- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]` +- [ ] Add a link to the MR to the [links section](#links) +- [ ] Add a link to an EE MR if required +- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**. +- [ ] Assign the MR to a RM once is reviewed and ready to be merged. Check the [RM list] to see who to ping. + +#### Backports + +- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases + - [ ] At this point, it might be easy to squash the commits from the MR into one + - You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [seckpick documentation] + - [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable) + - [ ] Create each MR targetting the security branch `security-X-Y` + - [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR +- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager. + +[seckpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#secpick-script + +#### Documentation and final details + +- [ ] Check the topic on #security to see when the next release is going to happen and add a link to the [links section](#links) +- [ ] Find out the versions affected (the Git history of the files affected may help you with this) and add them to the [details section](#details) +- [ ] Fill in any upgrade notes that users may need to take into account in the [details section](#details) +- [ ] Add Yes/No and further details if needed to the migration and settings columns in the [details section](#details) +- [ ] Add the nickname of the external user who found the issue (and/or HackerOne profile) to the Thanks row in the [details section](#details) + +### Summary + +#### Links + +| Description | Link | +| -------- | -------- | +| Original issue | #TODO | +| Security release issue | #TODO | +| `master` MR | !TODO | +| `master` MR (EE) | !TODO | +| `Backport X.Y` MR | !TODO | +| `Backport X.Y` MR | !TODO | +| `Backport X.Y` MR | !TODO | +| `Backport X.Y` MR (EE) | !TODO | +| `Backport X.Y` MR (EE) | !TODO | +| `Backport X.Y` MR (EE) | !TODO | + +#### Details + +| Description | Details | Further details| +| -------- | -------- | -------- | +| Versions affected | X.Y | | +| Upgrade notes | | | +| GitLab Settings updated | Yes/No| | +| Migration required | Yes/No | | +| Thanks | | | + +[security process for developers]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md +[RM list]: https://about.gitlab.com/release-managers/ + +/label ~security -- cgit v1.2.1 From 38744526d8f2c4752a4c9a40080d13949be19994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=A4mmerle?= Date: Fri, 1 Jun 2018 19:44:07 +0000 Subject: Update template name via sentence case (database changes) --- .../merge_request_templates/Database Changes.md | 50 ---------------------- .../merge_request_templates/Database changes.md | 50 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 50 deletions(-) delete mode 100644 .gitlab/merge_request_templates/Database Changes.md create mode 100644 .gitlab/merge_request_templates/Database changes.md diff --git a/.gitlab/merge_request_templates/Database Changes.md b/.gitlab/merge_request_templates/Database Changes.md deleted file mode 100644 index 1c4f30d9320..00000000000 --- a/.gitlab/merge_request_templates/Database Changes.md +++ /dev/null @@ -1,50 +0,0 @@ -Add a description of your merge request here. Merge requests without an adequate -description will not be reviewed until one is added. - -## Database Checklist - -When adding migrations: - -- [ ] Updated `db/schema.rb` -- [ ] Added a `down` method so the migration can be reverted -- [ ] Added the output of the migration(s) to the MR body -- [ ] Added tests for the migration in `spec/migrations` if necessary (e.g. when migrating data) - -When adding or modifying queries to improve performance: - -- [ ] Included data that shows the performance improvement, preferably in the form of a benchmark -- [ ] Included the output of `EXPLAIN (ANALYZE, BUFFERS)` of the relevant queries - -When adding foreign keys to existing tables: - -- [ ] Included a migration to remove orphaned rows in the source table before adding the foreign key -- [ ] Removed any instances of `dependent: ...` that may no longer be necessary - -When adding tables: - -- [ ] Ordered columns based on the [Ordering Table Columns](https://docs.gitlab.com/ee/development/ordering_table_columns.html#ordering-table-columns) guidelines -- [ ] Added foreign keys to any columns pointing to data in other tables -- [ ] Added indexes for fields that are used in statements such as WHERE, ORDER BY, GROUP BY, and JOINs - -When removing columns, tables, indexes or other structures: - -- [ ] Removed these in a post-deployment migration -- [ ] Made sure the application no longer uses (or ignores) these structures - -## General Checklist - -- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary -- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html) -- [ ] API support added -- [ ] Tests added for this feature/bug -- Conform by the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html) - - [ ] Has been reviewed by a Backend maintainer - - [ ] Has been reviewed by a Database specialist -- [ ] Conform by the [merge request performance guides](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html) -- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides) -- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) -- [ ] Internationalization required/considered -- [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan -- [ ] End-to-end tests pass (`package-and-qa` manual pipeline job) - -/label ~database diff --git a/.gitlab/merge_request_templates/Database changes.md b/.gitlab/merge_request_templates/Database changes.md new file mode 100644 index 00000000000..d14d52e1b6b --- /dev/null +++ b/.gitlab/merge_request_templates/Database changes.md @@ -0,0 +1,50 @@ +Add a description of your merge request here. Merge requests without an adequate +description will not be reviewed until one is added. + +## Database checklist + +When adding migrations: + +- [ ] Updated `db/schema.rb` +- [ ] Added a `down` method so the migration can be reverted +- [ ] Added the output of the migration(s) to the MR body +- [ ] Added tests for the migration in `spec/migrations` if necessary (e.g. when migrating data) + +When adding or modifying queries to improve performance: + +- [ ] Included data that shows the performance improvement, preferably in the form of a benchmark +- [ ] Included the output of `EXPLAIN (ANALYZE, BUFFERS)` of the relevant queries + +When adding foreign keys to existing tables: + +- [ ] Included a migration to remove orphaned rows in the source table before adding the foreign key +- [ ] Removed any instances of `dependent: ...` that may no longer be necessary + +When adding tables: + +- [ ] Ordered columns based on the [Ordering Table Columns](https://docs.gitlab.com/ee/development/ordering_table_columns.html#ordering-table-columns) guidelines +- [ ] Added foreign keys to any columns pointing to data in other tables +- [ ] Added indexes for fields that are used in statements such as WHERE, ORDER BY, GROUP BY, and JOINs + +When removing columns, tables, indexes or other structures: + +- [ ] Removed these in a post-deployment migration +- [ ] Made sure the application no longer uses (or ignores) these structures + +## General checklist + +- [ ] [Changelog entry](https://docs.gitlab.com/ee/development/changelog.html) added, if necessary +- [ ] [Documentation created/updated](https://docs.gitlab.com/ee/development/doc_styleguide.html) +- [ ] API support added +- [ ] Tests added for this feature/bug +- Conform by the [code review guidelines](https://docs.gitlab.com/ee/development/code_review.html) + - [ ] Has been reviewed by a Backend maintainer + - [ ] Has been reviewed by a Database specialist +- [ ] Conform by the [merge request performance guides](https://docs.gitlab.com/ee/development/merge_request_performance_guidelines.html) +- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/CONTRIBUTING.md#style-guides) +- [ ] If you have multiple commits, please combine them into a few logically organized commits by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) +- [ ] Internationalization required/considered +- [ ] If paid feature, have we considered GitLab.com plan and how it works for groups and is there a design for promoting it to users who aren't on the correct plan +- [ ] End-to-end tests pass (`package-and-qa` manual pipeline job) + +/label ~database -- cgit v1.2.1 From 42785686f98b7bcb9576bd7c4be5db0b244f91cf Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Fri, 15 Jun 2018 15:39:50 +0100 Subject: Updated the design of milestone list status --- app/views/shared/_milestone_expired.html.haml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/views/shared/_milestone_expired.html.haml b/app/views/shared/_milestone_expired.html.haml index 5e9007aaaac..814eda5e540 100644 --- a/app/views/shared/_milestone_expired.html.haml +++ b/app/views/shared/_milestone_expired.html.haml @@ -1,7 +1,9 @@ -- if milestone.expired? and not milestone.closed? - %span.cred (Expired) -- if milestone.upcoming? - %span.clgray (Upcoming) - if milestone.due_date || milestone.start_date - %span + %div = milestone_date_range(milestone) +- if milestone.expired? and not milestone.closed? + .status-box.status-box-expired Expired +- if milestone.upcoming? + .status-box.status-box-mr-merged Upcoming +- if milestone.closed? + .status-box.status-box-closed Closed -- cgit v1.2.1 From bee7f064ad368b3f9f317439eb23bdff66ea9b3f Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Fri, 15 Jun 2018 15:41:47 +0100 Subject: Updated the styling of milestone list --- app/assets/stylesheets/pages/milestone.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index dba83e56d72..34991a0217a 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -3,8 +3,18 @@ } .milestones { + padding: $gl-padding-top; + border-radius: $border-radius-default; + background-color: $gray-normal; + .milestone { - padding: 10px 16px; + padding: $gl-padding-top $gl-padding; + border-radius: $border-radius-default; + background-color: $white-light; + + &:not(:last-child){ + margin-bottom: $gl-padding-8; + } h4 { font-weight: $gl-font-weight-bold; -- cgit v1.2.1 From f5e4adc9bf1ae7a617d4e0090bad676e00a4041f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 18 Jun 2018 15:14:20 +0200 Subject: add spec replicating both label and milestone duplicated title issues --- spec/lib/gitlab/import_export/project.light.json | 6 ++-- .../import_export/project_tree_restorer_spec.rb | 37 ++++++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json index c13cf4a0507..a401ce4c0c7 100644 --- a/spec/lib/gitlab/import_export/project.light.json +++ b/spec/lib/gitlab/import_export/project.light.json @@ -7,7 +7,7 @@ "milestones": [ { "id": 1, - "title": "Project milestone", + "title": "A milestone", "project_id": 8, "description": "Project-level milestone", "due_date": null, @@ -66,7 +66,7 @@ "group_milestone_id": null, "milestone": { "id": 1, - "title": "Project milestone", + "title": "A milestone", "project_id": 8, "description": "Project-level milestone", "due_date": null, @@ -86,7 +86,7 @@ "updated_at": "2017-08-15T18:37:40.795Z", "label": { "id": 6, - "title": "Another project label", + "title": "Another label", "color": "#A8D695", "project_id": null, "created_at": "2017-08-15T18:37:19.698Z", diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 68ddc947e02..613e4cb78d0 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -189,8 +189,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do @project.pipelines.zip([2, 2, 2, 2, 2]) .each do |(pipeline, expected_status_size)| - expect(pipeline.statuses.size).to eq(expected_status_size) - end + expect(pipeline.statuses.size).to eq(expected_status_size) + end end end @@ -368,5 +368,38 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do milestones: 1, first_issue_labels: 1 end + + context 'with existing group models' do + let!(:project) do + create(:project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + before do + project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.light.json") + end + + it 'imports labels' do + create(:group_label, name: 'Another label', group: project.group) + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) + + restored_project_json + + expect(project.labels.count).to eq(1) + end + + it 'imports milestones' do + create(:milestone, name: 'A milestone', group: project.group) + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) + + restored_project_json + + expect(project.milestones.count).to eq(1) + end + end end end -- cgit v1.2.1 From ac66547370c94ad07a55159a490e5e9e4706adbc Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 18 Jun 2018 15:17:03 +0200 Subject: add spec replicating both label and milestone duplicated title issues --- spec/lib/gitlab/import_export/project_tree_restorer_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 613e4cb78d0..71f84234e4e 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -385,6 +385,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it 'imports labels' do create(:group_label, name: 'Another label', group: project.group) + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) restored_project_json @@ -394,6 +395,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it 'imports milestones' do create(:milestone, name: 'A milestone', group: project.group) + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) restored_project_json -- cgit v1.2.1 From 518eefcb9a59c1d89971e592f7d76855cbf0e8be Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 18 Jun 2018 15:12:44 +0100 Subject: Updated placement of milestone actions in milestone list --- app/assets/stylesheets/pages/milestone.scss | 2 +- app/views/shared/milestones/_milestone.html.haml | 71 ++++++++++++------------ 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 34991a0217a..9cbc1a4f302 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -12,7 +12,7 @@ border-radius: $border-radius-default; background-color: $white-light; - &:not(:last-child){ + &:not(:last-child) { margin-bottom: $gl-padding-8; } diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 09bbd04c2bf..00893c65638 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -10,14 +10,45 @@ - else %span - Project Milestone - .col-sm-6 - .float-right.light #{milestone.percent_complete(current_user)}% complete - .row - .col-sm-6 + .col-sm-4 + = milestone_progress_bar(milestone) = link_to pluralize(milestone.total_issues_count(current_user), 'Issue'), issues_path · = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path - .col-sm-6= milestone_progress_bar(milestone) + .float-right.light #{milestone.percent_complete(current_user)}% complete + .col-sm-2 + - if @project + .col-sm-6.milestone-actions + - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? + = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-sm btn-grouped" do + Edit + \ + + - if @project.group + %button.js-promote-project-milestone-button.btn.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), + disabled: true, + type: 'button', + data: { url: promote_project_milestone_path(milestone.project, milestone), + milestone_title: milestone.title, + group_name: @project.group.name, + target: '#promote-milestone-modal', + container: 'body', + toggle: 'modal' } } + = _('Promote') + + = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped" + + %button.js-delete-milestone-button.btn.btn-sm.btn-grouped.btn-danger{ data: { toggle: 'modal', + target: '#delete-milestone-modal', + milestone_id: milestone.id, + milestone_title: markdown_field(milestone, :title), + milestone_url: project_milestone_path(milestone.project, milestone), + milestone_issue_count: milestone.issues.count, + milestone_merge_request_count: milestone.merge_requests.count }, + disabled: true } + = _('Delete') + = icon('spin spinner', class: 'js-loading-icon hidden' ) + - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone? .row .col-sm-6 @@ -44,33 +75,3 @@ .row .col-sm-6 = render('shared/milestone_expired', milestone: milestone) - .col-sm-6.milestone-actions - - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-sm btn-grouped" do - Edit - \ - - - if @project.group - %button.js-promote-project-milestone-button.btn.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), - disabled: true, - type: 'button', - data: { url: promote_project_milestone_path(milestone.project, milestone), - milestone_title: milestone.title, - group_name: @project.group.name, - target: '#promote-milestone-modal', - container: 'body', - toggle: 'modal' } } - = _('Promote') - - = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped" - - %button.js-delete-milestone-button.btn.btn-sm.btn-grouped.btn-danger{ data: { toggle: 'modal', - target: '#delete-milestone-modal', - milestone_id: milestone.id, - milestone_title: markdown_field(milestone, :title), - milestone_url: project_milestone_path(milestone.project, milestone), - milestone_issue_count: milestone.issues.count, - milestone_merge_request_count: milestone.merge_requests.count }, - disabled: true } - = _('Delete') - = icon('spin spinner', class: 'js-loading-icon hidden' ) -- cgit v1.2.1 From 2659dcb6b4df3d3c5697fc9692732fc2ed367e20 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 18 Jun 2018 15:34:40 +0100 Subject: Removed edit and delete buttons from milestone list --- app/views/shared/milestones/_milestone.html.haml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 00893c65638..7dfce0cd812 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -20,10 +20,6 @@ - if @project .col-sm-6.milestone-actions - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-sm btn-grouped" do - Edit - \ - - if @project.group %button.js-promote-project-milestone-button.btn.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), disabled: true, @@ -38,17 +34,6 @@ = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped" - %button.js-delete-milestone-button.btn.btn-sm.btn-grouped.btn-danger{ data: { toggle: 'modal', - target: '#delete-milestone-modal', - milestone_id: milestone.id, - milestone_title: markdown_field(milestone, :title), - milestone_url: project_milestone_path(milestone.project, milestone), - milestone_issue_count: milestone.issues.count, - milestone_merge_request_count: milestone.merge_requests.count }, - disabled: true } - = _('Delete') - = icon('spin spinner', class: 'js-loading-icon hidden' ) - - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone? .row .col-sm-6 -- cgit v1.2.1 From 7e35188e0825c8d96ee2b043958f656654e51a63 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 18 Jun 2018 16:32:02 +0100 Subject: Updated styling of promote project milestone button in the milestones list --- app/views/shared/milestones/_milestone.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 7dfce0cd812..baa2100da95 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -18,10 +18,10 @@ .float-right.light #{milestone.percent_complete(current_user)}% complete .col-sm-2 - if @project - .col-sm-6.milestone-actions + .milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - if @project.group - %button.js-promote-project-milestone-button.btn.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), + %button.js-promote-project-milestone-button.btn.btn-transparent.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), disabled: true, type: 'button', data: { url: promote_project_milestone_path(milestone.project, milestone), @@ -30,7 +30,7 @@ target: '#promote-milestone-modal', container: 'body', toggle: 'modal' } } - = _('Promote') + = sprite_icon('level-up', size: 12) = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped" -- cgit v1.2.1 From 2a638c4231fdbaab77240999be98dd6cde385b76 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 18 Jun 2018 16:55:27 +0100 Subject: Added 'Reopen Milestone' button to milestones list for closed milestones --- app/views/shared/milestones/_milestone.html.haml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index baa2100da95..4ac69d21174 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -33,6 +33,8 @@ = sprite_icon('level-up', size: 12) = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped" + - unless milestone.active? + = link_to 'Reopen Milestone', project_milestone_path(@project, milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone? .row -- cgit v1.2.1 From d2daa6183251a1ce0397f4a50395dd171ec2a7eb Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 18 Jun 2018 17:14:10 +0100 Subject: Update milestone actions placement for group milestones in milestones list --- app/views/shared/milestones/_milestone.html.haml | 36 ++++++++++-------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 4ac69d21174..d263b4288c5 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -10,6 +10,15 @@ - else %span - Project Milestone + - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone? + = render('shared/milestone_expired', milestone: milestone) + - if milestone.legacy_group_milestone? + .projects + - milestone.milestones.each do |milestone| + = link_to milestone_path(milestone) do + %span.badge.badge-gray + = dashboard ? milestone.project.full_name : milestone.project.name + .col-sm-4 = milestone_progress_bar(milestone) = link_to pluralize(milestone.total_issues_count(current_user), 'Issue'), issues_path @@ -17,8 +26,8 @@ = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path .float-right.light #{milestone.percent_complete(current_user)}% complete .col-sm-2 - - if @project - .milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end + .milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end + - if @project - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - if @project.group %button.js-promote-project-milestone-button.btn.btn-transparent.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), @@ -35,30 +44,15 @@ = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped" - unless milestone.active? = link_to 'Reopen Milestone', project_milestone_path(@project, milestone, {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" - - - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone? - .row - .col-sm-6 - - if milestone.legacy_group_milestone? - .expiration= render('shared/milestone_expired', milestone: milestone) - .projects - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.badge.badge-gray - = dashboard ? milestone.project.full_name : milestone.project.name - - if @group - .col-sm-6.milestone-actions + - if @group - if can?(current_user, :admin_milestones, @group) - - if milestone.group_milestone? - = link_to edit_group_milestone_path(@group, milestone), class: "btn btn-sm btn-grouped" do - Edit - \ - if milestone.closed? = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen" - else = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close" - - if @project + - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone? .row .col-sm-6 - = render('shared/milestone_expired', milestone: milestone) + + -- cgit v1.2.1 From 8b2a6d41d5359dbfdd5a159f0cce602f36785e1e Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 18 Jun 2018 17:34:59 +0100 Subject: updated placement of milestone type for dashboard milestone list --- app/views/shared/milestones/_milestone.html.haml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index d263b4288c5..725698226ac 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -1,14 +1,13 @@ - dashboard = local_assigns[:dashboard] - custom_dom_id = dom_id(milestone.try(:milestones) ? milestone.milestones.first : milestone) +- milestone_type = milestone.group_milestone? ? 'Group Milestone' : 'Project Milestone' %li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } .row .col-sm-6 %strong= link_to truncate(milestone.title, length: 100), milestone_path - - if milestone.group_milestone? - %span - Group Milestone - - else - %span - Project Milestone + - unless dashboard + = milestone_type - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone? = render('shared/milestone_expired', milestone: milestone) @@ -50,9 +49,10 @@ = link_to 'Reopen Milestone', group_milestone_route(milestone, {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen" - else = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close" + - if dashboard + .status-box.status-box-upcoming + = milestone_type + - - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone? - .row - .col-sm-6 -- cgit v1.2.1 From b99bc6d38088b7b87f6ffa4a706ab5eacb489449 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 19 Jun 2018 09:54:47 +0200 Subject: add group finder spec and logic --- lib/gitlab/import_export/group_project_finder.rb | 33 ++++++++++++++++++++++ .../import_export/group_project_finder_spec.rb | 21 ++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 lib/gitlab/import_export/group_project_finder.rb create mode 100644 spec/lib/gitlab/import_export/group_project_finder_spec.rb diff --git a/lib/gitlab/import_export/group_project_finder.rb b/lib/gitlab/import_export/group_project_finder.rb new file mode 100644 index 00000000000..d2d4898f6bc --- /dev/null +++ b/lib/gitlab/import_export/group_project_finder.rb @@ -0,0 +1,33 @@ +module Gitlab + module ImportExport + class GroupProjectFinder + def self.find(*args) + new(*args).find + end + + def initialize(klass, attributes) + @klass = klass + @attributes = attributes + @group_id = @attributes['group_id'] + @project_id = @attributes['project_id'] + end + + def find + @klass.where(where_clause) + end + + private + + def where_clause + @attributes.except('group_id', 'project_id').map do |key, value| + table[key].eq(value).and(table[:group_id].eq(@group_id)) + .or(table[key].eq(value).and(table[:project_id].eq(@project_id))) + end.reduce(:or) + end + + def table + @table ||= @klass.arel_table + end + end + end +end diff --git a/spec/lib/gitlab/import_export/group_project_finder_spec.rb b/spec/lib/gitlab/import_export/group_project_finder_spec.rb new file mode 100644 index 00000000000..17239c150ab --- /dev/null +++ b/spec/lib/gitlab/import_export/group_project_finder_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::GroupProjectFinder do + let(:project) do + create(:project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + it 'finds the right group label' do + group_label = create(:group_label, 'name': 'group label', 'group': project.group) + + expect(described_class.find(Label, + title: 'group label', + 'project_id': project.id, + 'group_id': project.group.id)).to eq([group_label]) + end +end -- cgit v1.2.1 From fe56d29d300ccbb26328886166e07c84fc247155 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 19 Jun 2018 11:25:58 +0200 Subject: update spec --- .../import_export/group_project_finder_spec.rb | 61 +++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/spec/lib/gitlab/import_export/group_project_finder_spec.rb b/spec/lib/gitlab/import_export/group_project_finder_spec.rb index 17239c150ab..a86f0c2dd44 100644 --- a/spec/lib/gitlab/import_export/group_project_finder_spec.rb +++ b/spec/lib/gitlab/import_export/group_project_finder_spec.rb @@ -10,12 +10,61 @@ describe Gitlab::ImportExport::GroupProjectFinder do group: create(:group)) end - it 'finds the right group label' do - group_label = create(:group_label, 'name': 'group label', 'group': project.group) + context 'labels' do + it 'finds the right group label' do + group_label = create(:group_label, 'name': 'group label', 'group': project.group) - expect(described_class.find(Label, - title: 'group label', - 'project_id': project.id, - 'group_id': project.group.id)).to eq([group_label]) + expect(described_class.find_or_new(Label, + title: 'group label', + 'project_id' => project.id, + 'group_id' => project.group.id)).to eq(group_label) + end + + it 'initializes a new label' do + label = described_class.find_or_new(Label, + title: 'group label', + 'project_id' => project.id, + 'group_id' => project.group.id) + + expect(label.persisted?).to be false + end + + it 'creates a new label' do + label = described_class.find_or_create(Label, + title: 'group label', + 'project_id' => project.id, + 'group_id' => project.group.id) + + expect(label.persisted?).to be true + end + end + + context 'milestones' do + it 'finds the right group milestone' do + milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group) + + expect(described_class.find_or_new(Milestone, + title: 'group milestone', + 'project_id' => project.id, + 'group_id' => project.group.id)).to eq(milestone) + end + + it 'initializes a new milestone' do + milestone = described_class.find_or_new(Milestone, + title: 'group milestone', + 'project_id' => project.id, + 'group_id' => project.group.id) + + expect(milestone.persisted?).to be false + end + + it 'creates a new milestone' do + milestone = described_class.find_or_create(Milestone, + title: 'group milestone', + 'project_id' => project.id, + 'group_id' => project.group.id) + + expect(milestone.persisted?).to be true + end end end -- cgit v1.2.1 From 44c82ebbd13a92019297693cc224447653f0ecc2 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 11:43:47 +0100 Subject: Added styles for milestone date range in milestone list --- app/assets/stylesheets/pages/milestone.scss | 6 ++++++ app/views/shared/_milestone_expired.html.haml | 2 +- app/views/shared/milestones/_milestone.html.haml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 9cbc1a4f302..0c111ae7144 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -239,6 +239,12 @@ } } +.milestone-range { + color: $gl-text-color-tertiary; + margin: $gl-bar-padding 0; + font-size: $tooltip-font-size; +} + @include media-breakpoint-down(xs) { .milestone-banner-text, .milestone-banner-link { diff --git a/app/views/shared/_milestone_expired.html.haml b/app/views/shared/_milestone_expired.html.haml index 814eda5e540..738622a4993 100644 --- a/app/views/shared/_milestone_expired.html.haml +++ b/app/views/shared/_milestone_expired.html.haml @@ -1,5 +1,5 @@ - if milestone.due_date || milestone.start_date - %div + .milestone-range = milestone_date_range(milestone) - if milestone.expired? and not milestone.closed? .status-box.status-box-expired Expired diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 725698226ac..4df87471947 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -29,7 +29,7 @@ - if @project - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - if @project.group - %button.js-promote-project-milestone-button.btn.btn-transparent.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), + %button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), disabled: true, type: 'button', data: { url: promote_project_milestone_path(milestone.project, milestone), @@ -38,7 +38,7 @@ target: '#promote-milestone-modal', container: 'body', toggle: 'modal' } } - = sprite_icon('level-up', size: 12) + = sprite_icon('level-up', size: 14) = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close btn-grouped" - unless milestone.active? -- cgit v1.2.1 From 8c0c4750dbe79ac1515ee62485aaafd4883cc7cc Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 12:09:33 +0100 Subject: Added styling for milestone progress --- app/assets/stylesheets/framework/common.scss | 1 - app/assets/stylesheets/pages/milestone.scss | 7 +++++++ app/views/shared/milestones/_milestone.html.haml | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 326499125fc..738f3b28eb7 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -267,7 +267,6 @@ li.note { } .progress { - margin-bottom: 0; margin-top: 4px; box-shadow: none; background-color: $border-gray-light; diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 0c111ae7144..fa75a7df9ac 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -23,6 +23,13 @@ .progress { width: 100%; height: 6px; + margin-bottom: $gl-padding-4; + } + + .milestone-progress { + a { + color: $gl-link-color; + } } } } diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 4df87471947..17bb3872066 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -7,7 +7,7 @@ .col-sm-6 %strong= link_to truncate(milestone.title, length: 100), milestone_path - unless dashboard - = milestone_type + = " - #{milestone_type}" - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone? = render('shared/milestone_expired', milestone: milestone) @@ -18,7 +18,7 @@ %span.badge.badge-gray = dashboard ? milestone.project.full_name : milestone.project.name - .col-sm-4 + .col-sm-4.milestone-progress = milestone_progress_bar(milestone) = link_to pluralize(milestone.total_issues_count(current_user), 'Issue'), issues_path · -- cgit v1.2.1 From aad99b513ba699352f24db289a184bac70b6780a Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 12:36:47 +0100 Subject: Updated styling of milestone type in dashboard milestones list --- app/assets/stylesheets/framework/issue_box.scss | 5 +++++ app/views/shared/milestones/_milestone.html.haml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index 1d247671761..86de88729ee 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -45,4 +45,9 @@ &.status-box-upcoming { background: $gl-text-color-secondary; } + + &.status-box-milestone { + color: $gl-text-color; + background: $gray-darker; + } } diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 17bb3872066..14b743f4823 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -50,7 +50,7 @@ - else = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close" - if dashboard - .status-box.status-box-upcoming + .status-box.status-box-milestone = milestone_type -- cgit v1.2.1 From 0e06315af6e515281e01ef8d349c37b8ed1a364d Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 12:42:39 +0100 Subject: Fixed mobile placement for milestone percent complete in milestones list --- app/views/shared/milestones/_milestone.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 14b743f4823..1f3eff6b9e1 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -23,7 +23,7 @@ = link_to pluralize(milestone.total_issues_count(current_user), 'Issue'), issues_path · = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path - .float-right.light #{milestone.percent_complete(current_user)}% complete + .float-lg-right.light #{milestone.percent_complete(current_user)}% complete .col-sm-2 .milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end - if @project -- cgit v1.2.1 From eec5324aaefaddc55287c3345449289a90845606 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 12:50:23 +0100 Subject: Fixed lint errors --- app/views/shared/milestones/_milestone.html.haml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 1f3eff6b9e1..e0d9bb91b46 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -50,9 +50,5 @@ - else = link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-sm btn-grouped btn-close" - if dashboard - .status-box.status-box-milestone + .status-box.status-box-milestone = milestone_type - - - - -- cgit v1.2.1 From 3522623f0d13a1902b53dec208864941adab6a5c Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 13:22:56 +0100 Subject: Removed irrelevant test for edit milestone in milestone list --- spec/features/groups/milestone_spec.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 20337f1d3b0..2108d763028 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -107,19 +107,6 @@ feature 'Group milestones' do expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1) end - it 'updates milestone' do - page.within(".milestones #milestone_#{active_group_milestone.id}") do - click_link('Edit') - end - - page.within('.milestone-form') do - fill_in 'milestone_title', with: 'new title' - click_button('Update milestone') - end - - expect(find('#content-body h2')).to have_content('new title') - end - it 'shows milestone detail and supports its edit' do page.within(".milestones #milestone_#{active_group_milestone.id}") do click_link(active_group_milestone.title) -- cgit v1.2.1 From 8849159986f8565a23ae1512831c50ae3e76ae9a Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Tue, 19 Jun 2018 13:40:04 +0100 Subject: Updated test for user deleting a milestone --- spec/features/milestones/user_deletes_milestone_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/features/milestones/user_deletes_milestone_spec.rb b/spec/features/milestones/user_deletes_milestone_spec.rb index 414702daba4..9d4a68239d3 100644 --- a/spec/features/milestones/user_deletes_milestone_spec.rb +++ b/spec/features/milestones/user_deletes_milestone_spec.rb @@ -13,6 +13,7 @@ describe "User deletes milestone", :js do end it "deletes milestone" do + click_link(milestone.title) click_button("Delete") click_button("Delete milestone") -- cgit v1.2.1 From a19e08fecaa7181586a475d5dd4939b98fca2451 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 19 Jun 2018 15:27:30 +0200 Subject: refactor relation factory --- lib/gitlab/import_export/group_project_finder.rb | 35 ++++++++++++++---- lib/gitlab/import_export/relation_factory.rb | 46 +++++++++--------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/lib/gitlab/import_export/group_project_finder.rb b/lib/gitlab/import_export/group_project_finder.rb index d2d4898f6bc..b9ef43e4e51 100644 --- a/lib/gitlab/import_export/group_project_finder.rb +++ b/lib/gitlab/import_export/group_project_finder.rb @@ -1,8 +1,12 @@ module Gitlab module ImportExport class GroupProjectFinder - def self.find(*args) - new(*args).find + def self.find_or_new(*args) + new(*args).find_or_new + end + + def self.find_or_create(*args) + new(*args).find_or_create end def initialize(klass, attributes) @@ -12,22 +16,41 @@ module Gitlab @project_id = @attributes['project_id'] end - def find - @klass.where(where_clause) + def find_or_new + @klass.where(where_clause).first || @klass.new(project_attributes) + end + + def find_or_create + @klass.where(where_clause).first || @klass.create(project_attributes) end private def where_clause @attributes.except('group_id', 'project_id').map do |key, value| - table[key].eq(value).and(table[:group_id].eq(@group_id)) - .or(table[key].eq(value).and(table[:project_id].eq(@project_id))) + project_clause = table[key].eq(value).and(table[:project_id].eq(@project_id)) + + if @group_id + project_clause.or(table[key].eq(value).and(table[:group_id].eq(@group_id))) + else + project_clause + end end.reduce(:or) end def table @table ||= @klass.arel_table end + + def project_attributes + @attributes.except('group_id').tap do |atts| + atts['type'] = 'ProjectLabel' if label? + end + end + + def label? + @klass == Label || @klass < Label + end end end end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index c5cf290f191..43a0637933a 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -80,8 +80,8 @@ module Gitlab case @relation_name when :merge_request_diff_files then setup_diff when :notes then setup_note - when :project_label, :project_labels then setup_label - when :milestone, :milestones then setup_milestone + when :milestone, :milestones, + :project_label, :project_labels then setup_project_group when 'Ci::Pipeline' then setup_pipeline else @relation_hash['project_id'] = @project.id @@ -167,23 +167,10 @@ module Gitlab @relation_hash['target_project_id'] && @relation_hash['target_project_id'] == @relation_hash['source_project_id'] end - def setup_label - # If there's no group, move the label to a project label - if @relation_hash['type'] == 'GroupLabel' && @relation_hash['group_id'] - @relation_hash['project_id'] = nil - @relation_name = :group_label - else - @relation_hash['group_id'] = nil - @relation_hash['type'] = 'ProjectLabel' - end - end - - def setup_milestone - if @relation_hash['group_id'] - @relation_hash['group_id'] = @project.group.id - else - @relation_hash['project_id'] = @project.id - end + def setup_project_group + @relation_hash['project_id'] = @project.id + @relation_hash['group_id'] = @project.group&.id + @relation_hash.delete('type') end def reset_tokens! @@ -288,30 +275,29 @@ module Gitlab end def find_or_create_object! - finder_attributes = if @relation_name == :group_label - %w[title group_id] - elsif parsed_relation_hash['project_id'] - %w[title project_id] - else - %w[title group_id] - end - - finder_hash = parsed_relation_hash.slice(*finder_attributes) + finder_hash = parsed_relation_hash.slice('title', 'project_id', 'group_id') if label? - label = relation_class.find_or_initialize_by(finder_hash) + label = GroupProjectFinder.find_or_new(Label, finder_hash) parsed_relation_hash.delete('priorities') if label.persisted? + parsed_relation_hash.delete('type') label.save! label else - relation_class.find_or_create_by(finder_hash) + parsed_relation_hash.delete('type') if milestone? + + GroupProjectFinder.find_or_create(relation_class, finder_hash) end end def label? @relation_name.to_s.include?('label') end + + def milestone? + @relation_name.to_s.include?('milestone') + end end end end -- cgit v1.2.1 From 825c68e2ededed6f280ebd82c713cd932444d8ac Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 19 Jun 2018 17:08:02 +0200 Subject: fix some edge cases --- lib/gitlab/import_export/project_tree_restorer.rb | 20 +++++++++++--------- lib/gitlab/import_export/relation_factory.rb | 9 +++++++-- .../import_export/project_tree_restorer_spec.rb | 3 ++- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb index 4eb67fbe11e..25661a42ad5 100644 --- a/lib/gitlab/import_export/project_tree_restorer.rb +++ b/lib/gitlab/import_export/project_tree_restorer.rb @@ -1,8 +1,8 @@ module Gitlab module ImportExport class ProjectTreeRestorer - # Relations which cannot have both group_id and project_id at the same time - RESTRICT_PROJECT_AND_GROUP = %i(milestone milestones).freeze + # Relations which cannot be saved at project level + GROUP_MODELS = [GroupLabel, Milestone].freeze def initialize(user:, shared:, project:) @path = File.join(shared.export_path, 'project.json') @@ -70,12 +70,20 @@ module Gitlab def save_relation_hash(relation_hash_batch, relation_key) relation_hash = create_relation(relation_key, relation_hash_batch) + remove_group_models(relation_hash) if relation_hash.is_a?(Array) + @saved = false unless restored_project.append_or_update_attribute(relation_key, relation_hash) # Restore the project again, extra query that skips holding the AR objects in memory @restored_project = Project.find(@project_id) end + def remove_group_models(relation_hash) + relation_hash.reject! do |value| + value.respond_to?(:group_id) && value.group_id && GROUP_MODELS.include?(value.class) + end + end + def default_relation_list reader.tree.reject do |model| model.is_a?(Hash) && model[:project_members] @@ -181,13 +189,7 @@ module Gitlab end def parsed_relation_hash(relation_hash, relation_type) - if RESTRICT_PROJECT_AND_GROUP.include?(relation_type) - params = {} - params['group_id'] = restored_project.group.try(:id) if relation_hash['group_id'] - params['project_id'] = restored_project.id if relation_hash['project_id'] - else - params = { 'group_id' => restored_project.group.try(:id), 'project_id' => restored_project.id } - end + params = { 'group_id' => restored_project.group.try(:id), 'project_id' => restored_project.id } relation_hash.merge(params) end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index 43a0637933a..516f3bdbd4e 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -285,9 +285,14 @@ module Gitlab label.save! label else - parsed_relation_hash.delete('type') if milestone? + object = GroupProjectFinder.find_or_create(relation_class, finder_hash) - GroupProjectFinder.find_or_create(relation_class, finder_hash) + if milestone? + parsed_relation_hash.delete('group_id') if object.project_id + parsed_relation_hash.delete('project_id') if object.group_id + end + + object end end diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 71f84234e4e..a706ab0eb6f 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -400,7 +400,8 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do restored_project_json - expect(project.milestones.count).to eq(1) + expect(project.group.milestones.count).to eq(1) + expect(project.milestones.count).to eq(0) end end end -- cgit v1.2.1 From 4e9b094b29c225878dfe15a200dd60f0bc1fdd67 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 20 Jun 2018 10:27:40 +0200 Subject: refactor finder --- lib/gitlab/import_export/group_project_finder.rb | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/gitlab/import_export/group_project_finder.rb b/lib/gitlab/import_export/group_project_finder.rb index b9ef43e4e51..cd239466a5c 100644 --- a/lib/gitlab/import_export/group_project_finder.rb +++ b/lib/gitlab/import_export/group_project_finder.rb @@ -2,18 +2,22 @@ module Gitlab module ImportExport class GroupProjectFinder def self.find_or_new(*args) - new(*args).find_or_new + Project.transaction do + new(*args).find_or_new + end end def self.find_or_create(*args) - new(*args).find_or_create + Project.transaction do + new(*args).find_or_create + end end def initialize(klass, attributes) @klass = klass @attributes = attributes - @group_id = @attributes['group_id'] - @project_id = @attributes['project_id'] + @group = @attributes[:group] + @project = @attributes[:project] end def find_or_new @@ -27,11 +31,11 @@ module Gitlab private def where_clause - @attributes.except('group_id', 'project_id').map do |key, value| - project_clause = table[key].eq(value).and(table[:project_id].eq(@project_id)) + @attributes.except(:group, :project).map do |key, value| + project_clause = table[key].eq(value).and(table[:project_id].eq(@project.id)) - if @group_id - project_clause.or(table[key].eq(value).and(table[:group_id].eq(@group_id))) + if @group + project_clause.or(table[key].eq(value).and(table[:group_id].eq(@group.id))) else project_clause end @@ -43,7 +47,7 @@ module Gitlab end def project_attributes - @attributes.except('group_id').tap do |atts| + @attributes.except(:group).tap do |atts| atts['type'] = 'ProjectLabel' if label? end end -- cgit v1.2.1 From cc061cd1c664f2003d4c7abb3930834704d2eb9e Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 20 Jun 2018 11:57:22 +0200 Subject: fix some more edge cases --- lib/gitlab/import_export/relation_factory.rb | 15 ++++++++++----- .../import_export/project_tree_restorer_spec.rb | 19 +++---------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index 516f3bdbd4e..ff3873bc8ed 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -80,8 +80,7 @@ module Gitlab case @relation_name when :merge_request_diff_files then setup_diff when :notes then setup_note - when :milestone, :milestones, - :project_label, :project_labels then setup_project_group + when *(EXISTING_OBJECT_CHECK - [:project_feature]) then setup_project_group when 'Ci::Pipeline' then setup_pipeline else @relation_hash['project_id'] = @project.id @@ -275,7 +274,11 @@ module Gitlab end def find_or_create_object! - finder_hash = parsed_relation_hash.slice('title', 'project_id', 'group_id') + # Can't use IDs as validation exists calilng `.group` or `.project` + finder_hash = { project: @project }.tap do |hash| + hash[:group] = @project.group if relation_class.attribute_method?('group_id') + hash[:title] = parsed_relation_hash['title'] if parsed_relation_hash['title'] + end if label? label = GroupProjectFinder.find_or_new(Label, finder_hash) @@ -288,8 +291,10 @@ module Gitlab object = GroupProjectFinder.find_or_create(relation_class, finder_hash) if milestone? - parsed_relation_hash.delete('group_id') if object.project_id - parsed_relation_hash.delete('project_id') if object.group_id + parsed_relation_hash.delete('group_id') if object.project + parsed_relation_hash.delete('project_id') if object.group + parsed_relation_hash.delete('iid') + parsed_relation_hash.delete('id') end object diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index a706ab0eb6f..94251e2280b 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -246,13 +246,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do expect(project.issues.size).to eq(results.fetch(:issues, 0)) end - it 'has issue with group label and project label' do - labels = project.issues.first.labels - - expect(labels.where(type: "ProjectLabel").count).to eq(results.fetch(:first_issue_labels, 0)) - expect(labels.where(type: "ProjectLabel").where.not(group_id: nil).count).to eq(0) - end - it 'does not set params that are excluded from import_export settings' do expect(project.import_type).to be_nil expect(project.creator_id).not_to eq 123 @@ -268,12 +261,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it 'has group milestone' do expect(project.group.milestones.size).to eq(results.fetch(:milestones, 0)) end - - it 'has issue with group label' do - labels = project.issues.first.labels - - expect(labels.where(type: "GroupLabel").count).to eq(results.fetch(:first_issue_labels, 0)) - end end context 'Light JSON' do @@ -360,12 +347,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do it_behaves_like 'restores project correctly', issues: 2, labels: 1, - milestones: 1, + milestones: 2, first_issue_labels: 1 it_behaves_like 'restores group correctly', - labels: 1, - milestones: 1, + labels: 0, + milestones: 0, first_issue_labels: 1 end -- cgit v1.2.1 From 8522173e0ad912c41aaa68f4e01f03ac73cc4514 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 20 Jun 2018 15:45:15 +0200 Subject: refactor code once again to fix IID issues --- lib/gitlab/import_export/group_project_finder.rb | 29 ++++++++++++++++++-- lib/gitlab/import_export/relation_factory.rb | 32 ++++------------------ .../import_export/group_project_finder_spec.rb | 28 +++++++++---------- 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/lib/gitlab/import_export/group_project_finder.rb b/lib/gitlab/import_export/group_project_finder.rb index cd239466a5c..37923edb9db 100644 --- a/lib/gitlab/import_export/group_project_finder.rb +++ b/lib/gitlab/import_export/group_project_finder.rb @@ -31,7 +31,9 @@ module Gitlab private def where_clause - @attributes.except(:group, :project).map do |key, value| + return { project_id: @project.id } unless milestone? || label? + + @attributes.slice(:title).map do |key, value| project_clause = table[key].eq(value).and(table[:project_id].eq(@project.id)) if @group @@ -48,13 +50,36 @@ module Gitlab def project_attributes @attributes.except(:group).tap do |atts| - atts['type'] = 'ProjectLabel' if label? + if label? + atts['type'] = 'ProjectLabel' + elsif milestone? + if atts['group_id'] + atts['iid'] = nil + atts.delete('group_id') + else + claim_iid + end + end end end def label? @klass == Label || @klass < Label end + + def milestone? + @klass == Milestone + end + + def claim_iid + group_milestone = @project.milestones.find_by(iid: @attributes['iid']) + + group_milestone.update!(iid: max_milestone_iid + 1) if group_milestone + end + + def max_milestone_iid + [@attributes['iid'], @project.milestones.maximum(:iid)].compact.max + end end end end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index ff3873bc8ed..992e3e708d5 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -257,15 +257,7 @@ module Gitlab end def existing_object - @existing_object ||= - begin - existing_object = find_or_create_object! - - # Done in two steps, as MySQL behaves differently than PostgreSQL using - # the +find_or_create_by+ method and does not return the ID the second time. - existing_object.update!(parsed_relation_hash) - existing_object - end + @existing_object ||= find_or_create_object! end def unknown_service? @@ -275,29 +267,17 @@ module Gitlab def find_or_create_object! # Can't use IDs as validation exists calilng `.group` or `.project` - finder_hash = { project: @project }.tap do |hash| + finder_hash = parsed_relation_hash.tap do |hash| hash[:group] = @project.group if relation_class.attribute_method?('group_id') + hash[:project] = @project hash[:title] = parsed_relation_hash['title'] if parsed_relation_hash['title'] + hash.delete('project_id') end if label? - label = GroupProjectFinder.find_or_new(Label, finder_hash) - parsed_relation_hash.delete('priorities') if label.persisted? - parsed_relation_hash.delete('type') - - label.save! - label + GroupProjectFinder.find_or_new(Label, finder_hash) else - object = GroupProjectFinder.find_or_create(relation_class, finder_hash) - - if milestone? - parsed_relation_hash.delete('group_id') if object.project - parsed_relation_hash.delete('project_id') if object.group - parsed_relation_hash.delete('iid') - parsed_relation_hash.delete('id') - end - - object + GroupProjectFinder.find_or_create(relation_class, finder_hash) end end diff --git a/spec/lib/gitlab/import_export/group_project_finder_spec.rb b/spec/lib/gitlab/import_export/group_project_finder_spec.rb index a86f0c2dd44..c36d80e51d4 100644 --- a/spec/lib/gitlab/import_export/group_project_finder_spec.rb +++ b/spec/lib/gitlab/import_export/group_project_finder_spec.rb @@ -16,15 +16,15 @@ describe Gitlab::ImportExport::GroupProjectFinder do expect(described_class.find_or_new(Label, title: 'group label', - 'project_id' => project.id, - 'group_id' => project.group.id)).to eq(group_label) + project: project, + group: project.group)).to eq(group_label) end it 'initializes a new label' do label = described_class.find_or_new(Label, title: 'group label', - 'project_id' => project.id, - 'group_id' => project.group.id) + project: project, + group: project.group) expect(label.persisted?).to be false end @@ -32,8 +32,8 @@ describe Gitlab::ImportExport::GroupProjectFinder do it 'creates a new label' do label = described_class.find_or_create(Label, title: 'group label', - 'project_id' => project.id, - 'group_id' => project.group.id) + project: project, + group: project.group) expect(label.persisted?).to be true end @@ -45,24 +45,24 @@ describe Gitlab::ImportExport::GroupProjectFinder do expect(described_class.find_or_new(Milestone, title: 'group milestone', - 'project_id' => project.id, - 'group_id' => project.group.id)).to eq(milestone) + project: project, + group: project.group)).to eq(milestone) end it 'initializes a new milestone' do milestone = described_class.find_or_new(Milestone, - title: 'group milestone', - 'project_id' => project.id, - 'group_id' => project.group.id) + title: 'group milestone', + project: project, + group: project.group) expect(milestone.persisted?).to be false end it 'creates a new milestone' do milestone = described_class.find_or_create(Milestone, - title: 'group milestone', - 'project_id' => project.id, - 'group_id' => project.group.id) + title: 'group milestone', + project: project, + group: project.group) expect(milestone.persisted?).to be true end -- cgit v1.2.1 From b0ddc55dc79bc141ae02c79cff83ec51672a4160 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 20 Jun 2018 16:18:40 +0200 Subject: refactor code once again to fix IID issues --- lib/gitlab/import_export/group_project_finder.rb | 15 ++++++++++----- lib/gitlab/import_export/relation_factory.rb | 24 ++---------------------- spec/lib/gitlab/import_export/project.light.json | 2 +- 3 files changed, 13 insertions(+), 28 deletions(-) diff --git a/lib/gitlab/import_export/group_project_finder.rb b/lib/gitlab/import_export/group_project_finder.rb index 37923edb9db..be526ef55d5 100644 --- a/lib/gitlab/import_export/group_project_finder.rb +++ b/lib/gitlab/import_export/group_project_finder.rb @@ -14,7 +14,7 @@ module Gitlab end def initialize(klass, attributes) - @klass = klass + @klass = klass < Label ? Label : klass @attributes = attributes @group = @attributes[:group] @project = @attributes[:project] @@ -33,7 +33,7 @@ module Gitlab def where_clause return { project_id: @project.id } unless milestone? || label? - @attributes.slice(:title).map do |key, value| + @attributes.slice('title').map do |key, value| project_clause = table[key].eq(value).and(table[:project_id].eq(@project.id)) if @group @@ -51,9 +51,9 @@ module Gitlab def project_attributes @attributes.except(:group).tap do |atts| if label? - atts['type'] = 'ProjectLabel' + atts['type'] = 'ProjectLabel' # Always create project labels elsif milestone? - if atts['group_id'] + if atts['group_id'] # Transform new group milestones into project ones atts['iid'] = nil atts.delete('group_id') else @@ -64,13 +64,18 @@ module Gitlab end def label? - @klass == Label || @klass < Label + @klass == Label end def milestone? @klass == Milestone end + # If an existing group milesone used the IIID + # claim the IID back and set the group milestone to use one available + # This is neccessary to fix situations like the following: + # - Importing into a user namespace project with exported group milestones + # where the IID of the Group milestone could conflict with a project one. def claim_iid group_milestone = @project.milestones.find_by(iid: @attributes['iid']) diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index 992e3e708d5..5efebf867ad 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -80,7 +80,6 @@ module Gitlab case @relation_name when :merge_request_diff_files then setup_diff when :notes then setup_note - when *(EXISTING_OBJECT_CHECK - [:project_feature]) then setup_project_group when 'Ci::Pipeline' then setup_pipeline else @relation_hash['project_id'] = @project.id @@ -166,12 +165,6 @@ module Gitlab @relation_hash['target_project_id'] && @relation_hash['target_project_id'] == @relation_hash['source_project_id'] end - def setup_project_group - @relation_hash['project_id'] = @project.id - @relation_hash['group_id'] = @project.group&.id - @relation_hash.delete('type') - end - def reset_tokens! return unless Gitlab::ImportExport.reset_tokens? && TOKEN_RESET_MODELS.include?(@relation_name.to_s) @@ -266,27 +259,14 @@ module Gitlab end def find_or_create_object! - # Can't use IDs as validation exists calilng `.group` or `.project` + # Can't use IDs as validation exists calling `group` or `project` attributes finder_hash = parsed_relation_hash.tap do |hash| hash[:group] = @project.group if relation_class.attribute_method?('group_id') hash[:project] = @project - hash[:title] = parsed_relation_hash['title'] if parsed_relation_hash['title'] hash.delete('project_id') end - if label? - GroupProjectFinder.find_or_new(Label, finder_hash) - else - GroupProjectFinder.find_or_create(relation_class, finder_hash) - end - end - - def label? - @relation_name.to_s.include?('label') - end - - def milestone? - @relation_name.to_s.include?('milestone') + GroupProjectFinder.find_or_create(relation_class, finder_hash) end end end diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json index a401ce4c0c7..ba2248073f5 100644 --- a/spec/lib/gitlab/import_export/project.light.json +++ b/spec/lib/gitlab/import_export/project.light.json @@ -67,7 +67,7 @@ "milestone": { "id": 1, "title": "A milestone", - "project_id": 8, + "group_id": 8, "description": "Project-level milestone", "due_date": null, "created_at": "2016-06-14T15:02:04.415Z", -- cgit v1.2.1 From 92ec58abeb21793ed8087e7acac77c84153aa50b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 20 Jun 2018 16:24:42 +0200 Subject: add changelog --- changelogs/unreleased/43270-import-with-milestones-failing.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/43270-import-with-milestones-failing.yml diff --git a/changelogs/unreleased/43270-import-with-milestones-failing.yml b/changelogs/unreleased/43270-import-with-milestones-failing.yml new file mode 100644 index 00000000000..13bf8072376 --- /dev/null +++ b/changelogs/unreleased/43270-import-with-milestones-failing.yml @@ -0,0 +1,5 @@ +--- +title: Fix label and milestone duplicated records and IID errors +merge_request: 19961 +author: +type: fixed -- cgit v1.2.1 From 90a10c32eccf07011784bee5ad4cb0270f04a7dd Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Wed, 20 Jun 2018 23:22:44 +0100 Subject: Updated markup for milestone title in milestones list --- app/views/shared/milestones/_milestone.html.haml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index e0d9bb91b46..75f5cef2d6d 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -5,9 +5,10 @@ %li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } .row .col-sm-6 - %strong= link_to truncate(milestone.title, length: 100), milestone_path - - unless dashboard - = " - #{milestone_type}" + %div + %strong= link_to truncate(milestone.title, length: 100), milestone_path + - unless dashboard + = " - #{milestone_type}" - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone? = render('shared/milestone_expired', milestone: milestone) -- cgit v1.2.1 From 5196af23c5fc830c1bcafc8ab7631307ea19aec3 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Wed, 20 Jun 2018 23:36:34 +0100 Subject: Updated styling of milestone lists --- app/assets/stylesheets/framework/common.scss | 4 ---- app/assets/stylesheets/pages/milestone.scss | 7 ++++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 738f3b28eb7..218e37602dd 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -262,10 +262,6 @@ li.note { } .milestone { - &.milestone-closed { - background: $gray-light; - } - .progress { margin-top: 4px; box-shadow: none; diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index fa75a7df9ac..7dbe3bf8f09 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -3,17 +3,18 @@ } .milestones { - padding: $gl-padding-top; + padding: $gl-padding-8; border-radius: $border-radius-default; - background-color: $gray-normal; + background-color: $theme-gray-100; .milestone { + border: 0; padding: $gl-padding-top $gl-padding; border-radius: $border-radius-default; background-color: $white-light; &:not(:last-child) { - margin-bottom: $gl-padding-8; + margin-bottom: $gl-padding-4; } h4 { -- cgit v1.2.1 From 55df4770a99d0d8c55242bd5a76ded8c3df04a13 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Wed, 20 Jun 2018 23:38:54 +0100 Subject: Removed milestone type from project milestones --- app/views/shared/milestones/_milestone.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 75f5cef2d6d..0ed52404cd8 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -7,7 +7,7 @@ .col-sm-6 %div %strong= link_to truncate(milestone.title, length: 100), milestone_path - - unless dashboard + - if @group = " - #{milestone_type}" - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone? -- cgit v1.2.1 From 653fad45f32951c2b5fd8ddad52231bfce9a0a7b Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 21 Jun 2018 09:31:51 +0100 Subject: Updated the font-size of milestone status in milestone list --- app/assets/stylesheets/pages/milestone.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 7dbe3bf8f09..b53d2fe37e0 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -32,6 +32,10 @@ color: $gl-link-color; } } + + .status-box { + font-size: $tooltip-font-size; + } } } -- cgit v1.2.1 From 0eba5e48ac523e84f37e8b3cd2707bf74c0705a4 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 21 Jun 2018 13:09:57 +0100 Subject: Added underline to promoted milestone flash message --- app/controllers/projects/milestones_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index f85dcfe6bfc..594563d1f6f 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -77,7 +77,7 @@ class Projects::MilestonesController < Projects::ApplicationController def promote promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone) - flash[:notice] = "#{milestone.title} promoted to group milestone.".html_safe + flash[:notice] = "#{milestone.title} promoted to group milestone.".html_safe respond_to do |format| format.html do redirect_to project_milestones_path(project) -- cgit v1.2.1 From e25c8a1b2b30e4187dad379c99ba6995f2ef985a Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 21 Jun 2018 13:21:31 +0100 Subject: Updated the styling of milestone projects in groups milestone list --- app/assets/stylesheets/pages/milestone.scss | 3 ++- app/views/shared/milestones/_milestone.html.haml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index b53d2fe37e0..9ef7a4f2f08 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -35,6 +35,7 @@ .status-box { font-size: $tooltip-font-size; + margin-top: 0; } } } @@ -253,7 +254,7 @@ .milestone-range { color: $gl-text-color-tertiary; - margin: $gl-bar-padding 0; + margin: $gl-bar-padding 0 $gl-padding-4; font-size: $tooltip-font-size; } diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 0ed52404cd8..92fe76b8646 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -16,7 +16,7 @@ .projects - milestone.milestones.each do |milestone| = link_to milestone_path(milestone) do - %span.badge.badge-gray + %span.label-badge.label-badge-blue = dashboard ? milestone.project.full_name : milestone.project.name .col-sm-4.milestone-progress -- cgit v1.2.1 From 8951b865f646cea1fb33d76940a29cc6b85eba8d Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 21 Jun 2018 13:33:40 +0100 Subject: Added changelog entry --- app/views/shared/milestones/_milestone.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 92fe76b8646..4661126c904 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -16,7 +16,7 @@ .projects - milestone.milestones.each do |milestone| = link_to milestone_path(milestone) do - %span.label-badge.label-badge-blue + %span.label-badge.label-badge-blue = dashboard ? milestone.project.full_name : milestone.project.name .col-sm-4.milestone-progress -- cgit v1.2.1 From 95600c34c02986211a4784c3cd30adf94fc25127 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 21 Jun 2018 13:37:39 +0100 Subject: Added changelog entry --- changelogs/unreleased/39543-milestone-page-list-redesign.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/39543-milestone-page-list-redesign.yml diff --git a/changelogs/unreleased/39543-milestone-page-list-redesign.yml b/changelogs/unreleased/39543-milestone-page-list-redesign.yml new file mode 100644 index 00000000000..dcd73c5eddf --- /dev/null +++ b/changelogs/unreleased/39543-milestone-page-list-redesign.yml @@ -0,0 +1,5 @@ +--- +title: Milestone page list redesign +merge_request: 19832 +author: Constance Okoghenun +type: changed -- cgit v1.2.1 From 7d310e0f119426d70ab7be6061f9b095af455c95 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Thu, 21 Jun 2018 22:14:46 +0100 Subject: Updated test for promoted milestones success message --- spec/controllers/projects/milestones_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 02b30f9bc6d..b1d83246238 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -124,7 +124,7 @@ describe Projects::MilestonesController do it 'shows group milestone' do post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid - expect(flash[:notice]).to eq("#{milestone.title} promoted to group milestone.") + expect(flash[:notice]).to eq("#{milestone.title} promoted to group milestone.") expect(response).to redirect_to(project_milestones_path(project)) end end -- cgit v1.2.1 From 3d3e441c91ac793272723b5f20eae5b9cf19fcc2 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 09:00:10 +0200 Subject: refactor code based on feedback --- lib/gitlab/import_export/group_project_finder.rb | 90 ------------------ .../import_export/group_project_object_builder.rb | 101 +++++++++++++++++++++ lib/gitlab/import_export/relation_factory.rb | 2 +- .../import_export/group_project_finder_spec.rb | 70 -------------- .../group_project_object_builder_spec.rb | 52 +++++++++++ 5 files changed, 154 insertions(+), 161 deletions(-) delete mode 100644 lib/gitlab/import_export/group_project_finder.rb create mode 100644 lib/gitlab/import_export/group_project_object_builder.rb delete mode 100644 spec/lib/gitlab/import_export/group_project_finder_spec.rb create mode 100644 spec/lib/gitlab/import_export/group_project_object_builder_spec.rb diff --git a/lib/gitlab/import_export/group_project_finder.rb b/lib/gitlab/import_export/group_project_finder.rb deleted file mode 100644 index be526ef55d5..00000000000 --- a/lib/gitlab/import_export/group_project_finder.rb +++ /dev/null @@ -1,90 +0,0 @@ -module Gitlab - module ImportExport - class GroupProjectFinder - def self.find_or_new(*args) - Project.transaction do - new(*args).find_or_new - end - end - - def self.find_or_create(*args) - Project.transaction do - new(*args).find_or_create - end - end - - def initialize(klass, attributes) - @klass = klass < Label ? Label : klass - @attributes = attributes - @group = @attributes[:group] - @project = @attributes[:project] - end - - def find_or_new - @klass.where(where_clause).first || @klass.new(project_attributes) - end - - def find_or_create - @klass.where(where_clause).first || @klass.create(project_attributes) - end - - private - - def where_clause - return { project_id: @project.id } unless milestone? || label? - - @attributes.slice('title').map do |key, value| - project_clause = table[key].eq(value).and(table[:project_id].eq(@project.id)) - - if @group - project_clause.or(table[key].eq(value).and(table[:group_id].eq(@group.id))) - else - project_clause - end - end.reduce(:or) - end - - def table - @table ||= @klass.arel_table - end - - def project_attributes - @attributes.except(:group).tap do |atts| - if label? - atts['type'] = 'ProjectLabel' # Always create project labels - elsif milestone? - if atts['group_id'] # Transform new group milestones into project ones - atts['iid'] = nil - atts.delete('group_id') - else - claim_iid - end - end - end - end - - def label? - @klass == Label - end - - def milestone? - @klass == Milestone - end - - # If an existing group milesone used the IIID - # claim the IID back and set the group milestone to use one available - # This is neccessary to fix situations like the following: - # - Importing into a user namespace project with exported group milestones - # where the IID of the Group milestone could conflict with a project one. - def claim_iid - group_milestone = @project.milestones.find_by(iid: @attributes['iid']) - - group_milestone.update!(iid: max_milestone_iid + 1) if group_milestone - end - - def max_milestone_iid - [@attributes['iid'], @project.milestones.maximum(:iid)].compact.max - end - end - end -end diff --git a/lib/gitlab/import_export/group_project_object_builder.rb b/lib/gitlab/import_export/group_project_object_builder.rb new file mode 100644 index 00000000000..c41d68e7aab --- /dev/null +++ b/lib/gitlab/import_export/group_project_object_builder.rb @@ -0,0 +1,101 @@ +module Gitlab + module ImportExport + # Given a class, it finds or creates a new object + # (initializes in the case of Label) at group or project level + # If it does not exist in the group, it creates it at project level + # + # For example: + # `GroupProjectObjectBuilder.build(Label, label_attributes)` + # finds or initializes a label with the given attributes. + # + # It also adds some logic around Group Labels/Milestones for edge cases. + class GroupProjectObjectBuilder + def self.build(*args) + Project.transaction do + new(*args).find + end + end + + def initialize(klass, attributes) + @klass = klass < Label ? Label : klass + @attributes = attributes + @group = @attributes[:group] + @project = @attributes[:project] + end + + def find + find_or_action do + label? ? @klass.new(project_attributes) : @klass.create(project_attributes) + end + end + + private + + def find_or_action(&block) + @klass.where(where_clause).first || yield + end + + def where_clause + return { project_id: @project.id } unless milestone? || label? + + @attributes.slice('title').map do |key, value| + if @group + project_clause(key, value).or(group_clause(key, value)) + else + project_clause(key, value) + end + end.reduce(:or) + end + + def group_clause(key, value) + table[key].eq(value).and(table[:group_id].eq(@group.id)) + end + + def project_clause(key, value) + table[key].eq(value).and(table[:project_id].eq(@project.id)) + end + + def table + @table ||= @klass.arel_table + end + + def project_attributes + @attributes.except(:group).tap do |atts| + if label? + atts['type'] = 'ProjectLabel' # Always create project labels + elsif milestone? + if atts['group_id'] # Transform new group milestones into project ones + atts['iid'] = nil + atts.delete('group_id') + else + claim_iid + end + end + end + end + + def label? + @klass == Label + end + + def milestone? + @klass == Milestone + end + + # If an existing group milesone used the IID + # claim the IID back and set the group milestone to use one available + # This is neccessary to fix situations like the following: + # - Importing into a user namespace project with exported group milestones + # where the IID of the Group milestone could conflict with a project one. + def claim_iid + group_milestone = @project.milestones.find_by(iid: @attributes['iid']) + + group_milestone.update!(iid: max_milestone_iid + 1) if group_milestone + end + + def max_milestone_iid + [@attributes['iid'], @project.milestones.maximum(:iid)].compact.max + end + end + end +end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index 5efebf867ad..c5e8aa54f5d 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -266,7 +266,7 @@ module Gitlab hash.delete('project_id') end - GroupProjectFinder.find_or_create(relation_class, finder_hash) + GroupProjectObjectBuilder.build(relation_class, finder_hash) end end end diff --git a/spec/lib/gitlab/import_export/group_project_finder_spec.rb b/spec/lib/gitlab/import_export/group_project_finder_spec.rb deleted file mode 100644 index c36d80e51d4..00000000000 --- a/spec/lib/gitlab/import_export/group_project_finder_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'spec_helper' - -describe Gitlab::ImportExport::GroupProjectFinder do - let(:project) do - create(:project, - :builds_disabled, - :issues_disabled, - name: 'project', - path: 'project', - group: create(:group)) - end - - context 'labels' do - it 'finds the right group label' do - group_label = create(:group_label, 'name': 'group label', 'group': project.group) - - expect(described_class.find_or_new(Label, - title: 'group label', - project: project, - group: project.group)).to eq(group_label) - end - - it 'initializes a new label' do - label = described_class.find_or_new(Label, - title: 'group label', - project: project, - group: project.group) - - expect(label.persisted?).to be false - end - - it 'creates a new label' do - label = described_class.find_or_create(Label, - title: 'group label', - project: project, - group: project.group) - - expect(label.persisted?).to be true - end - end - - context 'milestones' do - it 'finds the right group milestone' do - milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group) - - expect(described_class.find_or_new(Milestone, - title: 'group milestone', - project: project, - group: project.group)).to eq(milestone) - end - - it 'initializes a new milestone' do - milestone = described_class.find_or_new(Milestone, - title: 'group milestone', - project: project, - group: project.group) - - expect(milestone.persisted?).to be false - end - - it 'creates a new milestone' do - milestone = described_class.find_or_create(Milestone, - title: 'group milestone', - project: project, - group: project.group) - - expect(milestone.persisted?).to be true - end - end -end diff --git a/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb new file mode 100644 index 00000000000..5ca353d612a --- /dev/null +++ b/spec/lib/gitlab/import_export/group_project_object_builder_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::GroupProjectObjectBuilder do + let(:project) do + create(:project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + context 'labels' do + it 'finds the right group label' do + group_label = create(:group_label, 'name': 'group label', 'group': project.group) + + expect(described_class.build(Label, + title: 'group label', + project: project, + group: project.group)).to eq(group_label) + end + + it 'initializes a new label' do + label = described_class.build(Label, + title: 'group label', + project: project, + group: project.group) + + expect(label.persisted?).to be false + end + end + + context 'milestones' do + it 'finds the right group milestone' do + milestone = create(:milestone, 'name' => 'group milestone', 'group' => project.group) + + expect(described_class.build(Milestone, + title: 'group milestone', + project: project, + group: project.group)).to eq(milestone) + end + + it 'creates a new milestone' do + milestone = described_class.build(Milestone, + title: 'group milestone', + project: project, + group: project.group) + + expect(milestone.persisted?).to be true + end + end +end -- cgit v1.2.1 From 35d69ccf95e18031cd232c594282a1671d97d1ae Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 09:48:44 +0200 Subject: add more specs and refactor more relation factory code --- lib/gitlab/import_export/project_tree_restorer.rb | 8 +-- lib/gitlab/import_export/relation_factory.rb | 12 +++- .../import_export/project.milestone-iid.json | 80 ++++++++++++++++++++++ .../import_export/project_tree_restorer_spec.rb | 27 ++++++++ 4 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 spec/lib/gitlab/import_export/project.milestone-iid.json diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb index 25661a42ad5..8b30483a5e2 100644 --- a/lib/gitlab/import_export/project_tree_restorer.rb +++ b/lib/gitlab/import_export/project_tree_restorer.rb @@ -178,7 +178,7 @@ module Gitlab def create_relation(relation, relation_hash_list) relation_array = [relation_hash_list].flatten.map do |relation_hash| Gitlab::ImportExport::RelationFactory.create(relation_sym: relation.to_sym, - relation_hash: parsed_relation_hash(relation_hash, relation.to_sym), + relation_hash: relation_hash, members_mapper: members_mapper, user: @user, project: @restored_project, @@ -188,12 +188,6 @@ module Gitlab relation_hash_list.is_a?(Array) ? relation_array : relation_array.first end - def parsed_relation_hash(relation_hash, relation_type) - params = { 'group_id' => restored_project.group.try(:id), 'project_id' => restored_project.id } - - relation_hash.merge(params) - end - def reader @reader ||= Gitlab::ImportExport::Reader.new(shared: @shared) end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index c5e8aa54f5d..f5a20a4d333 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -54,6 +54,8 @@ module Gitlab @project = project @imported_object_retries = 0 + @relation_hash['project_id'] = @project.id + # Remove excluded keys from relation_hash # We don't do this in the parsed_relation_hash because of the 'transformed attributes' # For example, MergeRequestDiffFiles exports its diff attribute as utf8_diff. Then, @@ -81,12 +83,11 @@ module Gitlab when :merge_request_diff_files then setup_diff when :notes then setup_note when 'Ci::Pipeline' then setup_pipeline - else - @relation_hash['project_id'] = @project.id end update_user_references update_project_references + update_group_references remove_duplicate_assignees reset_tokens! @@ -161,6 +162,13 @@ module Gitlab @relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id'] end + def update_group_references + return unless EXISTING_OBJECT_CHECK.include?(@relation_name) + return unless @relation_hash['group_id'] + + @relation_hash['group_id'] = @project.group&.id + end + def same_source_and_target? @relation_hash['target_project_id'] && @relation_hash['target_project_id'] == @relation_hash['source_project_id'] end diff --git a/spec/lib/gitlab/import_export/project.milestone-iid.json b/spec/lib/gitlab/import_export/project.milestone-iid.json new file mode 100644 index 00000000000..b028147b5eb --- /dev/null +++ b/spec/lib/gitlab/import_export/project.milestone-iid.json @@ -0,0 +1,80 @@ +{ + "description": "Nisi et repellendus ut enim quo accusamus vel magnam.", + "import_type": "gitlab_project", + "creator_id": 123, + "visibility_level": 10, + "archived": false, + "issues": [ + { + "id": 1, + "title": "Fugiat est minima quae maxime non similique.", + "assignee_id": null, + "project_id": 8, + "author_id": 1, + "created_at": "2017-07-07T18:13:01.138Z", + "updated_at": "2017-08-15T18:37:40.807Z", + "branch_name": null, + "description": "Quam totam fuga numquam in eveniet.", + "state": "opened", + "iid": 20, + "updated_by_id": 1, + "confidential": false, + "due_date": null, + "moved_to_id": null, + "lock_version": null, + "time_estimate": 0, + "closed_at": null, + "last_edited_at": null, + "last_edited_by_id": null, + "group_milestone_id": null, + "milestone": { + "id": 1, + "title": "Group-level milestone", + "description": "Group-level milestone", + "due_date": null, + "created_at": "2016-06-14T15:02:04.415Z", + "updated_at": "2016-06-14T15:02:04.415Z", + "state": "active", + "iid": 1, + "group_id": 8 + } + }, + { + "id": 2, + "title": "est minima quae maxime non similique.", + "assignee_id": null, + "project_id": 8, + "author_id": 1, + "created_at": "2017-07-07T18:13:01.138Z", + "updated_at": "2017-08-15T18:37:40.807Z", + "branch_name": null, + "description": "Quam totam fuga numquam in eveniet.", + "state": "opened", + "iid": 21, + "updated_by_id": 1, + "confidential": false, + "due_date": null, + "moved_to_id": null, + "lock_version": null, + "time_estimate": 0, + "closed_at": null, + "last_edited_at": null, + "last_edited_by_id": null, + "group_milestone_id": null, + "milestone": { + "id": 2, + "title": "Another milestone", + "project_id": 8, + "description": "milestone", + "due_date": null, + "created_at": "2016-06-14T15:02:04.415Z", + "updated_at": "2016-06-14T15:02:04.415Z", + "state": "active", + "iid": 1, + "group_id": null + } + } + ], + "snippets": [], + "hooks": [] +} diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 94251e2280b..1601b913b91 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -391,5 +391,32 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do expect(project.milestones.count).to eq(0) end end + + context 'with clashing milestones on IID' do + let!(:project) do + create(:project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + before do + project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.milestone-iid.json") + end + + it 'preserves the project milestone IID' do + create(:milestone, name: 'A milestone', group: project.group) + + expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) + + restored_project_json + + expect(project.milestones.count).to eq(2) + expect(Milestone.find_by_title('Another milestone').iid).to eq(1) + expect(Milestone.find_by_title('Group-level milestone').iid).to eq(2) + end + end end end -- cgit v1.2.1 From fb5ca695c475ef9d9eb9b32f53bf9d5471b3c531 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 09:54:44 +0200 Subject: fix spec --- spec/lib/gitlab/import_export/project_tree_restorer_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 1601b913b91..612f1ba4043 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -407,8 +407,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do end it 'preserves the project milestone IID' do - create(:milestone, name: 'A milestone', group: project.group) - expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) restored_project_json -- cgit v1.2.1 From d8252efea9c0b8ad18275318e0af762f3b5ba59b Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 09:55:16 +0200 Subject: fix spec --- spec/lib/gitlab/import_export/project_tree_restorer_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 612f1ba4043..bac5693c830 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -402,11 +402,9 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do group: create(:group)) end - before do + it 'preserves the project milestone IID' do project_tree_restorer.instance_variable_set(:@path, "spec/lib/gitlab/import_export/project.milestone-iid.json") - end - it 'preserves the project milestone IID' do expect_any_instance_of(Gitlab::ImportExport::Shared).not_to receive(:error) restored_project_json -- cgit v1.2.1 From ec394d4908c339a54417a5ad89f3ed0504c1b123 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 22 Jun 2018 11:27:52 +0200 Subject: add some more comments --- lib/gitlab/import_export/group_project_object_builder.rb | 12 +++++++----- lib/gitlab/import_export/project_tree_restorer.rb | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/gitlab/import_export/group_project_object_builder.rb b/lib/gitlab/import_export/group_project_object_builder.rb index c41d68e7aab..829fe6098ee 100644 --- a/lib/gitlab/import_export/group_project_object_builder.rb +++ b/lib/gitlab/import_export/group_project_object_builder.rb @@ -1,12 +1,12 @@ module Gitlab module ImportExport # Given a class, it finds or creates a new object - # (initializes in the case of Label) at group or project level - # If it does not exist in the group, it creates it at project level + # (initializes in the case of Label) at group or project level. + # If it does not exist in the group, it creates it at project level. # - # For example: - # `GroupProjectObjectBuilder.build(Label, label_attributes)` - # finds or initializes a label with the given attributes. + # Example: + # `GroupProjectObjectBuilder.build(Label, label_attributes)` + # finds or initializes a label with the given attributes. # # It also adds some logic around Group Labels/Milestones for edge cases. class GroupProjectObjectBuilder @@ -88,6 +88,8 @@ module Gitlab # - Importing into a user namespace project with exported group milestones # where the IID of the Group milestone could conflict with a project one. def claim_iid + # The milestone has to be a group milestone, as it's the only case where + # we set the IID as the maximum. The rest of them are fixed. group_milestone = @project.milestones.find_by(iid: @attributes['iid']) group_milestone.update!(iid: max_milestone_iid + 1) if group_milestone diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb index 8b30483a5e2..32d517ea679 100644 --- a/lib/gitlab/import_export/project_tree_restorer.rb +++ b/lib/gitlab/import_export/project_tree_restorer.rb @@ -78,6 +78,9 @@ module Gitlab @restored_project = Project.find(@project_id) end + # Remove project models that became group models as we found them at group level. + # This no longer required saving them at the root project level. + # For example, in the case of an existing group label that matched the title. def remove_group_models(relation_hash) relation_hash.reject! do |value| value.respond_to?(:group_id) && value.group_id && GROUP_MODELS.include?(value.class) -- cgit v1.2.1 From 3a14ae3ae3fd2376d46bb4af9678e648eb7a840c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 25 Jun 2018 09:42:07 +0200 Subject: refactor code based on feedback --- .../import_export/group_project_object_builder.rb | 20 ++++++++++++-------- lib/gitlab/import_export/project_tree_restorer.rb | 4 ++-- lib/gitlab/import_export/relation_factory.rb | 18 ++++++++---------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/gitlab/import_export/group_project_object_builder.rb b/lib/gitlab/import_export/group_project_object_builder.rb index 829fe6098ee..31f61502948 100644 --- a/lib/gitlab/import_export/group_project_object_builder.rb +++ b/lib/gitlab/import_export/group_project_object_builder.rb @@ -36,19 +36,17 @@ module Gitlab end def where_clause - return { project_id: @project.id } unless milestone? || label? - @attributes.slice('title').map do |key, value| if @group - project_clause(key, value).or(group_clause(key, value)) + project_group_clause(key, value) else project_clause(key, value) end end.reduce(:or) end - def group_clause(key, value) - table[key].eq(value).and(table[:group_id].eq(@group.id)) + def project_group_clause(key, value) + table[key].eq(value).and(table[:project_id].eq(@project.id).or(table[:group_id].eq(@group.id))) end def project_clause(key, value) @@ -92,11 +90,17 @@ module Gitlab # we set the IID as the maximum. The rest of them are fixed. group_milestone = @project.milestones.find_by(iid: @attributes['iid']) - group_milestone.update!(iid: max_milestone_iid + 1) if group_milestone + group_milestone.update!(iid: max_milestone_iid(group_milestone)) if group_milestone end - def max_milestone_iid - [@attributes['iid'], @project.milestones.maximum(:iid)].compact.max + def max_milestone_iid(group_milestone) + init_iid = [@attributes['iid'], @project.milestones.maximum(:iid)].compact.max + 1 + + InternalId::InternalIdGenerator.new(group_milestone, + { project: @project }, + :milestones, + init_iid + ).generate end end end diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb index 32d517ea679..76b99b1de16 100644 --- a/lib/gitlab/import_export/project_tree_restorer.rb +++ b/lib/gitlab/import_export/project_tree_restorer.rb @@ -1,7 +1,7 @@ module Gitlab module ImportExport class ProjectTreeRestorer - # Relations which cannot be saved at project level + # Relations which cannot be saved at project level (and have a group assigned) GROUP_MODELS = [GroupLabel, Milestone].freeze def initialize(user:, shared:, project:) @@ -83,7 +83,7 @@ module Gitlab # For example, in the case of an existing group label that matched the title. def remove_group_models(relation_hash) relation_hash.reject! do |value| - value.respond_to?(:group_id) && value.group_id && GROUP_MODELS.include?(value.class) + GROUP_MODELS.include?(value.class) && value.group_id end end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index f5a20a4d333..094981defbc 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -150,16 +150,16 @@ module Gitlab end def update_project_references - project_id = @relation_hash.delete('project_id') - # If source and target are the same, populate them with the new project ID. if @relation_hash['source_project_id'] - @relation_hash['source_project_id'] = same_source_and_target? ? project_id : MergeRequestParser::FORKED_PROJECT_ID + @relation_hash['source_project_id'] = same_source_and_target? ? @relation_hash['project_id'] : MergeRequestParser::FORKED_PROJECT_ID end - # project_id may not be part of the export, but we always need to populate it if required. - @relation_hash['project_id'] = project_id - @relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id'] + @relation_hash['target_project_id'] = @relation_hash['project_id'] if @relation_hash['target_project_id'] + end + + def same_source_and_target? + @relation_hash['target_project_id'] && @relation_hash['target_project_id'] == @relation_hash['source_project_id'] end def update_group_references @@ -169,10 +169,6 @@ module Gitlab @relation_hash['group_id'] = @project.group&.id end - def same_source_and_target? - @relation_hash['target_project_id'] && @relation_hash['target_project_id'] == @relation_hash['source_project_id'] - end - def reset_tokens! return unless Gitlab::ImportExport.reset_tokens? && TOKEN_RESET_MODELS.include?(@relation_name.to_s) @@ -267,6 +263,8 @@ module Gitlab end def find_or_create_object! + return relation_class.find_or_create_by(project_id: @project.id) if @relation_name == :project_feature + # Can't use IDs as validation exists calling `group` or `project` attributes finder_hash = parsed_relation_hash.tap do |hash| hash[:group] = @project.group if relation_class.attribute_method?('group_id') -- cgit v1.2.1 From 266451e68dbe680c4fab702ef0140bf23e71d9fa Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 5 Jun 2018 14:04:25 +0200 Subject: Change path to CI job traces --- doc/administration/high_availability/gitlab.md | 3 +-- doc/administration/high_availability/nfs.md | 4 +--- doc/administration/job_traces.md | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md index 0d9c10687f2..2da23d60af3 100644 --- a/doc/administration/high_availability/gitlab.md +++ b/doc/administration/high_availability/gitlab.md @@ -28,7 +28,6 @@ for each GitLab application server in your environment. 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 ``` @@ -36,7 +35,7 @@ for each GitLab application server in your environment. mount locations. ``` - mkdir -p /var/opt/gitlab/.ssh /var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/git-data + mkdir -p /var/opt/gitlab/.ssh /var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/git-data ``` 1. Download/install GitLab Omnibus using **steps 1 and 2** from diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md index 87e96b71dd4..94084ee9b8c 100644 --- a/doc/administration/high_availability/nfs.md +++ b/doc/administration/high_availability/nfs.md @@ -130,7 +130,6 @@ NFS mount point is `/gitlab-nfs`. Then, add the following bind mounts in /gitlab-nfs/gitlab-data/.ssh /var/opt/gitlab/.ssh none bind 0 0 /gitlab-nfs/gitlab-data/uploads /var/opt/gitlab/gitlab-rails/uploads none bind 0 0 /gitlab-nfs/gitlab-data/shared /var/opt/gitlab/gitlab-rails/shared none bind 0 0 -/gitlab-nfs/gitlab-data/builds /var/opt/gitlab/gitlab-ci/builds none bind 0 0 ``` Using bind mounts will require manually making sure the data directories @@ -148,8 +147,7 @@ following are the 5 locations need to be shared: | `/var/opt/gitlab/git-data` | Git repository data. This will account for a large portion of your data | `git_data_dirs({"default" => "/var/opt/gitlab/git-data"})` | `/var/opt/gitlab/.ssh` | SSH `authorized_keys` file and keys used to import repositories from some other Git services | `user['home'] = '/var/opt/gitlab/'` | `/var/opt/gitlab/gitlab-rails/uploads` | User uploaded attachments | `gitlab_rails['uploads_directory'] = '/var/opt/gitlab/gitlab-rails/uploads'` -| `/var/opt/gitlab/gitlab-rails/shared` | Build artifacts, GitLab Pages, LFS objects, temp files, etc. If you're using LFS this may also account for a large portion of your data | `gitlab_rails['shared_path'] = '/var/opt/gitlab/gitlab-rails/shared'` -| `/var/opt/gitlab/gitlab-ci/builds` | GitLab CI build traces | `gitlab_ci['builds_directory'] = '/var/opt/gitlab/gitlab-ci/builds'` +| `/var/opt/gitlab/gitlab-rails/shared` | Job artifacts, job traces, GitLab Pages, LFS objects, temp files, etc. If you're using LFS this may also account for a large portion of your data | `gitlab_rails['shared_path'] = '/var/opt/gitlab/gitlab-rails/shared'` Other GitLab directories should not be shared between nodes. They contain node-specific files and GitLab code that does not need to be shared. To ship diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md index a5cd2b642dc..54697f09b75 100644 --- a/doc/administration/job_traces.md +++ b/doc/administration/job_traces.md @@ -1,6 +1,6 @@ # Job traces (logs) -By default, all job traces (logs) are saved to `/var/opt/gitlab/gitlab-ci/builds` +By default, all job traces (logs) are saved to `/var/opt/gitlab/shared/asrtifacts` and `/home/git/gitlab/builds` for Omnibus packages and installations from source respectively. The job logs are organized by year and month (for example, `2017_03`), and then by project ID. -- cgit v1.2.1 From f41f8eca53ba109bdb4c3ab506c66eadcb37bcb5 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 12 Jun 2018 20:49:32 +0900 Subject: Fix trace documentation --- doc/administration/job_traces.md | 58 ++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md index 54697f09b75..623a33ceb05 100644 --- a/doc/administration/job_traces.md +++ b/doc/administration/job_traces.md @@ -1,9 +1,16 @@ # Job traces (logs) -By default, all job traces (logs) are saved to `/var/opt/gitlab/shared/asrtifacts` -and `/home/git/gitlab/builds` for Omnibus packages and installations from source -respectively. The job logs are organized by year and month (for example, `2017_03`), -and then by project ID. +Job traces are sent by gitlab-runner while it's processing a job. You can see traces in job pages, pipelines, email notifications, etc. +Basically, there are two states in job traces. One is "Live trace", and another one is "Archived trace"; + +|state|condition|step|data flow|stored path| +|---|---|---|---|---| +|Live trace|when a job is running|1: patching| gitlab-runner => gitlab-unicorn => file storage|`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| +|Live trace|when a job is finished|2: overwtiring| gitlab-runner => gitlab-unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| +|Archived trace|After a job is finished|3: archiving| sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| + +The `ROOT_PATH` varies per your enviroment. For example, if you used omnibus packages, it would be `/var/opt/gitlab/gitlab-ci`, +whereas if you used source instlation, it would be `/home/git/gitlab`. There isn't a way to automatically expire old job logs, but it's safe to remove them if they're taking up too much space. If you remove the logs manually, the @@ -41,24 +48,46 @@ To change the location where the job logs will be stored, follow the steps below [reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab" [restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab" +## Upload traces to object storage + +Archived trace is one of [job artifacts](job_artifacts.md). +If you set up [object storage settings](https://docs.gitlab.com/ce/administration/job_artifacts.html#object-storage-settings), +job traces are automatically migrated to object storage as well as other job artifacts. + +Here is the data flow; + +|state|condition|step|data flow|stored path| +|---|---|---|---|---| +|Live trace|when a job is running|1: patching| gitlab-runner => gitlab-unicorn => file storage|`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| +|Live trace|when a job is finished|2: overwtiring| gitlab-runner => gitlab-unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| +|Archived trace|After a job is finished|3: archiving| sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| +|Archived trace|After a trace is archived|4: uploading| sidekiq moves archived trace to object storage |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| + ## New live trace architecture -> [Introduced][ce-18169] in GitLab 10.4. +> [Introduced][ce-18169] in GitLab 10.4. +> [Announced as General availability][ce-46097] in GitLab 11.0. > **Notes**: -- This feature is still Beta, which could impact GitLab.com/on-premises instances, and in the worst case scenario, traces will be lost. -- This feature is still being discussed in [an issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/46097) for the performance improvements. +- Performance improvements are scheduled in [11.1](https://gitlab.com/gitlab-org/gitlab-ce/issues/47125). - This feature is off by default. Please check below how to enable/disable this featrue. -**What is "live trace"?** +**For cloud-native compatible application** + +By combining the process with object storage settings, we can completely bypass file storage. This is useful option in cloud-native GitLab installtion. + +Here is the data flow; -Job trace that is sent by runner while jobs are running. You can see live trace in job pages UI. -The live traces are archived once job finishes. +|state|condition|step|data flow|stored path| +|---|---|---|---|---| +|Live trace|when a job is running|1: patching| gitlab-runner => gitlab-unicorn => redis and database|- (Stored in Redis and Database, instead)| +|Live trace|when a job is finished|2: overwtiring| gitlab-runner => gitlab-unicorn => redis and database |- (Stored in Redis and Database, instead)| +|Archived trace|After a job is finished|3: archiving| sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| +|Archived trace|After a trace is archived|4: uploading| sidekiq moves archived trace to object storage |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| -**What is new architecture?** +(Step 3 is scheduled to be improved in https://gitlab.com/gitlab-org/gitlab-ce/issues/44663) -So far, when GitLab Runner sends a job trace to GitLab-Rails, traces have been saved to file storage as text files. -This was a problem for [Cloud Native-compatible GitLab application](https://gitlab.com/gitlab-com/migration/issues/23) where GitLab had to rely on File Storage. +**The detailed mechanizm** This new live trace architecture stores chunks of traces in Redis and database instead of file storage. Redis is used as first-class storage, and it stores up-to 128kB. Once the full chunk is sent it will be flushed to database. Afterwhile, the data in Redis and database will be archived to ObjectStorage. @@ -134,4 +163,5 @@ We're currently evaluating this feature on dev.gitalb.org or staging.gitlab.com - TBD -[ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169 \ No newline at end of file +[ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169 +[ce-46097]: https://gitlab.com/gitlab-org/gitlab-ce/issues/46097 -- cgit v1.2.1 From 0b6f7a2a8cd369885c4a5d22a0de9330a4f5d509 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 22 Jun 2018 17:49:08 +0200 Subject: Refactor the job traces admin docs --- doc/administration/high_availability/gitlab.md | 3 +- doc/administration/high_availability/nfs.md | 4 +- doc/administration/job_traces.md | 172 ++++++++++++------------- 3 files changed, 90 insertions(+), 89 deletions(-) diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md index 2da23d60af3..0d9c10687f2 100644 --- a/doc/administration/high_availability/gitlab.md +++ b/doc/administration/high_availability/gitlab.md @@ -28,6 +28,7 @@ for each GitLab application server in your environment. 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 ``` @@ -35,7 +36,7 @@ for each GitLab application server in your environment. mount locations. ``` - mkdir -p /var/opt/gitlab/.ssh /var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/git-data + mkdir -p /var/opt/gitlab/.ssh /var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/git-data ``` 1. Download/install GitLab Omnibus using **steps 1 and 2** from diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md index 94084ee9b8c..87e96b71dd4 100644 --- a/doc/administration/high_availability/nfs.md +++ b/doc/administration/high_availability/nfs.md @@ -130,6 +130,7 @@ NFS mount point is `/gitlab-nfs`. Then, add the following bind mounts in /gitlab-nfs/gitlab-data/.ssh /var/opt/gitlab/.ssh none bind 0 0 /gitlab-nfs/gitlab-data/uploads /var/opt/gitlab/gitlab-rails/uploads none bind 0 0 /gitlab-nfs/gitlab-data/shared /var/opt/gitlab/gitlab-rails/shared none bind 0 0 +/gitlab-nfs/gitlab-data/builds /var/opt/gitlab/gitlab-ci/builds none bind 0 0 ``` Using bind mounts will require manually making sure the data directories @@ -147,7 +148,8 @@ following are the 5 locations need to be shared: | `/var/opt/gitlab/git-data` | Git repository data. This will account for a large portion of your data | `git_data_dirs({"default" => "/var/opt/gitlab/git-data"})` | `/var/opt/gitlab/.ssh` | SSH `authorized_keys` file and keys used to import repositories from some other Git services | `user['home'] = '/var/opt/gitlab/'` | `/var/opt/gitlab/gitlab-rails/uploads` | User uploaded attachments | `gitlab_rails['uploads_directory'] = '/var/opt/gitlab/gitlab-rails/uploads'` -| `/var/opt/gitlab/gitlab-rails/shared` | Job artifacts, job traces, GitLab Pages, LFS objects, temp files, etc. If you're using LFS this may also account for a large portion of your data | `gitlab_rails['shared_path'] = '/var/opt/gitlab/gitlab-rails/shared'` +| `/var/opt/gitlab/gitlab-rails/shared` | Build artifacts, GitLab Pages, LFS objects, temp files, etc. If you're using LFS this may also account for a large portion of your data | `gitlab_rails['shared_path'] = '/var/opt/gitlab/gitlab-rails/shared'` +| `/var/opt/gitlab/gitlab-ci/builds` | GitLab CI build traces | `gitlab_ci['builds_directory'] = '/var/opt/gitlab/gitlab-ci/builds'` Other GitLab directories should not be shared between nodes. They contain node-specific files and GitLab code that does not need to be shared. To ship diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md index 623a33ceb05..ffc6b91b9b9 100644 --- a/doc/administration/job_traces.md +++ b/doc/administration/job_traces.md @@ -1,22 +1,30 @@ # Job traces (logs) -Job traces are sent by gitlab-runner while it's processing a job. You can see traces in job pages, pipelines, email notifications, etc. -Basically, there are two states in job traces. One is "Live trace", and another one is "Archived trace"; - -|state|condition|step|data flow|stored path| -|---|---|---|---|---| -|Live trace|when a job is running|1: patching| gitlab-runner => gitlab-unicorn => file storage|`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| -|Live trace|when a job is finished|2: overwtiring| gitlab-runner => gitlab-unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| -|Archived trace|After a job is finished|3: archiving| sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| - -The `ROOT_PATH` varies per your enviroment. For example, if you used omnibus packages, it would be `/var/opt/gitlab/gitlab-ci`, -whereas if you used source instlation, it would be `/home/git/gitlab`. +Job traces are sent by GitLab Runner while it's processing a job. You can see +traces in job pages, pipelines, email notifications, etc. There isn't a way to automatically expire old job logs, but it's safe to remove them if they're taking up too much space. If you remove the logs manually, the job output in the UI will be empty. -## Changing the job traces location +## Data flow + +In general, there are two states in job traces: "live trace" and "archived trace". +In the following table you can see the phases a trace is going through its +journey. + +| Phase | State | Condition | Data flow | Stored path | +| ----- | ----- | --------- | --------- | ----------- | +| 1: patching | Live trace | When a job is running | GitLab Runner => Unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| +| 2: overwriting | Live trace | When a job is finished | GitLab Runner => Unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| +| 3: archiving | Archived trace | After a job is finished | Sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| +| 4: uploading | Archived trace | After a trace is archived | Sidekiq moves archived trace to [object storage](#uploading-traces-to-object-storage) (if configured) |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| + +The `ROOT_PATH` varies per your environment. For example, Omnibus GitLab it +would be `/var/opt/gitlab/gitlab-ci`, whereas for installations from source +it would be `/home/git/gitlab`. + +## Changing the job traces local location To change the location where the job logs will be stored, follow the steps below. @@ -48,120 +56,110 @@ To change the location where the job logs will be stored, follow the steps below [reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab" [restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab" -## Upload traces to object storage +## Uploading traces to object storage -Archived trace is one of [job artifacts](job_artifacts.md). -If you set up [object storage settings](https://docs.gitlab.com/ce/administration/job_artifacts.html#object-storage-settings), -job traces are automatically migrated to object storage as well as other job artifacts. +An archived trace is considered as a [job artifact](job_artifacts.md). +Therefore, when you [set up an object storage](job_artifacts.md#object-storage-settings), +job traces are automatically migrated to it along with the other job artifacts. -Here is the data flow; - -|state|condition|step|data flow|stored path| -|---|---|---|---|---| -|Live trace|when a job is running|1: patching| gitlab-runner => gitlab-unicorn => file storage|`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| -|Live trace|when a job is finished|2: overwtiring| gitlab-runner => gitlab-unicorn => file storage |`#{ROOT_PATH}/builds/#{YYYY_mm}/#{project_id}/#{job_id}.log`| -|Archived trace|After a job is finished|3: archiving| sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| -|Archived trace|After a trace is archived|4: uploading| sidekiq moves archived trace to object storage |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| +Check the [data flow](#data-flow) to learn about the process. ## New live trace architecture -> [Introduced][ce-18169] in GitLab 10.4. +> [Introduced][ce-18169] in GitLab 10.4. > [Announced as General availability][ce-46097] in GitLab 11.0. -> **Notes**: -- Performance improvements are scheduled in [11.1](https://gitlab.com/gitlab-org/gitlab-ce/issues/47125). -- This feature is off by default. Please check below how to enable/disable this featrue. +NOTE: **Note:** +This feature is off by default. Check below how to [enable/disable](#enabling-live-trace) it. -**For cloud-native compatible application** +By combining the process with object storage settings, we can completely bypass +the local file storage. This is a useful option if GitLab is installed as +cloud-native, for example on Kubernetes. -By combining the process with object storage settings, we can completely bypass file storage. This is useful option in cloud-native GitLab installtion. +The data flow is the same as described in the [data flow section](#data-flow) +with one change: _the stored path of the first two phases is different_. This new live +trace architecture stores chunks of traces in Redis and the database instead of +file storage. Redis is used as first-class storage, and it stores up-to 128KB +of data. Once the full chunk is sent, it is flushed to database. After a while, +the data in Redis and database will be archived to [object storage](#uploading-traces-to-object-storage). -Here is the data flow; +The data are stored in the following Redis namespace: `Gitlab::Redis::SharedState`. -|state|condition|step|data flow|stored path| -|---|---|---|---|---| -|Live trace|when a job is running|1: patching| gitlab-runner => gitlab-unicorn => redis and database|- (Stored in Redis and Database, instead)| -|Live trace|when a job is finished|2: overwtiring| gitlab-runner => gitlab-unicorn => redis and database |- (Stored in Redis and Database, instead)| -|Archived trace|After a job is finished|3: archiving| sidekiq moves live trace to artifacts folder |`#{ROOT_PATH}/shared/artifacts/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| -|Archived trace|After a trace is archived|4: uploading| sidekiq moves archived trace to object storage |`#{bucket_name}/#{disk_hash}/#{YYYY_mm_dd}/#{job_id}/#{job_artifact_id}/trace.log`| +Here is the detailed data flow: -(Step 3 is scheduled to be improved in https://gitlab.com/gitlab-org/gitlab-ce/issues/44663) +1. GitLab Runner picks a job from GitLab +1. GitLab Runner sends a piece of trace to GitLab +1. GitLab appends the data to Redis +1. Once the data in Redis reach 128KB, the data is flushed to the database. +1. The above steps are repeated until the job is finished. +1. Once the job is finished, GitLab schedules a Sidekiq worker to archive the trace. +1. The Sidekiq worker archives the trace to object storage and cleans up the trace + in Redis and the database. -**The detailed mechanizm** +### Enabling live trace -This new live trace architecture stores chunks of traces in Redis and database instead of file storage. -Redis is used as first-class storage, and it stores up-to 128kB. Once the full chunk is sent it will be flushed to database. Afterwhile, the data in Redis and database will be archived to ObjectStorage. +The following commands are to be issues in a Rails console: -Here is the detailed data flow. +```sh +# Omnibus GitLab +gitlab-rails console -1. GitLab Runner picks a job from GitLab-Rails -1. GitLab Runner sends a piece of trace to GitLab-Rails -1. GitLab-Rails appends the data to Redis -1. If the data in Redis is fulfilled 128kB, the data is flushed to Database. -1. 2.~4. is continued until the job is finished -1. Once the job is finished, GitLab-Rails schedules a sidekiq worker to archive the trace -1. The sidekiq worker archives the trace to Object Storage, and cleanup the trace in Redis and Database +# Installation from source +cd /home/git/gitlab +sudo -u git -H bin/rails console RAILS_ENV=production +``` -**How to check if it's on or off?** +**To check if live trace is enabled:** ```ruby Feature.enabled?('ci_enable_live_trace') ``` -**How to enable?** +**To enable live trace:** ```ruby Feature.enable('ci_enable_live_trace') ``` ->**Note:** -The transition period will be handled gracefully. Upcoming traces will be generated with the new architecture, and on-going live traces will stay with the legacy architecture (i.e. on-going live traces won't be re-generated forcibly with the new architecture). +NOTE: **Note:** +The transition period will be handled gracefully. Upcoming traces will be +generated with the new architecture, and on-going live traces will stay with the +legacy architecture, which means that on-going live traces won't be forcibly +re-generated with the new architecture. -**How to disable?** +**To disable live trace:** ```ruby Feature.disable('ci_enable_live_trace') ``` ->**Note:** -The transition period will be handled gracefully. Upcoming traces will be generated with the legacy architecture, and on-going live traces will stay with the new architecture (i.e. on-going live traces won't be re-generated forcibly with the legacy architecture). - -**Redis namespace:** - -`Gitlab::Redis::SharedState` - -**Potential impact:** +NOTE: **Note:** +The transition period will be handled gracefully. Upcoming traces will be generated +with the legacy architecture, and on-going live traces will stay with the new +architecture, which means that on-going live traces won't be forcibly re-generated +with the legacy architecture. -- This feature could incur data loss: - - Case 1: When all data in Redis are accidentally flushed. - - On-going live traces could be recovered by re-sending traces (This is supported by all versions of GitLab Runner) - - Finished jobs which has not archived live traces will lose the last part (~128kB) of trace data. - - Case 2: When sidekiq workers failed to archive (e.g. There was a bug that prevents archiving process, Sidekiq inconsistancy, etc): - - Currently all trace data in Redis will be deleted after one week. If the sidekiq workers can't finish by the expiry date, the part of trace data will be lost. -- This feature could consume all memory on Redis instance. If the number of jobs is 1000, 128MB (128kB * 1000) is consumed. -- This feature could pressure Database replication lag. `INSERT` are generated to indicate that we have trace chunk. `UPDATE` with 128kB of data is issued once we receive multiple chunks. -- and so on +### Potential implications -**How to test?** +In some cases, having data stored on Redis could incur data loss: -We're currently evaluating this feature on dev.gitalb.org or staging.gitlab.com to verify this features. Here is the list of tests/measurements. +1. **Case 1: When all data in Redis are accidentally flushed** + - On going live traces could be recovered by re-sending traces (this is + supported by all versions of the GitLab Runner). + - Finished jobs which have not archived live traces will lose the last part + (~128KB) of trace data. -- Features: - - Live traces should be visible on job pages - - Archived traces should be visible on job pages - - Live traces should be archived to Object storage - - Live traces should be cleaned up after archived - - etc -- Performance: - - Schedule 1000~10000 jobs and let GitLab-runners process concurrently. Measure memoery presssure, IO load, etc. - - etc -- Failover: - - Simulate Redis outage - - etc +1. **Case 2: When Sidekiq workers fail to archive (e.g., there was a bug that + prevents archiving process, Sidekiq inconsistency, etc.)** + - Currently all trace data in Redis will be deleted after one week. If the + Sidekiq workers can't finish by the expiry date, the part of trace data will be lost. -**How to verify the correctnesss?** +Another issue that might arise is that it could consume all memory on the Redis +instance. If the number of jobs is 1000, 128MB (128KB * 1000) is consumed. -- TBD +Also, it could pressure the database replication lag. `INSERT`s are generated to +indicate that we have trace chunk. `UPDATE`s with 128KB of data is issued once we +receive multiple chunks. [ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169 [ce-46097]: https://gitlab.com/gitlab-org/gitlab-ce/issues/46097 -- cgit v1.2.1 From 792082948cbe8c02329ae711d550ce2ea6b1fea8 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Mon, 25 Jun 2018 12:04:23 +0200 Subject: fix space --- lib/gitlab/import_export/group_project_object_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/import_export/group_project_object_builder.rb b/lib/gitlab/import_export/group_project_object_builder.rb index 31f61502948..793657f5cea 100644 --- a/lib/gitlab/import_export/group_project_object_builder.rb +++ b/lib/gitlab/import_export/group_project_object_builder.rb @@ -100,7 +100,7 @@ module Gitlab { project: @project }, :milestones, init_iid - ).generate + ).generate end end end -- cgit v1.2.1 From 5468557a5f211c2a03f81ecf11d4c7567a4d9158 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 25 Jun 2018 12:35:55 +0100 Subject: Added spacing between milestone list and tabs --- app/assets/stylesheets/pages/milestone.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 9ef7a4f2f08..022c334ef49 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -4,6 +4,7 @@ .milestones { padding: $gl-padding-8; + margin-top: $gl-padding-8; border-radius: $border-radius-default; background-color: $theme-gray-100; -- cgit v1.2.1 From 3219b202222e91f129d69ec9b71f6e5ecc3dc0e4 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 25 Jun 2018 12:48:48 +0100 Subject: Updated stlying of title and date range of milestone list --- app/assets/stylesheets/pages/milestone.scss | 2 -- app/views/shared/_milestone_expired.html.haml | 2 +- app/views/shared/milestones/_milestone.html.haml | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 022c334ef49..f103c84e339 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -255,8 +255,6 @@ .milestone-range { color: $gl-text-color-tertiary; - margin: $gl-bar-padding 0 $gl-padding-4; - font-size: $tooltip-font-size; } @include media-breakpoint-down(xs) { diff --git a/app/views/shared/_milestone_expired.html.haml b/app/views/shared/_milestone_expired.html.haml index 738622a4993..e58f346b5b5 100644 --- a/app/views/shared/_milestone_expired.html.haml +++ b/app/views/shared/_milestone_expired.html.haml @@ -1,5 +1,5 @@ - if milestone.due_date || milestone.start_date - .milestone-range + .milestone-range.append-bottom-5 = milestone_date_range(milestone) - if milestone.expired? and not milestone.closed? .status-box.status-box-expired Expired diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 4661126c904..297b88d2342 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -5,7 +5,7 @@ %li{ class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } .row .col-sm-6 - %div + .append-bottom-5 %strong= link_to truncate(milestone.title, length: 100), milestone_path - if @group = " - #{milestone_type}" -- cgit v1.2.1 From a87a5836c3e6acd0f5cccf631c12e240c0dfaaa5 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 25 Jun 2018 14:31:19 +0100 Subject: Fixed alignment of milestone status and projects in milestone list --- app/views/shared/_milestone_expired.html.haml | 3 --- app/views/shared/milestones/_milestone.html.haml | 18 +++++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/views/shared/_milestone_expired.html.haml b/app/views/shared/_milestone_expired.html.haml index e58f346b5b5..f49017c59b2 100644 --- a/app/views/shared/_milestone_expired.html.haml +++ b/app/views/shared/_milestone_expired.html.haml @@ -1,6 +1,3 @@ -- if milestone.due_date || milestone.start_date - .milestone-range.append-bottom-5 - = milestone_date_range(milestone) - if milestone.expired? and not milestone.closed? .status-box.status-box-expired Expired - if milestone.upcoming? diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index 297b88d2342..d1b87b616ed 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -11,13 +11,17 @@ = " - #{milestone_type}" - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone? - = render('shared/milestone_expired', milestone: milestone) - - if milestone.legacy_group_milestone? - .projects - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.label-badge.label-badge-blue - = dashboard ? milestone.project.full_name : milestone.project.name + - if milestone.due_date || milestone.start_date + .milestone-range.append-bottom-5 + = milestone_date_range(milestone) + %div + = render('shared/milestone_expired', milestone: milestone) + - if milestone.legacy_group_milestone? + .projects + - milestone.milestones.each do |milestone| + = link_to milestone_path(milestone) do + %span.label-badge.label-badge-blue.d-inline-block.append-bottom-5.append-right-5 + = dashboard ? milestone.project.full_name : milestone.project.name .col-sm-4.milestone-progress = milestone_progress_bar(milestone) -- cgit v1.2.1 From b4267aaa8bd317c3ccad8a84ee9f2eecfdf35d55 Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Mon, 25 Jun 2018 17:58:02 +0300 Subject: Update external link icon in merge request widget --- .../vue_merge_request_widget/components/deployment.vue | 11 ++++++----- app/assets/stylesheets/pages/merge_requests.scss | 4 ++++ .../update-external-link-icon-in-merge-request-widget.yml | 5 +++++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/update-external-link-icon-in-merge-request-widget.yml diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue index c44419d24e6..5e464f8a0e2 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue @@ -1,4 +1,5 @@