From dab95689a3f267e6ee4e4fa9da304a01d37a5a4e Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Thu, 23 Jun 2016 13:07:51 +0000 Subject: Fix `ref` parameter name for `commits/statuses` The attribute to filter by branch or tag needs to be named `ref`, not `ref_name`. And indeed the attribute in the JSON response is `ref` (and not `ref_name`). Tested on Gitlab CE 8.9. --- doc/api/commits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/commits.md b/doc/api/commits.md index 57c2e1d9b87..d1317e5cc07 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -229,7 +229,7 @@ GET /projects/:id/repository/commits/:sha/statuses | --------- | ---- | -------- | ----------- | | `id` | integer | yes | The ID of a project | `sha` | string | yes | The commit SHA -| `ref_name`| string | no | The name of a repository branch or tag or, if not given, the default branch +| `ref` | string | no | The name of a repository branch or tag or, if not given, the default branch | `stage` | string | no | Filter by [build stage](../ci/yaml/README.md#stages), e.g., `test` | `name` | string | no | Filter by [job name](../ci/yaml/README.md#jobs), e.g., `bundler:audit` | `all` | boolean | no | Return all statuses, not only the latest ones -- cgit v1.2.1 From 33540238ee7da03ef532385e7a52103276252c18 Mon Sep 17 00:00:00 2001 From: Christian Meter Date: Thu, 7 Jul 2016 11:47:13 +0000 Subject: Replace builds_emails_service.png to fit to new layout of GitLab --- doc/project_services/img/builds_emails_service.png | Bin 33943 -> 30956 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/project_services/img/builds_emails_service.png b/doc/project_services/img/builds_emails_service.png index 88943dc410e..440728795be 100644 Binary files a/doc/project_services/img/builds_emails_service.png and b/doc/project_services/img/builds_emails_service.png differ -- cgit v1.2.1 From 51ef233e25d6b9cfed16b8b3a5b532e429993dec Mon Sep 17 00:00:00 2001 From: Niels Keurentjes Date: Sun, 10 Jul 2016 23:35:10 +0000 Subject: Fix docker.sock reference in config.toml Mapping was omitted so it would just create a temp volume. --- doc/ci/docker/using_docker_build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 7f83f846454..ee924323d96 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -188,7 +188,7 @@ In order to do that, follow the steps: image = "docker:latest" privileged = false disable_cache = false - volumes = ["/var/run/docker.sock", "/cache"] + volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"] [runners.cache] Insecure = false ``` -- cgit v1.2.1 From 24538ec0902667d399b47c7715aa75035aacecf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Libor=20Klep=C3=A1=C4=8D?= Date: Mon, 8 Aug 2016 16:28:57 +1000 Subject: Fix error 500 on Runners page. ~~~~ ActionView::Template::Error (Missing partial kaminari/_paginator ~~~~ Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=819903 Signed-off-by: Dmitry Smirnov --- app/views/admin/runners/index.html.haml | 2 +- app/views/admin/runners/show.html.haml | 2 +- app/views/projects/runners/_specific_runners.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index a53876d6757..f68b6cba524 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -73,4 +73,4 @@ - @runners.each do |runner| = render "admin/runners/runner", runner: runner - = paginate @runners + = paginate @runners, theme: "gitlab" diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 61abfc6ecbe..8ca2c97481c 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -67,7 +67,7 @@ = form_for [:admin, project.namespace.becomes(Namespace), project, project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: @runner.id = f.submit 'Enable', class: 'btn btn-xs' - = paginate @projects + = paginate @projects, theme: "gitlab" .col-md-6 %h4 Recent builds served by this runner diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index d469dda5b81..4278980d1f4 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -26,4 +26,4 @@ %h4.underlined-title Available specific runners %ul.bordered-list.available-specific-runners = render partial: 'runner', collection: @assignable_runners, as: :runner - = paginate @assignable_runners + = paginate @assignable_runners, theme: "gitlab" -- cgit v1.2.1 From eecb52e14585638b8538cfbbdef10283eeda94e4 Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Wed, 21 Sep 2016 03:49:22 -0700 Subject: improve docs on gitlab-ci-token user --- doc/ci/docker/using_docker_build.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 0f64137a8a9..364fda838e3 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -242,10 +242,10 @@ docker-in-docker on your runners, this is how your `.gitlab-ci.yml` could look: - docker push registry.example.com/group/project:latest ``` -You have to use the credentials of the special `gitlab-ci-token` user with its -password stored in `$CI_BUILD_TOKEN` in order to push to the Registry connected -to your project. This allows you to automate building and deployment of your -Docker images. +You have to use the special `gitlab-ci-token` user created for you in order to +push to the Registry connected to your project. Its password is provided in the +`$CI_BUILD_TOKEN` variable. This allows you to automate building and deployment +of your Docker images. Here's a more elaborate example that splits up the tasks into 4 pipeline stages, including two tests that run in parallel. The build is stored in the container -- cgit v1.2.1 From 01384edd6f7dad755710caa8f8dad0bd4c7da6bc Mon Sep 17 00:00:00 2001 From: Pierre-Alexandre Clorichel Date: Thu, 29 Sep 2016 19:12:32 +0000 Subject: users should remove former Go installation folder --- doc/install/installation.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/install/installation.md b/doc/install/installation.md index cb4c1f4a091..ec0bd115656 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -142,6 +142,9 @@ gitlab-workhorse we need a Go compiler. The instructions below assume you use 64-bit Linux. You can find downloads for other platforms at the [Go download page](https://golang.org/dl). + # Remove former Go installation folder + sudo rm -rf /usr/local/go + curl --remote-name --progress https://storage.googleapis.com/golang/go1.5.3.linux-amd64.tar.gz echo '43afe0c5017e502630b1aea4d44b8a7f059bf60d7f29dfd58db454d4e4e0ae53 go1.5.3.linux-amd64.tar.gz' | shasum -a256 -c - && \ sudo tar -C /usr/local -xzf go1.5.3.linux-amd64.tar.gz -- cgit v1.2.1 From 25f4c9334d80906b0155cea64cb6fcaff2f2110e Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 30 Sep 2016 07:19:10 +0000 Subject: Add info about creating a personal access token --- doc/profile/two_factor_authentication.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/profile/two_factor_authentication.md b/doc/profile/two_factor_authentication.md index 82505b13401..3f6dfe03d14 100644 --- a/doc/profile/two_factor_authentication.md +++ b/doc/profile/two_factor_authentication.md @@ -117,6 +117,22 @@ Click on **Authenticate via U2F Device** to complete the process. This will clear all your two-factor authentication registrations, including mobile applications and U2F devices. +## Personal access tokens + +When 2FA is enabled, you can no longer use your normal account password to +authenticate with Git over HTTPS on the command line, you must use a personal +access token instead. + +1. Log in to your GitLab account. +1. Go to your **Profile Settings**. +1. Go to **Access Tokens**. +1. Choose a name and expiry date for the token. +1. Click on **Create Personal Access Token**. +1. Save the personal access token somewhere safe. + +When using git over HTTPS on the command line, enter the personal access token +into the password field. + ## Note to GitLab administrators You need to take special care to that 2FA keeps working after -- cgit v1.2.1 From c920aad3ee82900c11f240f913abcd2caa0a3353 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 3 Oct 2016 01:11:50 +0000 Subject: Grammar fixes in docs --- doc/ci/docker/using_docker_images.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index a849905ac6b..1007db3df1a 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -37,7 +37,7 @@ The registered runner will use the `ruby:2.1` docker image and will run two services, `postgres:latest` and `mysql:latest`, both of which will be accessible during the build process. -## What is image +## What is an image The `image` keyword is the name of the docker image that is present in the local Docker Engine (list all images with `docker images`) or any image that @@ -47,7 +47,7 @@ Hub please read the [Docker Fundamentals][] documentation. In short, with `image` we refer to the docker image, which will be used to create a container on which your build will run. -## What is service +## What is a service The `services` keyword defines just another docker image that is run during your build and is linked to the docker image that the `image` keyword defines. @@ -61,7 +61,7 @@ time the project is built. You can see some widely used services examples in the relevant documentation of [CI services examples](../services/README.md). -### How is service linked to the build +### How services are linked to the build To better understand how the container linking works, read [Linking containers together][linking-containers]. -- cgit v1.2.1 From d2a9eefbfe86f1a152673d34f5803107c79c7d51 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 3 Oct 2016 23:18:35 +0800 Subject: Show commits from source project. Be consistent with: MergeRequest#pipeline Fixes #3596 --- app/views/projects/merge_requests/show/_commits.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml index 0b05785430b..61020516bcf 100644 --- a/app/views/projects/merge_requests/show/_commits.html.haml +++ b/app/views/projects/merge_requests/show/_commits.html.haml @@ -3,4 +3,4 @@ Most recent commits displayed first %ol#commits-list.list-unstyled - = render "projects/commits/commits", project: @merge_request.project + = render "projects/commits/commits", project: @merge_request.source_project -- cgit v1.2.1 From f11ebb9556612f663264868a24ee8b774babda32 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 5 Oct 2016 18:08:16 +0800 Subject: Add a view test for showing source commits --- .../merge_requests/_commits.html.haml_spec.rb | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 spec/views/projects/merge_requests/_commits.html.haml_spec.rb diff --git a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb new file mode 100644 index 00000000000..6f70b3daf8e --- /dev/null +++ b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe 'projects/merge_requests/show/_commits.html.haml' do + include Devise::Test::ControllerHelpers + + let(:user) { create(:user) } + let(:target_project) { create(:project) } + + let(:source_project) do + create(:project, forked_from_project: target_project) + end + + let(:merge_request) do + create(:merge_request, :simple, + source_project: source_project, + target_project: target_project, + author: user) + end + + before do + controller.prepend_view_path('app/views/projects') + + assign(:merge_request, merge_request) + assign(:commits, merge_request.commits) + end + + it 'shows commits from source project' do + render + + commit = source_project.commit(merge_request.source_branch) + href = namespace_project_commit_path( + source_project.namespace, + source_project, + commit) + + expect(rendered).to have_link(Commit.truncate_sha(commit.sha), href: href) + end +end -- cgit v1.2.1 From b5707a80ac1caa3aad809c0c9a0ad4fa91c9a834 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 5 Oct 2016 21:54:55 +0800 Subject: Add CHANGELOG entry [ci skip] --- CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 58f1e4a59c2..5bb9ff1ec0e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,7 @@ v 8.13.0 (unreleased) - Add word-wrap to issue title on issue and milestone boards (ClemMakesApps) - Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison) - Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska) + - Fix showing commits from source project for merge request !6658 - Add configurable email subject suffix (Fu Xu) - Use a ConnectionPool for Rails.cache on Sidekiq servers - Replace `alias_method_chain` with `Module#prepend` @@ -42,7 +43,7 @@ v 8.13.0 (unreleased) - Notify the Merger about merge after successful build (Dimitris Karakasilis) - Fix broken repository 500 errors in project list - Close todos when accepting merge requests via the API !6486 (tonygambone) - - Changed Slack service user referencing from full name to username (Sebastian Poxhofer) + - Changed Slack service user referencing from full name to username (Sebastian Poxhofer) v 8.12.4 (unreleased) -- cgit v1.2.1 From 7c59f45d10a791e790f55821ca501d9cdeddbabf Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 4 Oct 2016 15:27:02 +0100 Subject: Added markup for sidebar --- app/assets/javascripts/boards/boards_bundle.js.es6 | 4 ++- .../boards/components/board_card.js.es6 | 4 +++ .../boards/components/board_sidebar.js.es6 | 25 ++++++++++++++ .../javascripts/boards/stores/boards_store.js.es6 | 4 ++- .../projects/boards/components/_card.html.haml | 3 +- .../projects/boards/components/_sidebar.html.haml | 40 ++++++++++++++++++++++ app/views/projects/boards/show.html.haml | 10 +++--- 7 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 app/assets/javascripts/boards/components/board_sidebar.js.es6 create mode 100644 app/views/projects/boards/components/_sidebar.html.haml diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index 91c12570e09..b3a2386bbf3 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -6,6 +6,7 @@ //= require_tree ./services //= require_tree ./mixins //= require ./components/board +//= require ./components/board_sidebar //= require ./components/new_list_dropdown //= require ./vue_resource_interceptor @@ -22,7 +23,8 @@ $(() => { gl.IssueBoardsApp = new Vue({ el: $boardApp, components: { - 'board': gl.issueBoards.Board + 'board': gl.issueBoards.Board, + 'board-sidebar': gl.issueBoards.BoardSidebar }, data: { state: Store.state, diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 index 4a7cfeaeab2..1bd0b19b6da 100644 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ b/app/assets/javascripts/boards/components/board_card.js.es6 @@ -37,6 +37,10 @@ $('.labels-filter .dropdown-toggle-text').text(labelToggleText); Store.updateFiltersUrl(); + }, + showIssue () { + Store.state.detailIssue = this.issue; + console.log(Store.state.detailIssue); } } }); diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6 new file mode 100644 index 00000000000..e26c8209b69 --- /dev/null +++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6 @@ -0,0 +1,25 @@ +(() => { + const Store = gl.issueBoards.BoardsStore; + + window.gl = window.gl || {}; + window.gl.issueBoards = window.gl.issueBoards || {}; + + gl.issueBoards.BoardSidebar = Vue.extend({ + data() { + return { + issue: Store.state.detailIssue + }; + }, + ready: function () { + console.log(this.issue); + }, + watch: { + issue: { + handler () { + console.log('a'); + }, + deep: true + } + } + }); +})(); diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6 index bd07ee0c161..126e3f49fc8 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js.es6 +++ b/app/assets/javascripts/boards/stores/boards_store.js.es6 @@ -4,7 +4,9 @@ gl.issueBoards.BoardsStore = { disabled: false, - state: {}, + state: { + detailIssue: {} + }, moving: { issue: {}, list: {} diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml index d8f16022407..71f64d7827c 100644 --- a/app/views/projects/boards/components/_card.html.haml +++ b/app/views/projects/boards/components/_card.html.haml @@ -8,7 +8,8 @@ ":disabled" => "disabled", "track-by" => "id" } %li.card{ ":class" => "{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id }", - ":index" => "index" } + ":index" => "index", + "@click" => "showIssue" } %h4.card-title = icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential") %a{ ":href" => "issueLinkBase + '/' + issue.id", diff --git a/app/views/projects/boards/components/_sidebar.html.haml b/app/views/projects/boards/components/_sidebar.html.haml new file mode 100644 index 00000000000..6cec833d6dc --- /dev/null +++ b/app/views/projects/boards/components/_sidebar.html.haml @@ -0,0 +1,40 @@ +%board-sidebar{ "inline-template" => true } + %aside.right-sidebar.right-sidebar-expanded{ "v-if" => "showSidebar" } + .issuable-sidebar + .block.issuable-sidebar-header + %span.issuable-header-text.hide-collapsed.pull-left + %strong Test + %br/ + %span #13 + %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } + = icon("times") + .block.assignee + .title.hide-collapsed + Assignee + = icon("spinner spin", class: "block-loading") + = link_to "Edit", "#", class: "edit-link pull-right" + .value.hide-collapsed + %span.assign-yourself.no-value + No assignee + \- + %a.js-assign-yourself{ href: "#" } + assign yourself + .block.milestone + .title.hide-collapsed + Milestone + = icon("spinner spin", class: "block-loading") + .value.hide-collapsed + %span.no-value + None + .block.due_date + .title.hide-collapsed + Due date + = icon("spinner spin", class: "block-loading") + .value.hide-collapsed + %span.no-value No due date + .block.labels + .title.hide-collapsed + Labels + = icon("spinner spin", class: "block-loading") + .value.issuable-show-labels.hide-collapsed + %span.no-value None diff --git a/app/views/projects/boards/show.html.haml b/app/views/projects/boards/show.html.haml index edbbd3f3d2a..10b495363a6 100644 --- a/app/views/projects/boards/show.html.haml +++ b/app/views/projects/boards/show.html.haml @@ -10,10 +10,12 @@ = render 'shared/issuable/filter', type: :boards -.boards-list#board-app{ "v-cloak" => true, +#board-app{ "v-cloak" => true, "data-endpoint" => "#{namespace_project_board_path(@project.namespace, @project)}", "data-disabled" => "#{!can?(current_user, :admin_list, @project)}", "data-issue-link-base" => "#{namespace_project_issues_path(@project.namespace, @project)}" } - .boards-app-loading.text-center{ "v-if" => "loading" } - = icon("spinner spin") - = render "projects/boards/components/board" + .boards-list + .boards-app-loading.text-center{ "v-if" => "loading" } + = icon("spinner spin") + = render "projects/boards/components/board" + = render "projects/boards/components/sidebar" -- cgit v1.2.1 From 6cece3f49eb8777929b8a18a4fea1b8249bbb9df Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 5 Oct 2016 08:49:40 +0100 Subject: Show clicked issue data in the sidebar --- .../boards/components/board_card.js.es6 | 3 +- .../boards/components/board_sidebar.js.es6 | 29 ++++++++++--- app/assets/javascripts/boards/models/issue.js.es6 | 5 +++ .../javascripts/boards/models/milestone.js.es6 | 6 +++ .../javascripts/boards/stores/boards_store.js.es6 | 5 ++- app/assets/javascripts/right_sidebar.js | 11 ++++- .../projects/boards/issues_controller.rb | 2 +- .../projects/boards/components/_sidebar.html.haml | 47 +++++++--------------- .../boards/components/sidebar/_assignee.html.haml | 22 ++++++++++ .../boards/components/sidebar/_due_date.html.haml | 16 ++++++++ .../boards/components/sidebar/_labels.html.haml | 18 +++++++++ .../boards/components/sidebar/_milestone.html.haml | 16 ++++++++ .../components/sidebar/_notifications.html.haml | 11 +++++ 13 files changed, 147 insertions(+), 44 deletions(-) create mode 100644 app/assets/javascripts/boards/models/milestone.js.es6 create mode 100644 app/views/projects/boards/components/sidebar/_assignee.html.haml create mode 100644 app/views/projects/boards/components/sidebar/_due_date.html.haml create mode 100644 app/views/projects/boards/components/sidebar/_labels.html.haml create mode 100644 app/views/projects/boards/components/sidebar/_milestone.html.haml create mode 100644 app/views/projects/boards/components/sidebar/_notifications.html.haml diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 index 1bd0b19b6da..e61943de5a9 100644 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ b/app/assets/javascripts/boards/components/board_card.js.es6 @@ -39,8 +39,7 @@ Store.updateFiltersUrl(); }, showIssue () { - Store.state.detailIssue = this.issue; - console.log(Store.state.detailIssue); + Store.detail.issue = this.issue; } } }); diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6 index e26c8209b69..c267b4f0817 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js.es6 +++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6 @@ -7,18 +7,37 @@ gl.issueBoards.BoardSidebar = Vue.extend({ data() { return { - issue: Store.state.detailIssue + detail: Store.detail, + issue: {} }; }, - ready: function () { - console.log(this.issue); + computed: { + showSidebar () { + return Object.keys(this.issue).length; + } }, watch: { - issue: { + detail: { handler () { - console.log('a'); + this.issue = this.detail.issue; }, deep: true + }, + issue () { + if (this.showSidebar) { + this.$nextTick(() => { + new IssuableContext(); + new MilestoneSelect(); + new Sidebar(); + }); + } else { + $('.right-sidebar').getNiceScroll().remove(); + } + } + }, + methods: { + closeSidebar () { + this.detail.issue = {}; } } }); diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6 index eb082103de9..a8c28e27f41 100644 --- a/app/assets/javascripts/boards/models/issue.js.es6 +++ b/app/assets/javascripts/boards/models/issue.js.es6 @@ -3,12 +3,17 @@ class ListIssue { this.id = obj.iid; this.title = obj.title; this.confidential = obj.confidential; + this.dueDate = obj.due_date; this.labels = []; if (obj.assignee) { this.assignee = new ListUser(obj.assignee); } + if (obj.milestone) { + this.milestone = new ListMilestone(obj.milestone); + } + obj.labels.forEach((label) => { this.labels.push(new ListLabel(label)); }); diff --git a/app/assets/javascripts/boards/models/milestone.js.es6 b/app/assets/javascripts/boards/models/milestone.js.es6 new file mode 100644 index 00000000000..577adf11265 --- /dev/null +++ b/app/assets/javascripts/boards/models/milestone.js.es6 @@ -0,0 +1,6 @@ +class ListMilestone { + constructor (obj) { + this.id = obj.id; + this.title = obj.title; + } +} diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6 index 126e3f49fc8..a6c5739ee41 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js.es6 +++ b/app/assets/javascripts/boards/stores/boards_store.js.es6 @@ -4,8 +4,9 @@ gl.issueBoards.BoardsStore = { disabled: false, - state: { - detailIssue: {} + state: {}, + detail: { + issue: {} }, moving: { issue: {}, diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index e3d5f413c77..221460f32e7 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -5,15 +5,24 @@ function Sidebar(currentUser) { this.toggleTodo = bind(this.toggleTodo, this); this.sidebar = $('aside'); + this.removeListeners(); this.addEventListeners(); } + Sidebar.prototype.removeListeners = function () { + this.sidebar.off('click', '.sidebar-collapsed-icon'); + $('.dropdown').off('hidden.gl.dropdown'); + $('.dropdown').off('loading.gl.dropdown'); + $('.dropdown').off('loaded.gl.dropdown'); + $(document).off('click', '.js-sidebar-toggle'); + } + Sidebar.prototype.addEventListeners = function() { this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked); $('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden); $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading); $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded); - $(document).off('click', '.js-sidebar-toggle').on('click', '.js-sidebar-toggle', function(e, triggered) { + $(document).on('click', '.js-sidebar-toggle', function(e, triggered) { var $allGutterToggleIcons, $this, $thisIcon; e.preventDefault(); $this = $(this); diff --git a/app/controllers/projects/boards/issues_controller.rb b/app/controllers/projects/boards/issues_controller.rb index 095af6c35eb..b5a56d11d32 100644 --- a/app/controllers/projects/boards/issues_controller.rb +++ b/app/controllers/projects/boards/issues_controller.rb @@ -73,7 +73,7 @@ module Projects def serialize_as_json(resource) resource.as_json( - only: [:iid, :title, :confidential], + only: [:iid, :title, :confidential, :due_date], include: { assignee: { only: [:id, :name, :username], methods: [:avatar_url] }, labels: { only: [:id, :title, :description, :color, :priority], methods: [:text_color] } diff --git a/app/views/projects/boards/components/_sidebar.html.haml b/app/views/projects/boards/components/_sidebar.html.haml index 6cec833d6dc..b03b8384220 100644 --- a/app/views/projects/boards/components/_sidebar.html.haml +++ b/app/views/projects/boards/components/_sidebar.html.haml @@ -3,38 +3,19 @@ .issuable-sidebar .block.issuable-sidebar-header %span.issuable-header-text.hide-collapsed.pull-left - %strong Test + %strong + {{ issue.title }} %br/ - %span #13 - %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", aria: { label: "Toggle sidebar" } } + %span + = precede "#" do + {{ issue.id }} + %a.gutter-toggle.pull-right{ role: "button", + href: "#", + "@click" => "closeSidebar", + aria: { label: "Toggle sidebar" } } = icon("times") - .block.assignee - .title.hide-collapsed - Assignee - = icon("spinner spin", class: "block-loading") - = link_to "Edit", "#", class: "edit-link pull-right" - .value.hide-collapsed - %span.assign-yourself.no-value - No assignee - \- - %a.js-assign-yourself{ href: "#" } - assign yourself - .block.milestone - .title.hide-collapsed - Milestone - = icon("spinner spin", class: "block-loading") - .value.hide-collapsed - %span.no-value - None - .block.due_date - .title.hide-collapsed - Due date - = icon("spinner spin", class: "block-loading") - .value.hide-collapsed - %span.no-value No due date - .block.labels - .title.hide-collapsed - Labels - = icon("spinner spin", class: "block-loading") - .value.issuable-show-labels.hide-collapsed - %span.no-value None + = render "projects/boards/components/sidebar/assignee" + = render "projects/boards/components/sidebar/milestone" + = render "projects/boards/components/sidebar/due_date" + = render "projects/boards/components/sidebar/labels" + = render "projects/boards/components/sidebar/notifications" diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml new file mode 100644 index 00000000000..93780fc929e --- /dev/null +++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml @@ -0,0 +1,22 @@ +.block.assignee + .title.hide-collapsed + Assignee + = icon("spinner spin", class: "block-loading") + - if can?(current_user, :admin_issue, @project) + = link_to "Edit", "#", class: "edit-link pull-right" + .value.hide-collapsed + %span.assign-yourself.no-value{ "v-if" => "!issue.assignee" } + No assignee + - if can?(current_user, :admin_issue, @project) + \- + %a.js-assign-yourself{ href: "#" } + assign yourself + %a.author_link.bold{ href: "", + "v-if" => "issue.assignee" } + %img.avatar.avatar-inline.s32{ ":src" => "issue.assignee.avatar", + width: "32" } + %span.author + {{ issue.assignee.name }} + %span.username + = precede "@" do + {{ issue.assignee.username }} diff --git a/app/views/projects/boards/components/sidebar/_due_date.html.haml b/app/views/projects/boards/components/sidebar/_due_date.html.haml new file mode 100644 index 00000000000..9861523824a --- /dev/null +++ b/app/views/projects/boards/components/sidebar/_due_date.html.haml @@ -0,0 +1,16 @@ +.block.due_date + .title.hide-collapsed + Due date + = icon("spinner spin", class: "block-loading") + - if can?(current_user, :admin_issue, @project) + = link_to "Edit", "#", class: "edit-link pull-right" + .value.hide-collapsed + .value-content + %span.no-value{ "v-if" => "!issue.dueDate" } + No due date + %span.bold{ "v-if" => "issue.dueDate" } + {{ issue.dueDate }} + %span.no-value.js-remove-due-date-holder{ "v-if" => "issue.dueDate" } + \- + %a.js-remove-due-date{ href: "#", role: "button" } + remove due date diff --git a/app/views/projects/boards/components/sidebar/_labels.html.haml b/app/views/projects/boards/components/sidebar/_labels.html.haml new file mode 100644 index 00000000000..b135b134a96 --- /dev/null +++ b/app/views/projects/boards/components/sidebar/_labels.html.haml @@ -0,0 +1,18 @@ +.block.labels + .title.hide-collapsed + Labels + = icon("spinner spin", class: "block-loading") + - if can?(current_user, :admin_issue, @project) + = link_to "Edit", "#", class: "edit-link pull-right" + .value.issuable-show-labels.hide-collapsed + %span.no-value{ "v-if" => "issue.labels.length === 0" } + None + %a{ href: "#", + "v-for" => "label in issue.labels" } + %span.label.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" } + {{ label.title }} + .selectbox.hide-collapsed + %input{ type: "hidden", + name: "issue[label_names][]", + "v-for" => "label in issue.labels", + ":value" => "label.id" } diff --git a/app/views/projects/boards/components/sidebar/_milestone.html.haml b/app/views/projects/boards/components/sidebar/_milestone.html.haml new file mode 100644 index 00000000000..14f521f57a4 --- /dev/null +++ b/app/views/projects/boards/components/sidebar/_milestone.html.haml @@ -0,0 +1,16 @@ +.block.milestone + .title.hide-collapsed + Milestone + = icon("spinner spin", class: "block-loading") + - if can?(current_user, :admin_issue, @project) + = link_to "Edit", "#", class: "edit-link pull-right" + .value + %span.no-value{ "v-if" => "!issue.milestone" } + None + %span.bold.has-tooltip{ "v-if" => "issue.milestone" } + {{ issue.milestone.title }} + .selectbox + .dropdown + %button.dropdown-menu-toggle + Milestone + -# = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: true }}) diff --git a/app/views/projects/boards/components/sidebar/_notifications.html.haml b/app/views/projects/boards/components/sidebar/_notifications.html.haml new file mode 100644 index 00000000000..e28be47fd8e --- /dev/null +++ b/app/views/projects/boards/components/sidebar/_notifications.html.haml @@ -0,0 +1,11 @@ +- if current_user + .block.light.subscription{ data: { url: '' } } + .title.hide-collapsed + Notifications + %button.btn.btn-block.btn-default.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" } + Unsubscribe + .subscription-status.hide-collapsed{ data: { status: '' } } + .unsubscribed{class: ( 'hidden' if true )} + You're not receiving notifications from this thread. + .subscribed{class: ( 'hidden' unless true )} + You're receiving notifications because you're subscribed to this thread. -- cgit v1.2.1 From 6b3e3aeb9e6b78ade960a4fad1da906fa023cd5e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 5 Oct 2016 12:20:59 +0100 Subject: Sidebar details update when changing Need to get working the subscription Styling updates --- app/assets/javascripts/boards/boards_bundle.js.es6 | 1 + .../boards/components/board_sidebar.js.es6 | 8 +++- .../boards/filters/due_date_filters.js.es6 | 4 ++ app/assets/javascripts/boards/models/issue.js.es6 | 14 +++++++ .../boards/services/board_service.js.es6 | 2 - app/assets/javascripts/due_date_select.js | 29 ++++++++++++-- app/assets/javascripts/labels_select.js | 28 +++++++++++++- app/assets/javascripts/milestone_select.js | 20 +++++++++- app/assets/javascripts/subscription.js | 19 ++++++---- app/assets/javascripts/users_select.js | 44 ++++++++++++++++++++-- app/assets/stylesheets/pages/boards.scss | 15 ++++++++ .../projects/boards/components/_sidebar.html.haml | 16 ++++---- .../boards/components/sidebar/_assignee.html.haml | 18 +++++++++ .../boards/components/sidebar/_due_date.html.haml | 21 +++++++++-- .../boards/components/sidebar/_labels.html.haml | 26 +++++++++---- .../boards/components/sidebar/_milestone.html.haml | 24 +++++++++--- .../components/sidebar/_notifications.html.haml | 12 +++--- app/views/projects/boards/show.html.haml | 2 +- 18 files changed, 254 insertions(+), 49 deletions(-) create mode 100644 app/assets/javascripts/boards/filters/due_date_filters.js.es6 diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index b3a2386bbf3..c5dd51c39d7 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -5,6 +5,7 @@ //= require_tree ./stores //= require_tree ./services //= require_tree ./mixins +//= require_tree ./filters //= require ./components/board //= require ./components/board_sidebar //= require ./components/new_list_dropdown diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6 index c267b4f0817..12f69f93279 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js.es6 +++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6 @@ -5,6 +5,9 @@ window.gl.issueBoards = window.gl.issueBoards || {}; gl.issueBoards.BoardSidebar = Vue.extend({ + props: { + currentUser: Object + }, data() { return { detail: Store.detail, @@ -26,9 +29,12 @@ issue () { if (this.showSidebar) { this.$nextTick(() => { - new IssuableContext(); + new IssuableContext(this.currentUser); new MilestoneSelect(); + new DueDateSelect(); + new LabelsSelect(); new Sidebar(); + new Subscription('.subscription') }); } else { $('.right-sidebar').getNiceScroll().remove(); diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 b/app/assets/javascripts/boards/filters/due_date_filters.js.es6 new file mode 100644 index 00000000000..5c1519986c1 --- /dev/null +++ b/app/assets/javascripts/boards/filters/due_date_filters.js.es6 @@ -0,0 +1,4 @@ +Vue.filter('due-date', (value) => { + const date = new Date(value.replace(new RegExp('-', 'g'), ',')); + return $.datepicker.formatDate('M d, yy', date); +}); diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6 index a8c28e27f41..fac753dadb5 100644 --- a/app/assets/javascripts/boards/models/issue.js.es6 +++ b/app/assets/javascripts/boards/models/issue.js.es6 @@ -4,6 +4,7 @@ class ListIssue { this.title = obj.title; this.confidential = obj.confidential; this.dueDate = obj.due_date; + this.subscribed = true; this.labels = []; if (obj.assignee) { @@ -46,4 +47,17 @@ class ListIssue { getLists () { return gl.issueBoards.BoardsStore.state.lists.filter( list => list.findIssue(this.id) ); } + + update (url) { + const data = { + issue: { + milestone_id: this.milestone ? this.milestone.id : null, + due_date: this.dueDate, + assignee_id: this.assignee ? this.assignee.id : null, + label_ids: this.labels.map((label) => label.id ) + } + }; + + return Vue.http.patch(url, data); + } } diff --git a/app/assets/javascripts/boards/services/board_service.js.es6 b/app/assets/javascripts/boards/services/board_service.js.es6 index 2b825c3949f..b7a9ea16204 100644 --- a/app/assets/javascripts/boards/services/board_service.js.es6 +++ b/app/assets/javascripts/boards/services/board_service.js.es6 @@ -1,7 +1,5 @@ class BoardService { constructor (root) { - Vue.http.options.root = root; - this.lists = Vue.resource(`${root}/lists{/id}`, {}, { generate: { method: 'POST', diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js index bf68b7e3a9b..aad81892311 100644 --- a/app/assets/javascripts/due_date_select.js +++ b/app/assets/javascripts/due_date_select.js @@ -38,6 +38,19 @@ return $value.css('display', ''); } }); + + var updateIssueBoardIssue = function () { + $dropdown.trigger('loading.gl.dropdown'); + $selectbox.hide(); + $value.css('display', ''); + $loading.fadeIn(); + + gl.issueBoards.BoardsStore.detail.issue.update(issueUpdateURL) + .then(function () { + $loading.fadeOut(); + }); + } + addDueDate = function(isDropdown) { var data, date, mediumDate, value; // Create the post date @@ -83,15 +96,25 @@ }; $block.on('click', '.js-remove-due-date', function(e) { e.preventDefault(); - $("input[name='" + fieldName + "']").val(''); - return addDueDate(false); + if ($dropdown.hasClass('js-issue-boards-due-date')) { + gl.issueBoards.BoardsStore.detail.issue.dueDate = ''; + updateIssueBoardIssue(); + } else { + $("input[name='" + fieldName + "']").val(''); + return addDueDate(false); + } }); return $datePicker.datepicker({ dateFormat: 'yy-mm-dd', defaultDate: $("input[name='" + fieldName + "']").val(), altField: "input[name='" + fieldName + "']", onSelect: function() { - return addDueDate(true); + if ($dropdown.hasClass('js-issue-boards-due-date')) { + gl.issueBoards.BoardsStore.detail.issue.dueDate = $("input[name='" + fieldName + "']").val(); + updateIssueBoardIssue(); + } else { + return addDueDate(true); + } } }); }); diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index e356872624a..6e5d3dff9eb 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -22,7 +22,7 @@ abilityName = $dropdown.data('ability-name'); $selectbox = $dropdown.closest('.selectbox'); $block = $selectbox.closest('.block'); - $form = $dropdown.closest('form'); + $form = $dropdown.closest('.js-issuable-update'); $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span'); $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip'); $value = $block.find('.value'); @@ -334,7 +334,7 @@ page = $('body').data('page'); isIssueIndex = page === 'projects:issues:index'; isMRIndex = page === 'projects:merge_requests:index'; - if (page === 'projects:boards:show') { + if (page === 'projects:boards:show' && !$dropdown.hasClass('js-issue-boards-label')) { if (label.isAny) { gl.issueBoards.BoardsStore.state.filters['label_name'] = []; } @@ -362,6 +362,30 @@ else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); } + else if ($dropdown.hasClass('js-issue-boards-label')) { + if ($el.hasClass('is-active')) { + gl.issueBoards.BoardsStore.detail.issue.labels.push(new ListLabel({ + id: label.id, + title: label.title, + color: label.color[0], + textColor: '#fff' + })); + } + else { + var labels = gl.issueBoards.BoardsStore.detail.issue.labels; + labels = labels.filter(function (selectedLabel) { + return selectedLabel.id !== label.id; + }); + gl.issueBoards.BoardsStore.detail.issue.labels = labels; + } + + $loading.fadeIn(); + + gl.issueBoards.BoardsStore.detail.issue.update(issueUpdateURL) + .then(function () { + $loading.fadeOut(); + }); + } else { if ($dropdown.hasClass('js-multiselect')) { diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 26cc6eb0e96..3004cfee40c 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -110,7 +110,7 @@ e.preventDefault(); return; } - if (page === 'projects:boards:show') { + if (page === 'projects:boards:show' && !$dropdown.hasClass('js-issue-board-sidebar')) { gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = selected.name; gl.issueBoards.BoardsStore.updateFiltersUrl(); e.preventDefault(); @@ -123,6 +123,24 @@ return Issuable.filterResults($dropdown.closest('form')); } else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); + } else if ($dropdown.hasClass('js-issue-board-sidebar')) { + if (selected.id !== -1) { + Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'milestone', new ListMilestone({ + id: selected.id, + title: selected.name + })); + } else { + Vue.delete(gl.issueBoards.BoardsStore.detail.issue, 'milestone'); + } + + $dropdown.trigger('loading.gl.dropdown'); + $loading.fadeIn(); + + gl.issueBoards.BoardsStore.detail.issue.update(issueUpdateURL) + .then(function () { + $dropdown.trigger('loaded.gl.dropdown'); + $loading.fadeOut(); + }); } else { selected = $selectbox.find('input[type="hidden"]').val(); data = {}; diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js index 5e3c5983d75..bfef9532d2b 100644 --- a/app/assets/javascripts/subscription.js +++ b/app/assets/javascripts/subscription.js @@ -22,13 +22,18 @@ return function() { var status; btn.removeClass('disabled'); - status = current_status === 'subscribed' ? 'unsubscribed' : 'subscribed'; - _this.subscription_status.attr('data-status', status); - action = status === 'subscribed' ? 'Unsubscribe' : 'Subscribe'; - btn.find('span').text(action); - _this.subscription_status.find('>div').toggleClass('hidden'); - if (btn.attr('data-original-title')) { - return btn.tooltip('hide').attr('data-original-title', action).tooltip('fixTitle'); + + if ($('body').data('page') === 'projects:boards:show') { + Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'subscribed', !gl.issueBoards.BoardsStore.detail.issue.subscribed); + } else { + status = current_status === 'subscribed' ? 'unsubscribed' : 'subscribed'; + _this.subscription_status.attr('data-status', status); + action = status === 'subscribed' ? 'Unsubscribe' : 'Subscribe'; + btn.find('span').text(action); + _this.subscription_status.find('>div').toggleClass('hidden'); + if (btn.attr('data-original-title')) { + return btn.tooltip('hide').attr('data-original-title', action).tooltip('fixTitle'); + } } }; })(this)); diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index bcabda3ceb2..d3868f392ea 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -9,7 +9,11 @@ this.usersPath = "/autocomplete/users.json"; this.userPath = "/autocomplete/users/:id.json"; if (currentUser != null) { - this.currentUser = JSON.parse(currentUser); + if (typeof currentUser === 'object') { + this.currentUser = currentUser; + } else { + this.currentUser = JSON.parse(currentUser); + } } $('.js-user-search').each((function(_this) { return function(i, dropdown) { @@ -32,9 +36,30 @@ $value = $block.find('.value'); $collapsedSidebar = $block.find('.sidebar-collapsed-user'); $loading = $block.find('.block-loading').fadeOut(); + + var updateIssueBoardsIssue = function () { + $loading.fadeIn(); + gl.issueBoards.BoardsStore.detail.issue.update(issueURL) + .then(function () { + $loading.fadeOut(); + }); + }; + $block.on('click', '.js-assign-yourself', function(e) { e.preventDefault(); - return assignTo(_this.currentUser.id); + + if ($dropdown.hasClass('js-issue-board-assignee')) { + Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'assignee', new ListUser({ + id: _this.currentUser.id, + username: _this.currentUser.username, + name: _this.currentUser.name, + avatar_url: _this.currentUser.avatar_url + })); + + updateIssueBoardsIssue(); + } else { + return assignTo(_this.currentUser.id); + } }); assignTo = function(selected) { var data; @@ -160,7 +185,7 @@ selectedId = user.id; return; } - if (page === 'projects:boards:show') { + if (page === 'projects:boards:show' && !$dropdown.hasClass('js-issue-board-assignee')) { selectedId = user.id; gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = user.id; gl.issueBoards.BoardsStore.updateFiltersUrl(); @@ -170,6 +195,19 @@ return Issuable.filterResults($dropdown.closest('form')); } else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); + } else if ($dropdown.hasClass('js-issue-board-assignee')) { + if (user.id) { + Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'assignee', new ListUser({ + id: user.id, + username: user.username, + name: user.name, + avatar_url: user.avatar_url + })); + } else { + Vue.delete(gl.issueBoards.BoardsStore.detail.issue, 'assignee'); + } + + updateIssueBoardsIssue(); } else { selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val(); return assignTo(selected); diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 6e81c12aa55..6eb2cb93baa 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -46,6 +46,15 @@ lex .page-with-sidebar { padding-bottom: 0; } + + .issues-filters { + position: relative; + z-index: 999999; + } +} + +.boards-app { + position: relative; } .boards-app-loading { @@ -265,3 +274,9 @@ lex border-width: 1px 0 1px 1px; } } + +.right-sidebar.issue-boards-sidebar { + position: absolute; + top: 0; + bottom: 0; +} diff --git a/app/views/projects/boards/components/_sidebar.html.haml b/app/views/projects/boards/components/_sidebar.html.haml index b03b8384220..b4a757a40a6 100644 --- a/app/views/projects/boards/components/_sidebar.html.haml +++ b/app/views/projects/boards/components/_sidebar.html.haml @@ -1,5 +1,6 @@ -%board-sidebar{ "inline-template" => true } - %aside.right-sidebar.right-sidebar-expanded{ "v-if" => "showSidebar" } +%board-sidebar{ "inline-template" => true, + ":current-user" => "#{current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) if current_user}" } + %aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-if" => "showSidebar" } .issuable-sidebar .block.issuable-sidebar-header %span.issuable-header-text.hide-collapsed.pull-left @@ -14,8 +15,9 @@ "@click" => "closeSidebar", aria: { label: "Toggle sidebar" } } = icon("times") - = render "projects/boards/components/sidebar/assignee" - = render "projects/boards/components/sidebar/milestone" - = render "projects/boards/components/sidebar/due_date" - = render "projects/boards/components/sidebar/labels" - = render "projects/boards/components/sidebar/notifications" + .js-issuable-update + = render "projects/boards/components/sidebar/assignee" + = render "projects/boards/components/sidebar/milestone" + = render "projects/boards/components/sidebar/due_date" + = render "projects/boards/components/sidebar/labels" + = render "projects/boards/components/sidebar/notifications" diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml index 93780fc929e..35343e65097 100644 --- a/app/views/projects/boards/components/sidebar/_assignee.html.haml +++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml @@ -20,3 +20,21 @@ %span.username = precede "@" do {{ issue.assignee.username }} + - if can?(current_user, :admin_issue, @project) + .selectbox.hide-collapsed + %input{ type: "hidden", + name: "issue[assignee_id]", + id: "issue_assignee_id", + ":value" => "issue.assignee.id", + "v-if" => "issue.assignee" } + .dropdown + %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-assignee{ data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, field_name: "issue[assignee_id]", null_user: "true" }, + ":data-issuable-id" => "issue.id", + ":data-issue-update" => "'/root/issue-boards/issues/' + issue.id + '.json'" } + Select assignee + = icon("chevron-down") + .dropdown-menu.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author + = dropdown_title("Assign to") + = dropdown_filter("Search users") + = dropdown_content + = dropdown_loading diff --git a/app/views/projects/boards/components/sidebar/_due_date.html.haml b/app/views/projects/boards/components/sidebar/_due_date.html.haml index 9861523824a..c9fb1378274 100644 --- a/app/views/projects/boards/components/sidebar/_due_date.html.haml +++ b/app/views/projects/boards/components/sidebar/_due_date.html.haml @@ -1,16 +1,31 @@ .block.due_date - .title.hide-collapsed + .title Due date = icon("spinner spin", class: "block-loading") - if can?(current_user, :admin_issue, @project) = link_to "Edit", "#", class: "edit-link pull-right" - .value.hide-collapsed + .value .value-content %span.no-value{ "v-if" => "!issue.dueDate" } No due date %span.bold{ "v-if" => "issue.dueDate" } - {{ issue.dueDate }} + {{ issue.dueDate | due-date }} %span.no-value.js-remove-due-date-holder{ "v-if" => "issue.dueDate" } \- %a.js-remove-due-date{ href: "#", role: "button" } remove due date + - if can?(current_user, :admin_issue, @project) + .selectbox + %input{ type: "hidden", + name: "issue[due_date]", + ":value" => "issue.dueDate" } + .dropdown + %button.dropdown-menu-toggle.js-due-date-select.js-issue-boards-due-date{ type: 'button', + data: { toggle: 'dropdown', field_name: "issue[due_date]", ability_name: "issue" }, + ":data-issue-update" => "'/root/issue-boards/issues/' + issue.id + '.json'" } + %span.dropdown-toggle-text Due date + = icon('chevron-down') + .dropdown-menu.dropdown-menu-due-date + = dropdown_title('Due date') + = dropdown_content do + .js-due-date-calendar diff --git a/app/views/projects/boards/components/sidebar/_labels.html.haml b/app/views/projects/boards/components/sidebar/_labels.html.haml index b135b134a96..71dd93db7d5 100644 --- a/app/views/projects/boards/components/sidebar/_labels.html.haml +++ b/app/views/projects/boards/components/sidebar/_labels.html.haml @@ -1,18 +1,30 @@ .block.labels - .title.hide-collapsed + .title Labels = icon("spinner spin", class: "block-loading") - if can?(current_user, :admin_issue, @project) = link_to "Edit", "#", class: "edit-link pull-right" - .value.issuable-show-labels.hide-collapsed + .value.issuable-show-labels %span.no-value{ "v-if" => "issue.labels.length === 0" } None %a{ href: "#", "v-for" => "label in issue.labels" } %span.label.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" } {{ label.title }} - .selectbox.hide-collapsed - %input{ type: "hidden", - name: "issue[label_names][]", - "v-for" => "label in issue.labels", - ":value" => "label.id" } + - if can?(current_user, :admin_issue, @project) + .selectbox + %input{ type: "hidden", + name: "issue[label_names][]", + "v-for" => "label in issue.labels", + ":value" => "label.id" } + .dropdown + %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-boards-label{ type: "button", + data: { toggle: "dropdown", field_name: "issue[label_names][]", show_no: "true", show_any: "true", project_id: @project.id, labels: namespace_project_labels_path(@project.namespace, @project, :json) }, + ":data-issue-update" => "'/root/issue-boards/issues/' + issue.id + '.json'" } + %span.dropdown-toggle-text + Label + = icon('chevron-down') + .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable + = render partial: "shared/issuable/label_page_default" + - if can? current_user, :admin_label, @project and @project + = render partial: "shared/issuable/label_page_create" diff --git a/app/views/projects/boards/components/sidebar/_milestone.html.haml b/app/views/projects/boards/components/sidebar/_milestone.html.haml index 14f521f57a4..fb88700cb3a 100644 --- a/app/views/projects/boards/components/sidebar/_milestone.html.haml +++ b/app/views/projects/boards/components/sidebar/_milestone.html.haml @@ -1,5 +1,5 @@ .block.milestone - .title.hide-collapsed + .title Milestone = icon("spinner spin", class: "block-loading") - if can?(current_user, :admin_issue, @project) @@ -9,8 +9,20 @@ None %span.bold.has-tooltip{ "v-if" => "issue.milestone" } {{ issue.milestone.title }} - .selectbox - .dropdown - %button.dropdown-menu-toggle - Milestone - -# = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: true }}) + - if can?(current_user, :admin_issue, @project) + .selectbox + %input{ type: "hidden", + ":value" => "issue.milestone.id", + name: "issue[milestone_id]", + "v-if" => "issue.milestone" } + .dropdown + %button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true" }, + ":data-issuable-id" => "issue.id", + ":data-issue-update" => "'/root/issue-boards/issues/' + issue.id + '.json'" } + Milestone + = icon("chevron-down") + .dropdown-menu.dropdown-select.dropdown-menu-selectable + = dropdown_title("Assignee milestone") + = dropdown_filter("Search milestones") + = dropdown_content + = dropdown_loading diff --git a/app/views/projects/boards/components/sidebar/_notifications.html.haml b/app/views/projects/boards/components/sidebar/_notifications.html.haml index e28be47fd8e..21c9563e9db 100644 --- a/app/views/projects/boards/components/sidebar/_notifications.html.haml +++ b/app/views/projects/boards/components/sidebar/_notifications.html.haml @@ -1,11 +1,11 @@ - if current_user - .block.light.subscription{ data: { url: '' } } - .title.hide-collapsed + .block.light.subscription{ ":data-url" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '/toggle_subscription'" } + .title Notifications %button.btn.btn-block.btn-default.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" } - Unsubscribe - .subscription-status.hide-collapsed{ data: { status: '' } } - .unsubscribed{class: ( 'hidden' if true )} + {{ issue.subscribed ? 'Unsubscribe' : 'Subscribe' }} + .subscription-status{ ":data-status" => "issue.subscribed ? 'subscribed' : 'unsubscribed'" } + .unsubscribed{ "v-show" => "!issue.subscribed" } You're not receiving notifications from this thread. - .subscribed{class: ( 'hidden' unless true )} + .subscribed{ "v-show" => "issue.subscribed" } You're receiving notifications because you're subscribed to this thread. diff --git a/app/views/projects/boards/show.html.haml b/app/views/projects/boards/show.html.haml index 10b495363a6..432390eb03f 100644 --- a/app/views/projects/boards/show.html.haml +++ b/app/views/projects/boards/show.html.haml @@ -10,7 +10,7 @@ = render 'shared/issuable/filter', type: :boards -#board-app{ "v-cloak" => true, +#board-app.boards-app{ "v-cloak" => true, "data-endpoint" => "#{namespace_project_board_path(@project.namespace, @project)}", "data-disabled" => "#{!can?(current_user, :admin_list, @project)}", "data-issue-link-base" => "#{namespace_project_issues_path(@project.namespace, @project)}" } -- cgit v1.2.1 From 05e8404b700decf7ebff2e5a4e3c9cec6cc609f4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 5 Oct 2016 16:17:03 +0100 Subject: Fixed issue with dragging opening the issue sidebar Added indicator when issue detail is visible --- .../boards/components/board_card.js.es6 | 28 +++++++++++++++++++++- app/assets/stylesheets/pages/boards.scss | 4 ++++ .../projects/boards/components/_card.html.haml | 6 +++-- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 index e61943de5a9..17bcbc1d54f 100644 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ b/app/assets/javascripts/boards/components/board_card.js.es6 @@ -12,6 +12,21 @@ disabled: Boolean, index: Number }, + data () { + return { + showDetail: false, + detailIssue: Store.detail + }; + }, + computed: { + issueDetailVisible () { + if (this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id) { + return true; + } else { + return false; + } + } + }, methods: { filterByLabel (label, e) { let labelToggleText = label.title; @@ -38,8 +53,19 @@ Store.updateFiltersUrl(); }, + mouseDown () { + this.showDetail = true; + }, + mouseMove () { + if (this.showDetail) { + this.showDetail = false; + } + }, showIssue () { - Store.detail.issue = this.issue; + if (this.showDetail) { + this.showDetail = false; + Vue.set(Store.detail, 'issue', this.issue); + } } } }); diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 6eb2cb93baa..f57e9a37bc3 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -194,6 +194,10 @@ lex margin-bottom: 5px; } + &.is-active { + background-color: $row-hover; + } + .label { border: 0; outline: 0; diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml index 71f64d7827c..d466165816f 100644 --- a/app/views/projects/boards/components/_card.html.haml +++ b/app/views/projects/boards/components/_card.html.haml @@ -7,9 +7,11 @@ ":issue-link-base" => "issueLinkBase", ":disabled" => "disabled", "track-by" => "id" } - %li.card{ ":class" => "{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id }", + %li.card{ ":class" => "{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id, 'is-active': issueDetailVisible }", ":index" => "index", - "@click" => "showIssue" } + "@mousedown" => "mouseDown", + "@mouseMove" => "mouseMove", + "@mouseup" => "showIssue" } %h4.card-title = icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential") %a{ ":href" => "issueLinkBase + '/' + issue.id", -- cgit v1.2.1 From 5f84c4e240a87b8e40a3391501d93022c3258b75 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 5 Oct 2016 16:22:36 +0100 Subject: Hides sidebar when clicking same issue --- app/assets/javascripts/boards/components/board_card.js.es6 | 7 ++++++- .../javascripts/boards/mixins/sortable_default_options.js.es6 | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 index 17bcbc1d54f..27c78e7f02e 100644 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ b/app/assets/javascripts/boards/components/board_card.js.es6 @@ -64,7 +64,12 @@ showIssue () { if (this.showDetail) { this.showDetail = false; - Vue.set(Store.detail, 'issue', this.issue); + + if (Store.detail.issue && Store.detail.issue.id === this.issue.id) { + Store.detail.issue = {}; + } else { + Store.detail.issue = this.issue; + } } } } diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 index f629d45c587..bd9ba7d5118 100644 --- a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 +++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 @@ -22,7 +22,7 @@ fallbackOnBody: true, ghostClass: 'is-ghost', filter: '.has-tooltip, .btn', - delay: gl.issueBoards.touchEnabled ? 100 : 0, + delay: gl.issueBoards.touchEnabled ? 100 : 50, scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100, scrollSpeed: 20, onStart: gl.issueBoards.onStart, -- cgit v1.2.1 From 81e7490d3af1418d63276527be5be7149ca60ba7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 5 Oct 2016 16:23:45 +0100 Subject: Correct the issue update path --- app/views/projects/boards/components/sidebar/_assignee.html.haml | 2 +- app/views/projects/boards/components/sidebar/_due_date.html.haml | 2 +- app/views/projects/boards/components/sidebar/_labels.html.haml | 2 +- app/views/projects/boards/components/sidebar/_milestone.html.haml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml index 35343e65097..57039237d0b 100644 --- a/app/views/projects/boards/components/sidebar/_assignee.html.haml +++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml @@ -30,7 +30,7 @@ .dropdown %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-assignee{ data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, field_name: "issue[assignee_id]", null_user: "true" }, ":data-issuable-id" => "issue.id", - ":data-issue-update" => "'/root/issue-boards/issues/' + issue.id + '.json'" } + ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } Select assignee = icon("chevron-down") .dropdown-menu.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author diff --git a/app/views/projects/boards/components/sidebar/_due_date.html.haml b/app/views/projects/boards/components/sidebar/_due_date.html.haml index c9fb1378274..91b4a572ee4 100644 --- a/app/views/projects/boards/components/sidebar/_due_date.html.haml +++ b/app/views/projects/boards/components/sidebar/_due_date.html.haml @@ -22,7 +22,7 @@ .dropdown %button.dropdown-menu-toggle.js-due-date-select.js-issue-boards-due-date{ type: 'button', data: { toggle: 'dropdown', field_name: "issue[due_date]", ability_name: "issue" }, - ":data-issue-update" => "'/root/issue-boards/issues/' + issue.id + '.json'" } + ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } %span.dropdown-toggle-text Due date = icon('chevron-down') .dropdown-menu.dropdown-menu-due-date diff --git a/app/views/projects/boards/components/sidebar/_labels.html.haml b/app/views/projects/boards/components/sidebar/_labels.html.haml index 71dd93db7d5..865bfa6d1da 100644 --- a/app/views/projects/boards/components/sidebar/_labels.html.haml +++ b/app/views/projects/boards/components/sidebar/_labels.html.haml @@ -20,7 +20,7 @@ .dropdown %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-boards-label{ type: "button", data: { toggle: "dropdown", field_name: "issue[label_names][]", show_no: "true", show_any: "true", project_id: @project.id, labels: namespace_project_labels_path(@project.namespace, @project, :json) }, - ":data-issue-update" => "'/root/issue-boards/issues/' + issue.id + '.json'" } + ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } %span.dropdown-toggle-text Label = icon('chevron-down') diff --git a/app/views/projects/boards/components/sidebar/_milestone.html.haml b/app/views/projects/boards/components/sidebar/_milestone.html.haml index fb88700cb3a..ec1354509f2 100644 --- a/app/views/projects/boards/components/sidebar/_milestone.html.haml +++ b/app/views/projects/boards/components/sidebar/_milestone.html.haml @@ -18,7 +18,7 @@ .dropdown %button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true" }, ":data-issuable-id" => "issue.id", - ":data-issue-update" => "'/root/issue-boards/issues/' + issue.id + '.json'" } + ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } Milestone = icon("chevron-down") .dropdown-menu.dropdown-select.dropdown-menu-selectable -- cgit v1.2.1 From 45fd2d38844afafd4ec144eb4dc388b7a76b04ad Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 5 Oct 2016 16:59:47 +0100 Subject: Styling updates to sidebar to match design --- app/assets/stylesheets/pages/boards.scss | 38 +++++++++++++++++++--- .../projects/boards/components/_sidebar.html.haml | 2 +- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index f57e9a37bc3..0807d583207 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -279,8 +279,38 @@ lex } } -.right-sidebar.issue-boards-sidebar { - position: absolute; - top: 0; - bottom: 0; +.issue-boards-sidebar { + &.right-sidebar { + position: absolute; + top: 0; + bottom: 0; + } + + .issuable-sidebar-header { + position: relative; + } + + .gutter-toggle { + position: absolute; + top: 0; + bottom: 10px; + right: 0; + width: 22px; + color: $gray-darkest; + + .fa { + position: absolute; + top: 50%; + margin-top: (-15px / 2); + } + } + + .issuable-header-text { + width: 100%; + padding-right: 35px; + + > strong { + font-weight: 600; + } + } } diff --git a/app/views/projects/boards/components/_sidebar.html.haml b/app/views/projects/boards/components/_sidebar.html.haml index b4a757a40a6..cf70ad4927a 100644 --- a/app/views/projects/boards/components/_sidebar.html.haml +++ b/app/views/projects/boards/components/_sidebar.html.haml @@ -13,7 +13,7 @@ %a.gutter-toggle.pull-right{ role: "button", href: "#", "@click" => "closeSidebar", - aria: { label: "Toggle sidebar" } } + "aria-label" => "Toggle sidebar" } = icon("times") .js-issuable-update = render "projects/boards/components/sidebar/assignee" -- cgit v1.2.1 From e6fa8a3d10fe34e9ee17f3122139f0b8b1b0a3f9 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 6 Oct 2016 09:03:10 +0100 Subject: Fixed sidebar dropdowns to work with Vue --- app/assets/javascripts/boards/models/issue.js.es6 | 4 ++++ app/assets/javascripts/gl_dropdown.js | 11 +++++++++++ app/assets/javascripts/labels_select.js | 5 +++-- app/assets/javascripts/milestone_select.js | 1 + app/assets/javascripts/users_select.js | 5 +++-- .../projects/boards/components/sidebar/_assignee.html.haml | 2 +- .../projects/boards/components/sidebar/_labels.html.haml | 2 +- 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6 index fac753dadb5..ca013aa92b5 100644 --- a/app/assets/javascripts/boards/models/issue.js.es6 +++ b/app/assets/javascripts/boards/models/issue.js.es6 @@ -58,6 +58,10 @@ class ListIssue { } }; + if (!data.issue.label_ids.length) { + data.issue.label_ids = ['']; + } + return Vue.http.patch(url, data); } } diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index d4403375643..834eaef6fff 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -620,6 +620,17 @@ selectedObject = this.renderedData[selectedIndex]; } } + + if (this.options.vue) { + if (el.hasClass(ACTIVE_CLASS)) { + el.removeClass(ACTIVE_CLASS); + } else { + el.addClass(ACTIVE_CLASS); + } + + return selectedObject; + } + field = []; value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id; if (isInput) { diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 6e5d3dff9eb..03e6136509a 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -317,6 +317,7 @@ } }, multiSelect: $dropdown.hasClass('js-multiselect'), + vue: $dropdown.hasClass('js-issue-board-sidebar'), clicked: function(label, $el, e) { var isIssueIndex, isMRIndex, page; _this.enableBulkLabelDropdown(); @@ -334,7 +335,7 @@ page = $('body').data('page'); isIssueIndex = page === 'projects:issues:index'; isMRIndex = page === 'projects:merge_requests:index'; - if (page === 'projects:boards:show' && !$dropdown.hasClass('js-issue-boards-label')) { + if (page === 'projects:boards:show' && !$dropdown.hasClass('js-issue-board-sidebar')) { if (label.isAny) { gl.issueBoards.BoardsStore.state.filters['label_name'] = []; } @@ -362,7 +363,7 @@ else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); } - else if ($dropdown.hasClass('js-issue-boards-label')) { + else if ($dropdown.hasClass('js-issue-board-sidebar')) { if ($el.hasClass('is-active')) { gl.issueBoards.BoardsStore.detail.issue.labels.push(new ListLabel({ id: label.id, diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 3004cfee40c..8dffadd88e2 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -101,6 +101,7 @@ // display:block overrides the hide-collapse rule return $value.css('display', ''); }, + vue: $dropdown.hasClass('js-issue-board-sidebar'), clicked: function(selected, $el, e) { var data, isIssueIndex, isMRIndex, page; page = $('body').data('page'); diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index d3868f392ea..67a71f6c381 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -175,6 +175,7 @@ // display:block overrides the hide-collapse rule return $value.css('display', ''); }, + vue: $dropdown.hasClass('js-issue-board-sidebar'), clicked: function(user, $el, e) { var isIssueIndex, isMRIndex, page, selected; page = $('body').data('page'); @@ -185,7 +186,7 @@ selectedId = user.id; return; } - if (page === 'projects:boards:show' && !$dropdown.hasClass('js-issue-board-assignee')) { + if (page === 'projects:boards:show' && !$dropdown.hasClass('js-issue-board-sidebar')) { selectedId = user.id; gl.issueBoards.BoardsStore.state.filters[$dropdown.data('field-name')] = user.id; gl.issueBoards.BoardsStore.updateFiltersUrl(); @@ -195,7 +196,7 @@ return Issuable.filterResults($dropdown.closest('form')); } else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); - } else if ($dropdown.hasClass('js-issue-board-assignee')) { + } else if ($dropdown.hasClass('js-issue-board-sidebar')) { if (user.id) { Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'assignee', new ListUser({ id: user.id, diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml index 57039237d0b..92f2a931668 100644 --- a/app/views/projects/boards/components/sidebar/_assignee.html.haml +++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml @@ -28,7 +28,7 @@ ":value" => "issue.assignee.id", "v-if" => "issue.assignee" } .dropdown - %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-assignee{ data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, field_name: "issue[assignee_id]", null_user: "true" }, + %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-sidebar{ data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, field_name: "issue[assignee_id]", null_user: "true" }, ":data-issuable-id" => "issue.id", ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } Select assignee diff --git a/app/views/projects/boards/components/sidebar/_labels.html.haml b/app/views/projects/boards/components/sidebar/_labels.html.haml index 865bfa6d1da..0e2ea74ec41 100644 --- a/app/views/projects/boards/components/sidebar/_labels.html.haml +++ b/app/views/projects/boards/components/sidebar/_labels.html.haml @@ -18,7 +18,7 @@ "v-for" => "label in issue.labels", ":value" => "label.id" } .dropdown - %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-boards-label{ type: "button", + %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", field_name: "issue[label_names][]", show_no: "true", show_any: "true", project_id: @project.id, labels: namespace_project_labels_path(@project.namespace, @project, :json) }, ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } %span.dropdown-toggle-text -- cgit v1.2.1 From 18607d6c88e820b927d90ca1d247a23a61be8c99 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 6 Oct 2016 14:36:44 +0100 Subject: Added tests --- .../boards/filters/due_date_filters.js.es6 | 2 +- app/assets/javascripts/users_select.js | 2 +- .../boards/components/sidebar/_assignee.html.haml | 2 +- spec/features/boards/sidebar_spec.rb | 297 +++++++++++++++++++++ 4 files changed, 300 insertions(+), 3 deletions(-) create mode 100644 spec/features/boards/sidebar_spec.rb diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 b/app/assets/javascripts/boards/filters/due_date_filters.js.es6 index 5c1519986c1..50ef1911022 100644 --- a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 +++ b/app/assets/javascripts/boards/filters/due_date_filters.js.es6 @@ -1,4 +1,4 @@ Vue.filter('due-date', (value) => { - const date = new Date(value.replace(new RegExp('-', 'g'), ',')); + const date = new Date(value); return $.datepicker.formatDate('M d, yy', date); }); diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 67a71f6c381..82c75c614b1 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -48,7 +48,7 @@ $block.on('click', '.js-assign-yourself', function(e) { e.preventDefault(); - if ($dropdown.hasClass('js-issue-board-assignee')) { + if ($dropdown.hasClass('js-issue-board-sidebar')) { Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'assignee', new ListUser({ id: _this.currentUser.id, username: _this.currentUser.username, diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml index 92f2a931668..4307e8e7626 100644 --- a/app/views/projects/boards/components/sidebar/_assignee.html.haml +++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml @@ -28,7 +28,7 @@ ":value" => "issue.assignee.id", "v-if" => "issue.assignee" } .dropdown - %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-sidebar{ data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, field_name: "issue[assignee_id]", null_user: "true" }, + %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-sidebar{ data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, null_user: "true" }, ":data-issuable-id" => "issue.id", ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } Select assignee diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb new file mode 100644 index 00000000000..2e754287f3a --- /dev/null +++ b/spec/features/boards/sidebar_spec.rb @@ -0,0 +1,297 @@ +require 'rails_helper' + +describe 'Issue Boards', feature: true, js: true do + include WaitForAjax + include WaitForVueResource + + let(:project) { create(:project_with_board, :public) } + let(:user) { create(:user) } + let!(:label) { create(:label, project: project) } + let!(:label2) { create(:label, project: project) } + let!(:milestone) { create(:milestone, project: project) } + let!(:issue2) { create(:labeled_issue, project: project, assignee: user, milestone: milestone, labels: [label]) } + let!(:issue) { create(:issue, project: project) } + + before do + project.team << [user, :master] + + login_as(user) + + visit namespace_project_board_path(project.namespace, project) + wait_for_vue_resource + end + + it 'shows sidebar when clicking issue' do + page.within(first('.board')) do + first('.card').click + end + + expect(page).to have_selector('.issue-boards-sidebar') + end + + it 'closes sidebar when clicking issue' do + page.within(first('.board')) do + first('.card').click + end + + expect(page).to have_selector('.issue-boards-sidebar') + + page.within(first('.board')) do + first('.card').click + end + + expect(page).not_to have_selector('.issue-boards-sidebar') + end + + it 'closes sidebar when clicking close button' do + page.within(first('.board')) do + first('.card').click + end + + expect(page).to have_selector('.issue-boards-sidebar') + + find('.gutter-toggle').click + + expect(page).not_to have_selector('.issue-boards-sidebar') + end + + it 'shows issue details when sidebar is open' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.issue-boards-sidebar') do + expect(page).to have_content(issue.title) + expect(page).to have_content(issue.to_reference) + end + end + + context 'assignee' do + it 'updates the issues assignee' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.assignee') do + click_link 'Edit' + + wait_for_ajax + + page.within('.dropdown-menu-user') do + click_link user.name + + wait_for_vue_resource + end + + expect(page).to have_content(user.name) + end + + page.within(first('.board')) do + page.within(first('.card')) do + expect(page).to have_selector('.avatar') + end + end + end + + it 'removes the assignee' do + page.within(first('.board')) do + find('.card:nth-child(2)').click + end + + page.within('.assignee') do + click_link 'Edit' + + wait_for_ajax + + page.within('.dropdown-menu-user') do + click_link 'Unassigned' + + wait_for_vue_resource + end + + expect(page).to have_content('No assignee') + end + + page.within(first('.board')) do + page.within(find('.card:nth-child(2)')) do + expect(page).not_to have_selector('.avatar') + end + end + end + + it 'assignees to current user' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.assignee') do + click_link 'assign yourself' + + wait_for_vue_resource + + expect(page).to have_content(user.name) + end + + page.within(first('.board')) do + page.within(first('.card')) do + expect(page).to have_selector('.avatar') + end + end + end + end + + context 'milestone' do + it 'adds a milestone' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.milestone') do + click_link 'Edit' + + wait_for_ajax + + click_link milestone.title + + wait_for_vue_resource + + page.within('.value') do + expect(page).to have_content(milestone.title) + end + end + end + + it 'removes a milestone' do + page.within(first('.board')) do + find('.card:nth-child(2)').click + end + + page.within('.milestone') do + click_link 'Edit' + + wait_for_ajax + + click_link "No Milestone" + + wait_for_vue_resource + + page.within('.value') do + expect(page).not_to have_content(milestone.title) + end + end + end + end + + context 'due date' do + it 'updates due date' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.due_date') do + click_link 'Edit' + + click_link Date.today.day + + wait_for_vue_resource + + expect(page).to have_content(Date.today.to_s(:medium)) + end + end + end + + context 'labels' do + it 'adds a single label' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.labels') do + click_link 'Edit' + + wait_for_ajax + + click_link label.title + + wait_for_vue_resource + + find('.dropdown-menu-close-icon').click + + page.within('.value') do + expect(page).to have_selector('.label', count: 1) + expect(page).to have_content(label.title) + end + end + + page.within(first('.board')) do + page.within(first('.card')) do + expect(page).to have_selector('.label', count: 1) + expect(page).to have_content(label.title) + end + end + end + + it 'adds a multiple labels' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.labels') do + click_link 'Edit' + + wait_for_ajax + + click_link label.title + click_link label2.title + + wait_for_vue_resource + + find('.dropdown-menu-close-icon').click + + page.within('.value') do + expect(page).to have_selector('.label', count: 2) + expect(page).to have_content(label.title) + expect(page).to have_content(label2.title) + end + end + + page.within(first('.board')) do + page.within(first('.card')) do + expect(page).to have_selector('.label', count: 2) + expect(page).to have_content(label.title) + expect(page).to have_content(label2.title) + end + end + end + + it 'removes a label' do + page.within(first('.board')) do + find('.card:nth-child(2)').click + end + + page.within('.labels') do + click_link 'Edit' + + wait_for_ajax + + click_link label.title + + wait_for_vue_resource + + find('.dropdown-menu-close-icon').click + + page.within('.value') do + expect(page).to have_selector('.label', count: 0) + expect(page).not_to have_content(label.title) + end + end + + page.within(first('.board')) do + page.within(find('.card:nth-child(2)')) do + expect(page).not_to have_selector('.label', count: 1) + expect(page).not_to have_content(label.title) + end + end + end + end +end -- cgit v1.2.1 From 0c286d54737f685caf7ec21320d43d8069f2001d Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 6 Oct 2016 11:51:38 -0300 Subject: Fix JSON Schema that validates data returned by board issues endpoint --- spec/controllers/projects/boards/issues_controller_spec.rb | 2 +- spec/fixtures/api/schemas/issue.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/controllers/projects/boards/issues_controller_spec.rb b/spec/controllers/projects/boards/issues_controller_spec.rb index 566658b508d..0eebb5a4624 100644 --- a/spec/controllers/projects/boards/issues_controller_spec.rb +++ b/spec/controllers/projects/boards/issues_controller_spec.rb @@ -21,7 +21,7 @@ describe Projects::Boards::IssuesController do it 'returns issues that have the list label applied' do johndoe = create(:user, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) create(:labeled_issue, project: project, labels: [planning]) - create(:labeled_issue, project: project, labels: [development]) + create(:labeled_issue, project: project, labels: [development], due_date: Date.tomorrow) create(:labeled_issue, project: project, labels: [development], assignee: johndoe) list_issues user: user, list_id: list2 diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json index 532ebb9640e..6de6618e475 100644 --- a/spec/fixtures/api/schemas/issue.json +++ b/spec/fixtures/api/schemas/issue.json @@ -9,6 +9,7 @@ "iid": { "type": "integer" }, "title": { "type": "string" }, "confidential": { "type": "boolean" }, + "due_date": { "type": ["date", "null"] }, "labels": { "type": "array", "items": { -- cgit v1.2.1 From 1aff95c76844adb880e7f935deab8af5e797fb51 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 6 Oct 2016 16:14:12 +0100 Subject: Fix issue when clicking links inside issue showing sidebar --- app/assets/javascripts/boards/components/board_card.js.es6 | 6 +++++- app/views/projects/boards/components/_card.html.haml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 index 27c78e7f02e..bcb1b60b978 100644 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ b/app/assets/javascripts/boards/components/board_card.js.es6 @@ -61,7 +61,11 @@ this.showDetail = false; } }, - showIssue () { + showIssue (e) { + const targetTagName = e.target.tagName.toLowerCase(); + + if (targetTagName === 'a' || targetTagName === 'button') return; + if (this.showDetail) { this.showDetail = false; diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml index d466165816f..5cfd42485c5 100644 --- a/app/views/projects/boards/components/_card.html.haml +++ b/app/views/projects/boards/components/_card.html.haml @@ -11,7 +11,7 @@ ":index" => "index", "@mousedown" => "mouseDown", "@mouseMove" => "mouseMove", - "@mouseup" => "showIssue" } + "@mouseup" => "showIssue($event)" } %h4.card-title = icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential") %a{ ":href" => "issueLinkBase + '/' + issue.id", -- cgit v1.2.1 From 7d20a91b2ecf0af89b3a6d3a5d4d8621114687ec Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 7 Oct 2016 09:24:57 +0100 Subject: Restore subscribe status in JSON --- app/controllers/projects/boards/issues_controller.rb | 7 +++++-- app/models/issue.rb | 6 ++++++ spec/controllers/projects/boards/issues_controller_spec.rb | 2 ++ spec/fixtures/api/schemas/issue.json | 3 ++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/boards/issues_controller.rb b/app/controllers/projects/boards/issues_controller.rb index b5a56d11d32..fbb06c0ffba 100644 --- a/app/controllers/projects/boards/issues_controller.rb +++ b/app/controllers/projects/boards/issues_controller.rb @@ -76,8 +76,11 @@ module Projects only: [:iid, :title, :confidential, :due_date], include: { assignee: { only: [:id, :name, :username], methods: [:avatar_url] }, - labels: { only: [:id, :title, :description, :color, :priority], methods: [:text_color] } - }) + labels: { only: [:id, :title, :description, :color, :priority], methods: [:text_color] }, + milestone: { only: [:id, :title] } + }, + user: current_user + ) end end end diff --git a/app/models/issue.rb b/app/models/issue.rb index abd58e0454a..89794290520 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -274,4 +274,10 @@ class Issue < ActiveRecord::Base def check_for_spam? project.public? end + + def as_json(options = {}) + super(options).tap do |json| + json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user) + end + end end diff --git a/spec/controllers/projects/boards/issues_controller_spec.rb b/spec/controllers/projects/boards/issues_controller_spec.rb index 0eebb5a4624..75f6e7f54e2 100644 --- a/spec/controllers/projects/boards/issues_controller_spec.rb +++ b/spec/controllers/projects/boards/issues_controller_spec.rb @@ -20,9 +20,11 @@ describe Projects::Boards::IssuesController do context 'with valid list id' do it 'returns issues that have the list label applied' do johndoe = create(:user, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) + issue = create(:labeled_issue, project: project, labels: [planning]) create(:labeled_issue, project: project, labels: [planning]) create(:labeled_issue, project: project, labels: [development], due_date: Date.tomorrow) create(:labeled_issue, project: project, labels: [development], assignee: johndoe) + issue.subscribe(johndoe) list_issues user: user, list_id: list2 diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json index 6de6618e475..77f2bcee1f3 100644 --- a/spec/fixtures/api/schemas/issue.json +++ b/spec/fixtures/api/schemas/issue.json @@ -43,7 +43,8 @@ "name": { "type": "string" }, "username": { "type": "string" }, "avatar_url": { "type": "uri" } - } + }, + "subscribed": { "type": ["boolean", "null"] } }, "additionalProperties": false } -- cgit v1.2.1 From e4571614983ca6ba7ef7c4ea8c4bc5bbd992ce32 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 7 Oct 2016 09:38:35 +0100 Subject: Make the subscribe button work correctly --- app/assets/javascripts/boards/components/board_new_issue.js.es6 | 7 ++++++- app/assets/javascripts/boards/models/issue.js.es6 | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/boards/components/board_new_issue.js.es6 b/app/assets/javascripts/boards/components/board_new_issue.js.es6 index a4fad422eca..b31adfdb8da 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.js.es6 +++ b/app/assets/javascripts/boards/components/board_new_issue.js.es6 @@ -1,4 +1,6 @@ (() => { + const Store = gl.issueBoards.BoardsStore; + window.gl = window.gl || {}; gl.issueBoards.BoardNewIssue = Vue.extend({ @@ -27,13 +29,16 @@ const labels = this.list.label ? [this.list.label] : []; const issue = new ListIssue({ title: this.title, - labels + labels, + subscribed: true }); this.list.newIssue(issue) .then((data) => { // Need this because our jQuery very kindly disables buttons on ALL form submissions $(this.$els.submitButton).enable(); + + Store.detail.issue = issue; }) .catch(() => { // Need this because our jQuery very kindly disables buttons on ALL form submissions diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6 index ca013aa92b5..9704274b886 100644 --- a/app/assets/javascripts/boards/models/issue.js.es6 +++ b/app/assets/javascripts/boards/models/issue.js.es6 @@ -4,7 +4,7 @@ class ListIssue { this.title = obj.title; this.confidential = obj.confidential; this.dueDate = obj.due_date; - this.subscribed = true; + this.subscribed = obj.subscribed; this.labels = []; if (obj.assignee) { -- cgit v1.2.1 From b602023e95f130e4b24cf2be1817f4a22444bb5b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 7 Oct 2016 09:55:12 +0100 Subject: Added tests for showing sidebar when new issue is saved --- spec/features/boards/new_issue_spec.rb | 15 +++++++++++++++ spec/features/boards/sidebar_spec.rb | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index c046e6b8d79..c776c977416 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -65,6 +65,21 @@ describe 'Issue Boards new issue', feature: true, js: true do expect(page).to have_content('1') end end + + it 'shows sidebar when creating new issue' do + page.within(first('.board')) do + find('.board-issue-count-holder .btn').click + end + + page.within(first('.board-new-issue-form')) do + find('.form-control').set('bug') + click_button 'Submit issue' + end + + wait_for_vue_resource + + expect(page).to have_selector('.issue-boards-sidebar') + end end context 'unauthorized user' do diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 2e754287f3a..905d720705d 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -294,4 +294,18 @@ describe 'Issue Boards', feature: true, js: true do end end end + + context 'subscription' do + it 'changes issue subscription' do + page.within(first('.board')) do + first('.card').click + end + + page.within('.subscription') do + click_button 'Subscribe' + + expect(page).to have_content("You're receiving notifications because you're subscribed to this thread.") + end + end + end end -- cgit v1.2.1 From 306e55261e055081b08c7774eaa9e741c4f5515a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 7 Oct 2016 11:12:02 +0100 Subject: Fixed filter specs --- app/assets/javascripts/labels_select.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 03e6136509a..19eb0ab4294 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -22,7 +22,7 @@ abilityName = $dropdown.data('ability-name'); $selectbox = $dropdown.closest('.selectbox'); $block = $selectbox.closest('.block'); - $form = $dropdown.closest('.js-issuable-update'); + $form = $dropdown.closest('form, .js-issuable-update'); $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span'); $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip'); $value = $block.find('.value'); -- cgit v1.2.1 From e4176f4ec42fa8e391b088d62deef051cdafaf23 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 7 Oct 2016 11:20:22 +0100 Subject: Fixed some JS styling --- app/assets/javascripts/boards/components/board_sidebar.js.es6 | 2 +- app/assets/javascripts/boards/models/issue.js.es6 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6 index 12f69f93279..8d217f0f573 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js.es6 +++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6 @@ -34,7 +34,7 @@ new DueDateSelect(); new LabelsSelect(); new Sidebar(); - new Subscription('.subscription') + new Subscription('.subscription'); }); } else { $('.right-sidebar').getNiceScroll().remove(); diff --git a/app/assets/javascripts/boards/models/issue.js.es6 b/app/assets/javascripts/boards/models/issue.js.es6 index 9704274b886..03fd6c05d87 100644 --- a/app/assets/javascripts/boards/models/issue.js.es6 +++ b/app/assets/javascripts/boards/models/issue.js.es6 @@ -54,7 +54,7 @@ class ListIssue { milestone_id: this.milestone ? this.milestone.id : null, due_date: this.dueDate, assignee_id: this.assignee ? this.assignee.id : null, - label_ids: this.labels.map((label) => label.id ) + label_ids: this.labels.map( (label) => label.id ) } }; -- cgit v1.2.1 From 43438c3695ac06e0d12e24824f2c191f9608a51b Mon Sep 17 00:00:00 2001 From: Ismael Arenzana Date: Fri, 7 Oct 2016 17:29:48 +0000 Subject: Changed gitlab-shell version to avoid warning when precompiling the assets. --- doc/update/8.11-to-8.12.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md index 07743d050f7..cddfa7e3e01 100644 --- a/doc/update/8.11-to-8.12.md +++ b/doc/update/8.11-to-8.12.md @@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-12-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all --tags -sudo -u git -H git checkout v3.6.0 +sudo -u git -H git checkout v3.6.1 ``` ### 6. Update gitlab-workhorse -- cgit v1.2.1 From a04e9f9b61d93ca872fe108d83f519ba6151631e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 11 Oct 2016 03:32:52 +0000 Subject: Remove bad stuffs from auto-merging --- CHANGELOG | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index eb9905a6f52..da4e66762e4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -83,8 +83,6 @@ v 8.13.0 (unreleased) - Fix Pipeline list commit column width should be adjusted - Close todos when accepting merge requests via the API !6486 (tonygambone) - Changed Slack service user referencing from full name to username (Sebastian Poxhofer) - -v 8.12.4 (unreleased) - Retouch environments list and deployments list - Add Container Registry on/off status to Admin Area !6638 (the-undefined) - Grouped pipeline dropdown is a scrollable container -- cgit v1.2.1 From 9293c7d8d95cfb444303bb113b84853fcda46aad Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Wed, 12 Oct 2016 08:52:33 -0500 Subject: Fix encoding issues on pipeline commits --- app/views/projects/ci/pipelines/_pipeline.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 36eadbd2bf1..d53d2cee90c 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -32,7 +32,7 @@ %p.commit-title - if commit = pipeline.commit = author_avatar(commit, size: 20) - = link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message" + = link_to_gfm truncate(commit.title, length: 60, escape: false), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message" - else Cant find HEAD commit for this branch -- cgit v1.2.1 From 3ca064eeef874cbb258903abb8009e3ce257a4b4 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Thu, 13 Oct 2016 18:08:15 +0500 Subject: Add missing tests for download snippet ref: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6720 --- spec/controllers/snippets_controller_spec.rb | 156 ++++++++++++++------------- 1 file changed, 83 insertions(+), 73 deletions(-) diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index 41d263a46a4..2d762fdaa04 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -116,116 +116,126 @@ describe SnippetsController do end end - describe 'GET #raw' do - let(:user) { create(:user) } + %w(raw download).each do |action| + describe "GET #{action}" do + context 'when the personal snippet is private' do + let(:personal_snippet) { create(:personal_snippet, :private, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end - context 'when the personal snippet is private' do - let(:personal_snippet) { create(:personal_snippet, :private, author: user) } + context 'when signed in user is not the author' do + let(:other_author) { create(:author) } + let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } - context 'when signed in' do - before do - sign_in(user) - end + it 'responds with status 404' do + get action, id: other_personal_snippet.to_param - context 'when signed in user is not the author' do - let(:other_author) { create(:author) } - let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } + expect(response).to have_http_status(404) + end + end - it 'responds with status 404' do - get :raw, id: other_personal_snippet.to_param + context 'when signed in user is the author' do + before { get action, id: personal_snippet.to_param } - expect(response).to have_http_status(404) - end - end + it 'responds with status 200' do + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end - context 'when signed in user is the author' do - it 'renders the raw snippet' do - get :raw, id: personal_snippet.to_param + it 'has expected headers' do + expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) + if action == :download + expect(response.header['Content-Disposition']).to match(/attachment/) + elsif action == :raw + expect(response.header['Content-Disposition']).to match(/inline/) + end + end end end - end - context 'when not signed in' do - it 'redirects to the sign in page' do - get :raw, id: personal_snippet.to_param + context 'when not signed in' do + it 'redirects to the sign in page' do + get action, id: personal_snippet.to_param - expect(response).to redirect_to(new_user_session_path) + expect(response).to redirect_to(new_user_session_path) + end end end - end - context 'when the personal snippet is internal' do - let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } + context 'when the personal snippet is internal' do + let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } - context 'when signed in' do - before do - sign_in(user) - end + context 'when signed in' do + before do + sign_in(user) + end - it 'renders the raw snippet' do - get :raw, id: personal_snippet.to_param + it 'responds with status 200' do + get action, id: personal_snippet.to_param - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end end - end - context 'when not signed in' do - it 'redirects to the sign in page' do - get :raw, id: personal_snippet.to_param + context 'when not signed in' do + it 'redirects to the sign in page' do + get action, id: personal_snippet.to_param - expect(response).to redirect_to(new_user_session_path) + expect(response).to redirect_to(new_user_session_path) + end end end - end - context 'when the personal snippet is public' do - let(:personal_snippet) { create(:personal_snippet, :public, author: user) } + context 'when the personal snippet is public' do + let(:personal_snippet) { create(:personal_snippet, :public, author: user) } - context 'when signed in' do - before do - sign_in(user) - end + context 'when signed in' do + before do + sign_in(user) + end - it 'renders the raw snippet' do - get :raw, id: personal_snippet.to_param + it 'responds with status 200' do + get action, id: personal_snippet.to_param - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end end - end - context 'when not signed in' do - it 'renders the raw snippet' do - get :raw, id: personal_snippet.to_param + context 'when not signed in' do + it 'responds with status 200' do + get action, id: personal_snippet.to_param - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end end end - end - context 'when the personal snippet does not exist' do - context 'when signed in' do - before do - sign_in(user) - end + context 'when the personal snippet does not exist' do + context 'when signed in' do + before do + sign_in(user) + end - it 'responds with status 404' do - get :raw, id: 'doesntexist' + it 'responds with status 404' do + get action, id: 'doesntexist' - expect(response).to have_http_status(404) + expect(response).to have_http_status(404) + end end - end - context 'when not signed in' do - it 'responds with status 404' do - get :raw, id: 'doesntexist' + context 'when not signed in' do + it 'responds with status 404' do + get action, id: 'doesntexist' - expect(response).to have_http_status(404) + expect(response).to have_http_status(404) + end end end end -- cgit v1.2.1 From b2c771f45261e1484158d1304cd898e866002f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Oct 2016 18:44:52 +0200 Subject: Add an API styleguide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/README.md | 2 ++ doc/development/api_styleguide.md | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 doc/development/api_styleguide.md diff --git a/doc/development/README.md b/doc/development/README.md index 9706cb1de7f..630fe64cee6 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -8,6 +8,8 @@ ## Styleguides +- [API styleguide](api_styleguide.md) Use this styleguide if you are + contributing to the API. - [Documentation styleguide](doc_styleguide.md) Use this styleguide if you are contributing to documentation. - [SQL Migration Style Guide](migration_style_guide.md) for creating safe SQL migrations diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md new file mode 100644 index 00000000000..ee5e7ad3988 --- /dev/null +++ b/doc/development/api_styleguide.md @@ -0,0 +1,58 @@ +# API styleguide + +This styleguide recommends best practices for the API development. + +## Declared params + +> Grape allows you to access only the parameters that have been declared by your +`params` block. It filters out the params that have been passed, but are not +allowed. + +– https://github.com/ruby-grape/grape#declared + +### Exclude params from parent namespaces + +> By default `declared(params) `includes parameters that were defined in all +parent namespaces. + +– https://github.com/ruby-grape/grape#include-parent-namespaces + +In most cases you will want to exclude params from the parent namespaces: + +```ruby +declared(params, include_parent_namespaces: false) +``` + +### When to use `declared(params)`? + +You should always use `declared(params)` when you pass the params hash as +arguments to a method call. + +For instance: + +```ruby +# bad +User.create(params) # imagine the user submitted `admin=1`... :) + +# good +User.create(declared(params, include_parent_namespaces: false).to_h) +``` + +>**Note:** +`declared(params)` return a `Hashie::Mash` object, on which you will have to +call `.to_h`. + +But we can use directly `params[key]` when we access single elements. + +For instance: + +```ruby +# good +Model.create(foo: params[:foo]) +``` + +>**Note:** +Since you [should use Grape's DSL to declare params](doc_styleguide.md#method-description), [parameters validation and +coercion] comes for free! + +[parameters validation and coercion]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion -- cgit v1.2.1 From c1dd1795ed57c9481a1a9cab81daef39dd218346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Oct 2016 19:35:57 +0200 Subject: Move the Grape DSL part from Doc styleguide to API styleguide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also improve API styleguide Signed-off-by: Rémy Coutable --- doc/development/api_styleguide.md | 48 +++++++++++++++++++++++++++++++++++---- doc/development/doc_styleguide.md | 6 ----- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index ee5e7ad3988..be303fc6d39 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -2,6 +2,47 @@ This styleguide recommends best practices for the API development. +## Instance variables + +Please do not use instance variables, there is no need for them (we don't need +to access them as we do in Rails views), local variables are fine. + +## Entities + +Always use an [Entity] to present the endpoint's payload. + +## Methods and parameters description + +Every method must be described using the [Grape DSL](https://github.com/ruby-grape/grape#describing-methods) +(see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/environments.rb +for a good example): + +- `desc` for the method summary. You should pass it a block for additional + details such as: + - The GitLab version when the endpoint was added + - If the endpoint is deprecated, and if yes when will it be removed + +- `params` for the method params. This acts as description, validation, and + coercion of the parameters + +A good example is as follows: + +```ruby +desc 'Get all broadcast messages' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::BroadcastMessage +end +params do + optional :page, type: Integer, desc: 'Current page number' + optional :per_page, type: Integer, desc: 'Number of messages per page' +end +get do + messages = BroadcastMessage.all + + present paginate(messages), with: Entities::BroadcastMessage +end +``` + ## Declared params > Grape allows you to access only the parameters that have been declared by your @@ -10,7 +51,7 @@ allowed. – https://github.com/ruby-grape/grape#declared -### Exclude params from parent namespaces +### Exclude params from parent namespaces! > By default `declared(params) `includes parameters that were defined in all parent namespaces. @@ -51,8 +92,5 @@ For instance: Model.create(foo: params[:foo]) ``` ->**Note:** -Since you [should use Grape's DSL to declare params](doc_styleguide.md#method-description), [parameters validation and -coercion] comes for free! - +[Entity]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/entities.rb [parameters validation and coercion]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index 0b725cf200c..882da2a6562 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -342,12 +342,6 @@ You can use the following fake tokens as examples. Here is a list of must-have items. Use them in the exact order that appears on this document. Further explanation is given below. -- Every method must be described using [Grape's DSL](https://github.com/ruby-grape/grape/tree/v0.13.0#describing-methods) - (see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/environments.rb - for a good example): - - `desc` for the method summary (you can pass it a block for additional details) - - `params` for the method params (this acts as description **and** validation - of the params) - Every method must have the REST API request. For example: ``` -- cgit v1.2.1 From 3a1d9bccd6c3ce8249e90153f4299db1c037eb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Oct 2016 19:38:38 +0200 Subject: Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/api_styleguide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index be303fc6d39..94047dfe173 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -1,6 +1,6 @@ # API styleguide -This styleguide recommends best practices for the API development. +This styleguide recommends best practices for API development. ## Instance variables -- cgit v1.2.1 From b4810fd2bce42d66cada56af3ef697219f176b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Oct 2016 19:40:20 +0200 Subject: More improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/api_styleguide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index 94047dfe173..2a41314d2db 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -22,8 +22,8 @@ for a good example): - The GitLab version when the endpoint was added - If the endpoint is deprecated, and if yes when will it be removed -- `params` for the method params. This acts as description, validation, and - coercion of the parameters +- `params` for the method params. This acts as description, + [validation, and coercion of the parameters] A good example is as follows: @@ -93,4 +93,4 @@ Model.create(foo: params[:foo]) ``` [Entity]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/entities.rb -[parameters validation and coercion]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion +[validation, and coercion of the parameters]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion -- cgit v1.2.1 From 11e40c0e26e07d153cd2712bf23b5d679b54615c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 17 Oct 2016 10:10:13 +0200 Subject: Improve copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/api_styleguide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index 2a41314d2db..ce444ebdde4 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -20,7 +20,7 @@ for a good example): - `desc` for the method summary. You should pass it a block for additional details such as: - The GitLab version when the endpoint was added - - If the endpoint is deprecated, and if yes when will it be removed + - If the endpoint is deprecated, and if so, when will it be removed - `params` for the method params. This acts as description, [validation, and coercion of the parameters] @@ -83,7 +83,7 @@ User.create(declared(params, include_parent_namespaces: false).to_h) `declared(params)` return a `Hashie::Mash` object, on which you will have to call `.to_h`. -But we can use directly `params[key]` when we access single elements. +But we can use `params[key]` directly when we access single elements. For instance: -- cgit v1.2.1 From a871032f4a5353d118e0f0dbd7373cfdefb89f57 Mon Sep 17 00:00:00 2001 From: Tobias Genberg Date: Wed, 19 Oct 2016 05:52:07 +0000 Subject: Changing gitlab-shell version to 3.6.6 instead of 3.6.3, later on the upgrade process it will complain about running 3.6.3 instead of 3.6.6. --- doc/update/8.12-to-8.13.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.12-to-8.13.md b/doc/update/8.12-to-8.13.md index 8940d14559b..c0084d9d59c 100644 --- a/doc/update/8.12-to-8.13.md +++ b/doc/update/8.12-to-8.13.md @@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-13-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all --tags -sudo -u git -H git checkout v3.6.3 +sudo -u git -H git checkout v3.6.6 ``` ### 6. Update gitlab-workhorse -- cgit v1.2.1 From a0f6526f02ac855027d7dfe763aab4b2a9978dca Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Oct 2016 10:26:28 +0100 Subject: Tests update --- spec/features/boards/sidebar_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb index 905d720705d..f160052a844 100644 --- a/spec/features/boards/sidebar_spec.rb +++ b/spec/features/boards/sidebar_spec.rb @@ -4,7 +4,8 @@ describe 'Issue Boards', feature: true, js: true do include WaitForAjax include WaitForVueResource - let(:project) { create(:project_with_board, :public) } + let(:project) { create(:empty_project, :public) } + let(:board) { create(:board, project: project) } let(:user) { create(:user) } let!(:label) { create(:label, project: project) } let!(:label2) { create(:label, project: project) } @@ -17,7 +18,7 @@ describe 'Issue Boards', feature: true, js: true do login_as(user) - visit namespace_project_board_path(project.namespace, project) + visit namespace_project_board_path(project.namespace, project, board) wait_for_vue_resource end -- cgit v1.2.1 From cd5e83b6d6da3bddbc44334a1bcdbac287b35fb4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Oct 2016 22:26:51 +0100 Subject: Scroll board into view when clicking issue Changed return statement instead of if Delete objects after issue is closed --- app/assets/javascripts/boards/boards_bundle.js.es6 | 8 +++++++- .../javascripts/boards/components/board.js.es6 | 21 +++++++++++++++++++++ .../javascripts/boards/components/board_card.js.es6 | 6 +----- .../boards/components/board_sidebar.js.es6 | 19 +++++++++++++------ app/assets/stylesheets/pages/boards.scss | 4 ++++ .../projects/boards/components/_sidebar.html.haml | 2 +- app/views/projects/boards/index.html.haml | 2 +- app/views/projects/boards/show.html.haml | 2 +- 8 files changed, 49 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index 0cecdc4f50a..56f91503017 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -33,9 +33,15 @@ $(() => { endpoint: $boardApp.dataset.endpoint, boardId: $boardApp.dataset.boardId, disabled: $boardApp.dataset.disabled === 'true', - issueLinkBase: $boardApp.dataset.issueLinkBase + issueLinkBase: $boardApp.dataset.issueLinkBase, + detailIssue: Store.detail }, init: Store.create.bind(Store), + computed: { + detailIssueVisible () { + return Object.keys(this.detailIssue.issue).length; + } + }, created () { gl.boardService = new BoardService(this.endpoint, this.boardId); }, diff --git a/app/assets/javascripts/boards/components/board.js.es6 b/app/assets/javascripts/boards/components/board.js.es6 index cacb36a897f..500213a3a43 100644 --- a/app/assets/javascripts/boards/components/board.js.es6 +++ b/app/assets/javascripts/boards/components/board.js.es6 @@ -21,6 +21,7 @@ }, data () { return { + detailIssue: Store.detail, filters: Store.state.filters, showIssueForm: false }; @@ -32,6 +33,26 @@ this.list.getIssues(true); }, deep: true + }, + detailIssue: { + handler () { + if (!Object.keys(this.detailIssue.issue).length) return; + + const issue = this.list.findIssue(this.detailIssue.issue.id); + + if (issue) { + const boardsList = document.querySelectorAll('.boards-list')[0]; + const right = (this.$el.offsetLeft + this.$el.offsetWidth) - boardsList.offsetWidth; + const left = boardsList.scrollLeft - this.$el.offsetLeft; + + if (right - boardsList.scrollLeft > 0) { + boardsList.scrollLeft = right; + } else if (left > 0) { + boardsList.scrollLeft = this.$el.offsetLeft; + } + } + }, + deep: true } }, methods: { diff --git a/app/assets/javascripts/boards/components/board_card.js.es6 b/app/assets/javascripts/boards/components/board_card.js.es6 index bcb1b60b978..f743d72f936 100644 --- a/app/assets/javascripts/boards/components/board_card.js.es6 +++ b/app/assets/javascripts/boards/components/board_card.js.es6 @@ -20,11 +20,7 @@ }, computed: { issueDetailVisible () { - if (this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id) { - return true; - } else { - return false; - } + return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id; } }, methods: { diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6 index 1c002a54bed..35d97531439 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js.es6 +++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6 @@ -29,15 +29,22 @@ issue () { if (this.showSidebar) { this.$nextTick(() => { - new IssuableContext(this.currentUser); - new MilestoneSelect(); - new gl.DueDateSelectors(); - new LabelsSelect(); - new Sidebar(); - new Subscription('.subscription'); + this.issuableContext = new IssuableContext(this.currentUser); + this.milestoneSelect = new MilestoneSelect(); + this.dueDateSelect = new gl.DueDateSelectors(); + this.labelsSelect = new LabelsSelect(); + this.sidebar = new Sidebar(); + this.subscription = new Subscription('.subscription'); }); } else { $('.right-sidebar').getNiceScroll().remove(); + + delete this.issuableContext; + delete this.milestoneSelect; + delete this.dueDateSelect; + delete this.labelsSelect; + delete this.sidebar; + delete this.subscription; } } }, diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 0807d583207..8e972020234 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -76,6 +76,10 @@ lex height: 475px; // Needed for PhantomJS height: calc(100vh - 220px); min-height: 475px; + + &.is-compact { + width: calc(100% - 290px); + } } } diff --git a/app/views/projects/boards/components/_sidebar.html.haml b/app/views/projects/boards/components/_sidebar.html.haml index cf70ad4927a..7907fdfa810 100644 --- a/app/views/projects/boards/components/_sidebar.html.haml +++ b/app/views/projects/boards/components/_sidebar.html.haml @@ -12,7 +12,7 @@ {{ issue.id }} %a.gutter-toggle.pull-right{ role: "button", href: "#", - "@click" => "closeSidebar", + "@click.prevent" => "closeSidebar", "aria-label" => "Toggle sidebar" } = icon("times") .js-issuable-update diff --git a/app/views/projects/boards/index.html.haml b/app/views/projects/boards/index.html.haml index 45c2e0ee2da..29c9a43a0c1 100644 --- a/app/views/projects/boards/index.html.haml +++ b/app/views/projects/boards/index.html.haml @@ -11,7 +11,7 @@ = render 'shared/issuable/filter', type: :boards #board-app.boards-app{ "v-cloak" => true, data: board_data } - .boards-list + .boards-list{ ":class" => "{ 'is-compact': detailIssueVisible }" } .boards-app-loading.text-center{ "v-if" => "loading" } = icon("spinner spin") = render "projects/boards/components/board" diff --git a/app/views/projects/boards/show.html.haml b/app/views/projects/boards/show.html.haml index 45c2e0ee2da..29c9a43a0c1 100644 --- a/app/views/projects/boards/show.html.haml +++ b/app/views/projects/boards/show.html.haml @@ -11,7 +11,7 @@ = render 'shared/issuable/filter', type: :boards #board-app.boards-app{ "v-cloak" => true, data: board_data } - .boards-list + .boards-list{ ":class" => "{ 'is-compact': detailIssueVisible }" } .boards-app-loading.text-center{ "v-if" => "loading" } = icon("spinner spin") = render "projects/boards/components/board" -- cgit v1.2.1 From 344154e9068945af2874d6cb253457fb90fef3f3 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Oct 2016 22:39:57 +0100 Subject: Updated close sidebar icon --- app/assets/stylesheets/pages/boards.scss | 10 ++++++++-- app/views/projects/boards/components/_sidebar.html.haml | 2 +- app/views/shared/icons/_icon_close.svg | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 app/views/shared/icons/_icon_close.svg diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 8e972020234..641f35f1609 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -302,10 +302,16 @@ lex width: 22px; color: $gray-darkest; - .fa { + svg { position: absolute; top: 50%; - margin-top: (-15px / 2); + margin-top: (-11px / 2); + } + + &:hover { + path { + fill: $gray-darkest; + } } } diff --git a/app/views/projects/boards/components/_sidebar.html.haml b/app/views/projects/boards/components/_sidebar.html.haml index 7907fdfa810..9616ee5b795 100644 --- a/app/views/projects/boards/components/_sidebar.html.haml +++ b/app/views/projects/boards/components/_sidebar.html.haml @@ -14,7 +14,7 @@ href: "#", "@click.prevent" => "closeSidebar", "aria-label" => "Toggle sidebar" } - = icon("times") + = custom_icon("icon_close", size: 15) .js-issuable-update = render "projects/boards/components/sidebar/assignee" = render "projects/boards/components/sidebar/milestone" diff --git a/app/views/shared/icons/_icon_close.svg b/app/views/shared/icons/_icon_close.svg new file mode 100644 index 00000000000..9d62012518b --- /dev/null +++ b/app/views/shared/icons/_icon_close.svg @@ -0,0 +1 @@ + \ No newline at end of file -- cgit v1.2.1 From 373c232c05dce8b720017265763ed34f36f1aeaf Mon Sep 17 00:00:00 2001 From: evhoffmann Date: Wed, 19 Oct 2016 18:30:06 -0400 Subject: a round of terms and edits --- doc/university/glossary/README.md | 367 ++++++++++++++++++++++++-------------- 1 file changed, 230 insertions(+), 137 deletions(-) diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md index a86ff165f2e..2cca5439a5a 100644 --- a/doc/university/glossary/README.md +++ b/doc/university/glossary/README.md @@ -6,83 +6,87 @@ Please add any terms that you discover that you think would be useful for others ### 2FA -User authentication by combination of 2 different steps during login. This allows for more security. +User authentication by combination of 2 different steps during login. This allows for [more security](https://about.gitlab.com/handbook/security/). ### Access Levels -Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. -See, [GitLab's Permission Guidelines](http://doc.gitlab.com/ce/permissions/permissions.html) +Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](http://doc.gitlab.com/ce/permissions/permissions.html) ### Active Directory (AD) -A Microsoft based directory service for windows domain networks. It uses LDAP technology under the hood +A Microsoft-based [directory service](https://msdn.microsoft.com/en-us/library/bb742424.aspx) for windows domain networks. It uses LDAP technology under the hood. ### Agile -Building and delivering software in phases/parts rather than trying to build everything at once then delivering to the user/client. The later is known as a WaterFall model +Building and [delivering software](http://agilemethodology.org/) in phases/parts rather than trying to build everything at once then delivering to the user/client. The latter is known as the WaterFall model. ### Application Lifecycle Management (ALM) -Entire product lifecycle management process for an application. From requirements management, development and testing until deployment. +The entire product lifecycle management process for an application, from requirements management, development, and testing until deployment. GitLab has [advantages](https://docs.google.com/presentation/d/1vCU-NbZWz8NTNK8Vu3y4zGMAHb5DpC8PE5mHtw1PWfI/edit#slide=id.g72f2e4906_2_288) over both legacy and modern ALM tools. ### Artifactory -Version control for binaries. +A version control [system](https://www.jfrog.com/open-source/#os-arti) for non-text files. ### Artifacts -objects (usually binary and large) created by a build process +Objects (usually binary and large) created by a build process. These can include use cases, class diagrams, requirements and design documents. ### Atlassian -A company that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo. See [Atlassian] (https://www.atlassian.com) +A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo. ### Audit Log -*** Needs definition here +Also called an [audit trail](https://en.wikipedia.org/wiki/Audit_trail), an audit log is a document that records an event in an IT system. ### Auto Defined User Group -User groups are a way of centralizing control over important management tasks, particularly access control and password policies. -A simple example of such groups are the users and the admins groups. -In most of the cases these groups are auto defined in terms of access, rules of usage, conditions to be part of, etc... +User groups are a way of centralizing control over important management tasks, particularly access control and password policies. A simple example of such groups are the users and the admins groups. +In most of the cases these groups are auto defined in terms of access, rules of usage, conditions to be part of, etc. ### Bamboo -Atlassian's CI tool similar to GitLab CI and Jenkins +Atlassian's CI tool similar to GitLab CI and Jenkins. ### Basic Subscription -Entry level subscription for GitLab EE currently available in packs of 10 see [Basic subscription](https://about.gitlab.com/pricing/) +Entry level [subscription](https://about.gitlab.com/pricing/) for GitLab EE currently available in packs of 10. ### Bitbucket -Atlassian's web hosting service for Git and Mercurial Projects i.e. GitLab.com competitor +Atlassian's web hosting service for Git and Mercurial Projects i.e. GitLab.com competitor. ### Branch -A branch is a parallel version of a repository. Allows you to work on the repository without you affecting the "master" branch. Allows you to make changes without affecting the current "live" version. When you have made all your changes to your branch you can then merge to the master and to make the changes fo "live". +A branch is a parallel version of a repository. This allows you to work on the repository without affecting the "master" branch, and without affecting the current "live" version. When you have made all your changes to your branch you can then merge to the master. When your merge request is accepted your changes will be "live." ### Branded Login -Having your own logo on your GitLab instance login page instead of the GitLab logo. +Having your own logo on [your GitLab instance login page](https://docs.gitlab.com/ee/customization/branded_login_page.html) instead of the GitLab logo. + +### Build triggers +These protect your code base against breaks, for instance when a team is working on the same project. Learn about [setting up](https://docs.gitlab.com/ce/ci/triggers/README.html) build triggers. ### CEPH -is a distributed object store and file system designed to provide excellent performance, reliability and scalability. + A distributed object store and file [system](http://ceph.com/) designed to provide excellent performance, reliability and scalability. + +### ChatOps + +The ability to [initiate an action](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/1412) from chat. ChatBots run in your chat application and give you the ability to do "anything" from chat. ### Clone -A copy of a repository stored on your machine that allows you to use your own editor without being online, but still tracks the changes made remotely. +A [copy](https://git-scm.com/docs/git-clone) of a repository stored on your machine that allows you to use your own editor without being online, but still tracks the changes made remotely. ### Code Review -Examination of a progam's code. The main aim is to maintain high standards quality of code that is being shipped. +Examination of a progam's code. The main aim is to maintain high quality standards of code that is being shipped. Merge requests [serve as a code review tool](https://about.gitlab.com/2014/09/29/gitlab-flow/) in GitLab. ### Code Snippet -A small amount of code. Usually for the purpose of showing other developers how -to do something specific or reproduce a problem. +A small amount of code, usually selected for the purpose of showing other developers how to do something specific or reproduce a problem. ### Collaborator @@ -90,31 +94,39 @@ Person with read and write access to a repository who has been invited by reposi ### Commit -Is a change (revision) to a file, and also creates an ID that allows you to see revision history and who made the changes. +A [change](https://git-scm.com/docs/git-commit) (revision) to a file that also creates an ID, allowing you to see revision history and the author of the changes. ### Community -Everyone who is using GitLab +[Everyone](https://about.gitlab.com/community/) who uses GitLab. ### Confluence -Atlassian's product for collaboration of documents and projects. +Atlassian's product for collaboration on documents and projects. -### Continuous Deivery +### Continuous Delivery -Continuous delivery is a series of practices designed to ensure that code can be rapidly and safely deployed to production by delivering every change to a production-like environment and ensuring business applications and services function as expected through rigorous automated testing. +A [software engineering approach](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which continuous integration, automated testing, and automated deployment capabilities allow software to be developed and deployed rapidly, reliably and repeatedly with minimal human intervention. Still, the deployment to production is defined strategically and triggered manually. ### Continuous Deployment -Continuous deployment is the next step of continuous delivery: Every change that passes the automated tests is deployed to production automatically. +A [software development practice](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which every code change goes through the entire pipeline and is put into production automatically, resulting in many production deployments every day. It does everything that Continuous Delivery does, but the process is fully automated, there's no human intervention at all. ### Continuous Integration -A process that involves adding new code commits to source code with the combined code being run on an automated test to ensure that the changes do not break the software. +A [software development practice](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which you build and test software every time a developer pushes code to the application, and it happens several times a day. ### Contributor -Term used to a person contributing to an Open Source Project. +Term used for a person contributing to an open source project. + +### Conversational Development (ConvDev) + +A [natural evolution](https://about.gitlab.com/2016/09/14/gitlab-live-event-recap/) of software development that carries a conversation across functional groups throughout the development process, enabling developers to track the full path of development in a cohesive and intuitive way. ConvDev accelerates the development lifecycle by fostering collaboration and knowledge sharing from idea to production. + +### Cycle Time + +The time it takes to move from [idea to production](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab). ### Data Centre @@ -122,37 +134,47 @@ Atlassian product for High Availability. ### Deploy Keys -An SSH key stored on the your server that grants access to a single GitLab repository. This is used by a GitLab runner to clone a project's code so that tests can be run against the checked out code. +A [SSH key](https://docs.gitlab.com/ce/gitlab-basics/create-your-ssh-keys.html)stored on your server that grants access to a single GitLab repository. This is used by a GitLab runner to clone a project's code so that tests can be run against the checked out code. ### Developer -For us (GitLab) this means a software developer, i.e. someone who makes software. It is also one of the levels of access in our multi level approval system. +For us at GitLab, this means a software developer, or someone who makes software. It is also one of the levels of access in our multi-level approval system. + +### DevOps + +The intersection of software engineering, quality assurance, and technology operations. Explore more DevOps topics in the [glossary by XebiaLabs](https://xebialabs.com/glossary/) ### Diff -Is the difference between two commits, or saved changes. This will also be shown visually after the changes. +The difference between two commits, or saved changes. This will also be shown visually after the changes. + +#### Directory + +A folder used for storing multiple files. ### Docker -Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. -This guarantees that it will always run the same, regardless of the environment it is running in. +Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in. + +### Dynamic Environment + +### Emacs ### Fork -Your own copy of a repository that allows you to make changes to the repository without affecting the original. +Your [own copy](https://docs.gitlab.com/ce/workflow/forking_workflow.html) of a repository that allows you to make changes to the repository without affecting the original. ### Gerrit -A code review tool built on top of Git. +A code review [tool](https://www.gerritcodereview.com/) built on top of Git. ### Git Hooks -Are scripts you can use to trigger actions at certain points. +[Scripts](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you can use to trigger actions at certain points. ### GitHost.io -Is a single-tenant solution that provides GitLab CE or EE as a managed service. GitLab Inc. is responsible for -installing, updating, hosting, and backing up customers own private and secure GitLab instance. +A single-tenant solution that provides GitLab CE or EE as a managed service. GitLab Inc. is responsible for installing, updating, hosting, and backing up customers' own private and secure GitLab instance. ### GitHub @@ -164,51 +186,78 @@ Our free on Premise solution with >100,000 users ### GitLab CI -Our own Continuos Integration feature that is shipped with each instance +Our own Continuos Integration [feature](https://about.gitlab.com/gitlab-ci/) that is shipped with each instance ### GitLab EE -Our premium on premise solution that currently has Basic, Standard and Plus subscription packages with additional features and support. +Our premium on premise [solution](https://about.gitlab.com/features/#enterprise) that currently has Basic, Standard and Plus subscription packages with additional features and support. ### GitLab.com Our free SaaS for public and private repositories. +### GitLab Geo + +Allows you to replicate your GitLab instance to other geographical locations as a read-only fully operational version. It [can be used](https://docs.gitlab.com/ee/gitlab-geo/README.html) for cloning and fetching projects, in addition to reading any data. This will make working with large repositories over large distances much faster. + +### GitLab Pages +These allow you to [create websites](https://pages.gitlab.io/) for your GitLab projects, groups, or user account. + ### Gitolite -Is basically an access layer that sits on top of Git. Users are granted access to repos via a simple config file and you as an admin only needs the users public SSH key and a username from the user. +An [access layer](https://git-scm.com/book/en/v1/Git-on-the-Server-Gitolite) that sits on top of Git. Users are granted access to repos via a simple config file. As an admin, you only need the users' public SSH key and a username. ### Gitorious -A web based hosting service for projects using Git. It was acquired by GitLab and we discontinued the service. [Gitorious Acquisition Blog Post](https://about.gitlab.com/2015/03/03/gitlab-acquires-gitorious/) +A web-based hosting service for projects using Git. It was acquired by GitLab and we discontinued the service. Read the[Gitorious Acquisition Blog Post](https://about.gitlab.com/2015/03/03/gitlab-acquires-gitorious/). -### HADR +### Go -Sometimes written HA/DR. High Availability for Disaster Recovery. Usually refers to a strategy having a failover server in place in case the main server fails. +An open source programming [language](https://golang.org/). + +### GUI/ Git GUI + +A portable [graphical interface](https://git-scm.com/docs/git-gui) to Git that allows users to make changes to their repository by making new commits, amending existing ones, creating branches, performing local merges, and fetching/pushing to remote repositories. + +### High Availability for Disaster Recovery (HADR) + +Sometimes written HA/DR, this usually refers to a strategy for having a failover server in place in case the main server fails. ### Hip Chat -Atlassian's real time chat application for teams. Competitor to Slack, RocketChat and MatterMost. +Atlassian's real time chat application for teams, Hip Chat is a competitor to Slack, RocketChat and MatterMost. ### High Availability -Refers to a system or component that is continuously operational for a desirably long length of time. Availability can be measured relative to "100% operational" or "never failing." +Refers to a [system or component](https://about.gitlab.com/high-availability/) that is continuously operational for a desirably long length of time. Availability can be measured relative to "100% operational" or "never failing." + +### Inner-sourcing + +The [use of](https://about.gitlab.com/2014/09/05/innersourcing-using-the-open-source-workflow-to-improve-collaboration-within-an-organization/) open source development techniques within the corporation. + +### Internet Relay Chat (IRC) + +An [application layer protocol](http://www.irchelp.org/) that facilitates communication in the form of text. ### Issue Tracker -A tool used to manage, organize, and maintain a list of issues, making it easier for an organization to manage. +A [tool](https://docs.gitlab.com/ee/integration/external-issue-tracker.html) used to manage, organize, and maintain a list of issues, making it easier for an organization to manage. ### Jenkins -An Open Source CI tool written using the Java programming language. Does the same job as GitLab CI, Bamboo, Travis CI. It is extremely popular. see [Jenkins](https://jenkins-ci.org/) +An Open Source CI tool written using the Java programming language. [Jenkins](https://jenkins-ci.org/) does the same job as GitLab CI, Bamboo, and Travis CI. It is extremely popular. ### Jira -Atlassian's project management software. i.e. a complex issue tracker. See[Jira](https://www.atlassian.com/software/jira) +Atlassian's [project management software](https://www.atlassian.com/software/jira), i.e. a complex issue tracker. GitLab [can be configured](https://docs.gitlab.com/ee/project_services/jira.html) to interact with JIRA Core either using an on-premises instance or the SaaS solution that Atlassian offers. + +### JUnit + +A testing framework for the Java programming language, [JUnit](http://junit.org/junit4/) has been important in the evolution of test-driven development. ### Kerberos -A network authentication protocol that uses secret-key cryptography for security. +A network authentication [protocol](http://web.mit.edu/kerberos/) that uses secret-key cryptography for security. ### Kubernetes @@ -216,23 +265,27 @@ An open source container cluster manager originally designed by Google. It's bas ### Labels -An identifier to describe a group of one or more specific file revisions +An [identifier](https://docs.gitlab.com/ce/user/project/labels.html) to describe a group of one or more specific file revisions. -### LDAP +### Lightweight Directory Access Protocol (LDAP) -Lightweight Directory Access Protocol - basically its a directory (electronic address book) with user information e.g. name, phone_number etc + A directory (electronic address book) with user information (e.g. name, phone_number etc.) ### LDAP User Authentication -Allowing GitLab to sign in people from an LDAP server i.e. Allow people whose names are on the electronic user directory server) to be able to use their LDAP accounts to login. +GitLab [integrates](https://docs.gitlab.com/ce/administration/auth/ldap.html) with LDAP to support user authentication. This enables GitLab to sign in people from an LDAP server (i.e., allowing people whose names are on the electronic user directory server to be able to use their LDAP accounts to login.) ### LDAP Group Sync Allows you to synchronize the members of a GitLab group with one or more LDAP groups. -### Git LFS +### Load Balancer + +A [device](https://en.wikipedia.org/wiki/Load_balancing_(computing)) that distributes network or application traffic across multiple servers. + +### Git Large File Storage (LFS) -Git Large File Storage. A way to enable git to handle large binary files by using reference pointers within small text files to point to the large files. +A way [to enable](https://about.gitlab.com/2015/11/23/announcing-git-lfs-support-in-gitlab/) git to handle large binary files by using reference pointers within small text files to point to the large files. Large files such as high resolution images and videos, audio files, and assets can be called from a remote server. ### Linux @@ -240,8 +293,7 @@ An operating system like Windows or OS X. It is mostly used by software develope ### Markdown -Is a lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. -Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. +A lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. Checkout GitLab's [Markdown guide](https://gitlab.com/help/user/markdown.md). ### Maria DB @@ -249,193 +301,211 @@ A community developed fork/variation of MySQL. MySQL is owned by Oracle. ### Master -Name of the default branch in every git repository. +Name of the [default branch](https://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is) in every git repository. + +### Mattermost + +An open source, self-hosted messaging alternative to Slack. View GitLab's Mattermost [feature](https://gitlab.com/gitlab-org/gitlab-mattermost). ### Mercurial -A free distributed version control system like Git. Think of it as a competitor to Git. +A free distributed version control system similar to and a competitor with Git. ### Merge -Takes changes from one branch, and applies them into another branch. +Takes changes from one branch, and [applies them](https://git-scm.com/docs/git-merge) into another branch. + +### Merge Conflict + +[Arises](https://about.gitlab.com/2016/09/06/resolving-merge-conflicts-from-the-gitlab-ui/) when a merge can't be performed cleanly between two versions of the same file. ### Meteor -A hip platform for building javascript apps.[Meteor] (https://www.meteor.com) +A [platform](https://www.meteor.com) for building javascript apps. ### Milestones -Allows you to track the progress on issues, and merge requests, which allows you to get a snapshot of the progress made. +Allow you to [organize issues](https://docs.gitlab.com/ce/workflow/milestones.html) and merge requests in GitLab into a cohesive group, optionally setting a due date. A common use is keeping track of an upcoming software version. Milestones are created per-project. ### Mirror Repositories -You can set up a project to automatically have its branches, tags, and commits updated from an upstream repository. This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and its activity using the familiar GitLab interface. +A project that is setup to automatically have its branches, tags, and commits [updated from an upstream repository](https://docs.gitlab.com/ee/workflow/repository_mirroring.html). This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and activity using the familiar GitLab interface. ### MIT License -A type of software license. It lets people do anything with your code with proper attribution and without warranty. It is the most common license for open source applications written in Ruby on Rails. GitLab CE is issued under this license. -This means, you can download the code, modify it as you want even build a new commercial product using the underlying code and its not illegal. The only condition is that there is no form of waranty provided by GitLab so whatever happens if you use the code is your own problem. - -### Mondo +A type of software license. It lets people do anything with your code with proper attribution and without warranty. It is the most common license for open source applications written in Ruby on Rails. GitLab CE is issued under this [license](https://docs.gitlab.com/ce/development/licensing.html). This means you can download the code, modify it as you want, and even build a new commercial product using the underlying code and it's not illegal. The only condition is that there is no form of warranty provided by GitLab so whatever happens when you use the code is your own problem. -*** Needs definition here +### Mondo Rescue -### Multi LDAP Server +A free disaster recovery [software](https://help.ubuntu.com/community/MondoMindi). -*** Needs definition here +### MySQL -### My SQL - -A relational database. Currently only supported if you are using EE. It is owned by Oracle. +A relational [database](http://www.mysql.com/) owned by Oracle. Currently only supported if you are using EE. ### Namespace -In computing, a namespace is a set of symbols that are used to organize objects of various kinds, so that these objects may be referred to by name. - -Prominent examples include: -- file systems are namespaces that assign names to files; -- programming languages organize their variables and subroutines in namespaces; -- computer networks and distributed systems assign names to resources, such as computers, printers, websites, (remote) files, etc. +A set of symbols that are used to organize objects of various kinds so that these objects may be referred to by name. Examples of namespaces in action include file systems that assign names to files; programming languages that organize their variables and subroutines in namespaces; and computer networks and distributed systems that assign names to resources, such as computers, printers, websites, (remote) files, etc. ### Nginx -(pronounced "engine x") is a web server. It can act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache. +A web [server](https://www.nginx.com/resources/wiki/) (pronounced "engine x"). It can act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache. -### oAuth +### OAuth -Is an open standard for authorization, commonly used as a way for Internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. +An open [standard](https://oauth.net/) for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. ### Omnibus Packages -Omnibus is a way to package the different services and tools required to run GitLab, so that users can install it without as much work. +A way to [package different services and tools](https://docs.gitlab.com/omnibus/) required to run GitLab, so that most developers can install it without laborious configuration. ### On Premise -On your own server. In GitLab, this refers to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com which is hosted by GitLab Inc's servers. +On your own server. In GitLab, this [refers](https://about.gitlab.com/2015/02/12/why-ship-on-premises-in-the-saas-era/) to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com, which is hosted by GitLab Inc's servers. + +### Open Core + +GitLab's [business model](https://about.gitlab.com/2016/07/20/gitlab-is-open-core-github-is-closed-source/). Coined by Andrew Lampitt in 2008, the [open core model](https://en.wikipedia.org/wiki/Open_core) primarily involves offering a "core" or feature-limited version of a software product as free and open-source software, while offering "commercial" versions or add-ons as proprietary software. ### Open Source Software -Software for which the original source code is freely available and may be redistributed and modified. +Software for which the original source code is freely [available](https://opensource.org/docs/osd) and may be redistributed and modified. GitLab prioritizes open source [stewardship](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/). ### Owner -This is the most powerful person on a GitLab project. He has the permissions of all the other users plus the additional permission of being able to destroy i.e. delete the project +The most powerful person on a GitLab project. They have the permissions of all the other users plus the additional permission of being able to destroy (i.e. delete) the project. -### PaaS +### Platform as a Service (PaaS) -Typically referred to in regards to application development, it is a model in which a cloud provider delivers hardware and software tools to its users as a service +Typically referred to in regards to application development, PaaS is a model in which a cloud provider delivers hardware and software tools to its users as a service. ### Perforce -The company that produces Helix. A commercial, proprietary, centralised VCS well known for it's ability to version files of any size and type. They OEM a re-branded version of GitLab called "GitSwarm" that is tightly integrated with their "GitFusion" product, which in turn represents a portion of a Helix repository (called a depot) as a git repo +The company that produces Helix. A commercial, proprietary, centralised VCS well known for its ability to version files of any size and type. They OEM a re-branded version of GitLab called "GitSwarm" that is tightly integrated with their "GitFusion" product, which in turn represents a portion of a Helix repository (called a depot) as a git repo. ### Phabricator -Is a suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion. +A suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion. ### Piwik Analytics -An open source analytics software to help you analyze web traffic. It is similar to google analytics only that google analytics is not open source and information is stored by google while in Piwik the information is stored in your own server hence fully private. +An open source analytics software to help you analyze web traffic. It is similar to Google Analytics, except that the latter is not open source and information is stored by Google. In Piwik, the information is stored on your own server and hence is fully private. ### Plus Subscription -GitLab Premium EE subscription that includes training and dedicated Account Management and Service Engineer and complete support package [Plus subscription](https://about.gitlab.com/pricing/) +GitLab Premium EE [subscription](https://about.gitlab.com/pricing/) that includes training and dedicated Account Management and Service Engineer and complete support package. ### PostgreSQL -A relational database. Touted as the most advanced open source database. +An [object-relational](https://en.wikipedia.org/wiki/PostgreSQL) database. Touted as the most advanced open source database, it is one of two database management systems [supported by](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/database.md) GitLab, the other being MySQL. ### Protected Branches -A feature that protects branches from unauthorized pushes, force pushing or deletion. +A [feature](https://docs.gitlab.com/ce/user/project/protected_branches.html) that protects branches from unauthorized pushes, force pushing or deletion. ### Pull -Git command to synchronize the local repository with the remote repository, by fetching all remote changes and merging them into the local repository. +Git command to [synchronize](https://git-scm.com/docs/git-pull) the local repository with the remote repository, by fetching all remote changes and merging them into the local repository. ### Puppet -A popular devops automation tool +A popular DevOps [automation tool](https://puppet.com/product/how-puppet-works). ### Push -Git command to send commits from the local repository to the remote repository. +Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. ### RE Read Only -Permissions to see a file and it's contents, but not change it +Permissions to see a file and its contents, but not change it. ### Rebase -Moves a branch from one commit to another. This allows you to re-write your project's history. +In addition to the merge, the [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) is a main way to integrate changes from one branch into another. -### Git Repository +### (Git) Repository -Storage location of all files which are tracked by git. +A directory where Git [has been initiatlized](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository) to start version controlling your files. The history of your work is stored here. A remote repository is not on your machine, but usually online (like on GitLab.com, for instance). The main remote repository is usually called "Origin." ### Requirements management -*** Needs definition here - -### Revision - -*** Needs definition here +Gives your distributed teams a single shared repository to collaborate and share requirements, understand their relationship to tests, and evaluate linked defects. It includes multiple, preconfigured requirement types. ### Revision Control -Also known as version control or source control, is the management of changes to documents, computer programs, large web sites, and other collections of information. Changes are usually identified by a number or letter code, termed the "revision number", "revision level", or simply "revision". +Also known as version control or source control, this is the management of changes to documents, computer programs, large web sites, and other collections of information. Changes are usually identified by a number or letter code, termed the "revision number," "revision level," or simply "revision." ### RocketChat -An open source chat application for teams. Very similar to Slack only that is is open-source. +An open source chat application for teams, RocketChat is very similar to Slack but it is also open-source. + +### Route Table + +A route table contains rules (called routes) that determine where network traffic is directed. Each [subnet in a VPC](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Route_Tables.html) must be associated with a route table. ### Runners -Actual build machines/containers that run/execute tests you have specified to be run on GitLab CI +Actual build machines/containers that [run and execute tests](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner) you have specified to be run on GitLab CI. -### SaaS +### Software as a service (SaaS) -Software as a service. Software is hosted centrally and accessed on-demand i.e. when you want to. This refers to GitLab.com in our scenario +Software that is hosted centrally and accessed on-demand (i.e. whenever you want to). This applies to GitLab.com. -### SCM +### Software Configuration Management (SCM) -Software Configuration Management. Often used by people when they mean Version Control +This term is often used by people when they mean "Version Control." ## Scrum -An Agile framework designed to help complete complex (typically) software projects. It's made up of several parts: product requirments backlog, sprint plannnig, sprint (development), sprint review, retrospec (analyzing the sprint). The goal is to end up with potentially shippable products. +An Agile [framework](https://www.scrum.org/Resources/What-is-Scrum) designed to typically help complete complex software projects. It's made up of several parts: product requirements backlog, sprint planning, sprint (development), sprint review, and retrospec (analyzing the sprint). The goal is to end up with potentially shippable products. ### Scrum Board The board used to track the status and progress of each of the sprint backlog items. +### Shell + +[Terminal](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. + +### Single-tenant + +The tenant purchases their own copy of the software and the software can be customized to meet the specific and needs of that customer. [GitHost.io](https://about.gitlab.com/handbook/positioning-faq/) is our provider of single-tenant 'managed cloud' GitLab instances. + ### Slack -Real time messaging app for teams. Used internally by GitLab +Real time messaging app for teams that is used internally by GitLab team members. GitLab users can enable [Slack integration](https://docs.gitlab.com/ce/project_services/slack.html) to trigger push, issue, and merge request events among others. ### Slave Servers -Also known as secondary servers. They help to spread the load over multiple machines, they also provide backups when the master/primary server crashes. +Also known as secondary servers, these help to spread the load over multiple machines. They also provide backups when the master/primary server crashes. ### Source Code -Program code as typed by a computer programmer. i.e. it has not yet been compiled/translated by the computer to machine language. +Program code as typed by a computer programmer (i.e. it has not yet been compiled/translated by the computer to machine language). ### SSH Key -A unique identifier of a computer. It is used to identify computers without the need for a password. e.g. On GitLab I have added the ssh key of all my work machines so that the GitLab instance knows that it can accept code pushes and pulls from this trusted machines whose keys are I have added. +A unique identifier of a computer. It is used to identify computers without the need for a password (e.g., On GitLab I have [added the ssh key](https://docs.gitlab.com/ce/gitlab-basics/create-your-ssh-keys.html) of all my work machines so that the GitLab instance knows that it can accept code pushes and pulls from this trusted machines whose keys are I have added.) + +### Single Sign On (SSO) -### SSO +An authentication process that allows you enter one username and password to access multiple applications. -Single Sign On. An authentication process that allows you enter one username and password to access multiple applications. +### Staging Area + +[Staging occurs](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics) before the commit process in git. The staging area is a file, generally contained in your Git directory, that stores information about what will go into your next commit. It’s sometimes referred to as the “index."" ### Standard Subscription -Our mid range EE subscription that includes 24/7 support, support for High Availability [Standard Subscription](https://about.gitlab.com/pricing/) +Our mid range EE subscription that includes 24/7 support and support for High Availability [Standard Subscription](https://about.gitlab.com/pricing/). ### Stash -Atlassian's Git On-Premises solution. Think of it as Atlassian's GitLab EE. It is now known as BitBucket Server. +Atlassian's Git on-premise solution. Think of it as Atlassian's GitLab EE, now known as BitBucket Server. + +### Static Site Generators (SSGs) + +A [software](https://wiki.python.org/moin/StaticSiteGenerator) that takes some text and templates as input and produces html files on the output. ### Subversion @@ -443,40 +513,63 @@ Non-proprietary, centralized version control system. ### Sudo -A program that allows you to perform superuser/administrator actions on Unix Operating Systems e.g. Linux, OS X. It actually stands for 'superuser do' +A program that allows you to perform superuser/administrator actions on Unix Operating Systems (e.g., Linux, OS X.) It actually stands for 'superuser do.' -### SVN +### Subversion (SVN) -Abbreviation for Subversion. +An open source version control system. ### Tag -Represents a version of a particular branch at a moment in time. +[Represents](https://docs.gitlab.com/ce/api/tags.html) a version of a particular branch at a moment in time. ### Tool Stack -Set of tools used in a process to achieve a common outcome. E.g. set of tools used in Application Lifecycle Management. +The set of tools used in a process to achieve a common outcome (e.g. set of tools used in Application Lifecycle Management). ### Trac -An Open Source project management and bug tracking web application. +An open source project management and bug tracking web [application](https://trac.edgewall.org/). + +### Untracked files + +New files that Git has not [been told](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) to track previously. ### User Anyone interacting with the software. -### VCS +### Version Control Software (VCS) + +Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. VCS [has evolved](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.gd69537a19_0_32) from local version control systems, to centralized version control systems, to the present distributed version control systems like Git, Mercurial, Bazaar, and Darcs. + +### Virtual Private Cloud (VPC) + +An on demand configurable pool of shared computing resources allocated within a public cloud environment, providing some isolation between the different users using the resources. GitLab users need to create a new Amazon VPC in order to [setup High Availability](https://docs.gitlab.com/ce/university/high-availability/aws/). -Version Control Software +### Virtual private server (VPS) + +A [virtual machine](https://en.wikipedia.org/wiki/Virtual_private_server) sold as a service by an Internet hosting service. A VPS runs its own copy of an operating system, and customers have superuser-level access to that operating system instance, so they can install almost any software that runs on that OS. + +### VM Instance ### Waterfall -A model of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the COMPLETE software to the customer that meets all the requirements specified by the customer +A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the complete software to the customer that meets all the requirements they specified. ### Webhooks -A way for for an app to provide other applications with real-time information. e.g. send a message to a slack channel when a commit is pushed +A way for for an app to [provide](https://docs.gitlab.com/ce/web_hooks/web_hooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) ### Wiki -A website/system that allows for collaborative editing of its content by the users. In programming, they usually contain documentation of how to use the software +A [website/system](http://www.wiki.com/) that allows for collaborative editing of its content by the users. In programming, wikis usually contain documentation of how to use the software. + +### Working Tree + +[Consists of files](http://stackoverflow.com/questions/3689838/difference-between-head-working-tree-index-in-git) that you are currently working on. + +### YAML + +A human-readable data serialization [language](http://www.yaml.org/about.html) that takes concepts from programming languages such as C, Perl, and Python, and ideas from XML and the data format of electronic mail. + -- cgit v1.2.1 From 29645f06e7889fe85bb155abaa0f361f8680311b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 20 Oct 2016 08:54:33 +0100 Subject: Hides/shows the boards sidebar Rather than constructing & then deconstructing, we know just hide & show the sidebar. This is done so we dont' have a memory leak on the frontend with objects getting created. --- .../boards/components/board_sidebar.js.es6 | 24 +++++++----------- app/assets/javascripts/due_date_select.js.es6 | 29 +++++++++++++++++++--- app/assets/javascripts/labels_select.js | 2 +- app/assets/javascripts/milestone_select.js | 2 +- app/assets/javascripts/subscription.js | 15 +++++++---- app/assets/javascripts/users_select.js | 2 +- .../projects/boards/components/_sidebar.html.haml | 2 +- .../boards/components/sidebar/_due_date.html.haml | 9 ++++--- .../boards/components/sidebar/_labels.html.haml | 2 +- 9 files changed, 55 insertions(+), 32 deletions(-) diff --git a/app/assets/javascripts/boards/components/board_sidebar.js.es6 b/app/assets/javascripts/boards/components/board_sidebar.js.es6 index 35d97531439..b20890df622 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js.es6 +++ b/app/assets/javascripts/boards/components/board_sidebar.js.es6 @@ -29,22 +29,8 @@ issue () { if (this.showSidebar) { this.$nextTick(() => { - this.issuableContext = new IssuableContext(this.currentUser); - this.milestoneSelect = new MilestoneSelect(); - this.dueDateSelect = new gl.DueDateSelectors(); - this.labelsSelect = new LabelsSelect(); - this.sidebar = new Sidebar(); - this.subscription = new Subscription('.subscription'); + $(".right-sidebar").getNiceScroll(0).doScrollTop(0, 0); }); - } else { - $('.right-sidebar').getNiceScroll().remove(); - - delete this.issuableContext; - delete this.milestoneSelect; - delete this.dueDateSelect; - delete this.labelsSelect; - delete this.sidebar; - delete this.subscription; } } }, @@ -52,6 +38,14 @@ closeSidebar () { this.detail.issue = {}; } + }, + ready () { + new IssuableContext(this.currentUser); + new MilestoneSelect(); + new gl.DueDateSelectors(); + new LabelsSelect(); + new Sidebar(); + new Subscription('.subscription'); } }); })(); diff --git a/app/assets/javascripts/due_date_select.js.es6 b/app/assets/javascripts/due_date_select.js.es6 index 41925fcc8e3..4f7c1092d05 100644 --- a/app/assets/javascripts/due_date_select.js.es6 +++ b/app/assets/javascripts/due_date_select.js.es6 @@ -41,7 +41,12 @@ defaultDate: $("input[name='" + this.fieldName + "']").val(), altField: "input[name='" + this.fieldName + "']", onSelect: () => { - return this.saveDueDate(true); + if (this.$dropdown.hasClass('js-issue-boards-due-date')) { + gl.issueBoards.BoardsStore.detail.issue.dueDate = $("input[name='" + this.fieldName + "']").val(); + this.updateIssueBoardIssue(); + } else { + return this.saveDueDate(true); + } } }); } @@ -49,8 +54,14 @@ initRemoveDueDate() { this.$block.on('click', '.js-remove-due-date', (e) => { e.preventDefault(); - $("input[name='" + this.fieldName + "']").val(''); - return this.saveDueDate(false); + + if (this.$dropdown.hasClass('js-issue-boards-due-date')) { + gl.issueBoards.BoardsStore.detail.issue.dueDate = ''; + this.updateIssueBoardIssue(); + } else { + $("input[name='" + this.fieldName + "']").val(''); + return this.saveDueDate(false); + } }); } @@ -83,6 +94,18 @@ this.datePayload = datePayload; } + updateIssueBoardIssue () { + this.$loading.fadeIn(); + this.$dropdown.trigger('loading.gl.dropdown'); + this.$selectbox.hide(); + this.$value.css('display', ''); + + gl.issueBoards.BoardsStore.detail.issue.update(this.$dropdown.attr('data-issue-update')) + .then(() => { + this.$loading.fadeOut(); + }); + } + submitSelectedDate(isDropdown) { return $.ajax({ type: 'PUT', diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index c532737626c..4e29ab71bd4 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -382,7 +382,7 @@ $loading.fadeIn(); - gl.issueBoards.BoardsStore.detail.issue.update(issueUpdateURL) + gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) .then(function () { $loading.fadeOut(); }); diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index bed1d52c989..95b5b934c81 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -137,7 +137,7 @@ $dropdown.trigger('loading.gl.dropdown'); $loading.fadeIn(); - gl.issueBoards.BoardsStore.detail.issue.update(issueUpdateURL) + gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) .then(function () { $dropdown.trigger('loaded.gl.dropdown'); $loading.fadeOut(); diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js index bfef9532d2b..bfa3663bca3 100644 --- a/app/assets/javascripts/subscription.js +++ b/app/assets/javascripts/subscription.js @@ -5,10 +5,10 @@ function Subscription(container) { this.toggleSubscription = bind(this.toggleSubscription, this); var $container; - $container = $(container); - this.url = $container.attr('data-url'); - this.subscribe_button = $container.find('.js-subscribe-button'); - this.subscription_status = $container.find('.subscription-status'); + this.$container = $(container); + this.url = this.$container.attr('data-url'); + this.subscribe_button = this.$container.find('.js-subscribe-button'); + this.subscription_status = this.$container.find('.subscription-status'); this.subscribe_button.unbind('click').click(this.toggleSubscription); } @@ -18,12 +18,17 @@ action = btn.find('span').text(); current_status = this.subscription_status.attr('data-status'); btn.addClass('disabled'); + + if ($('html').hasClass('issue-boards-page')) { + this.url = this.$container.attr('data-url'); + } + return $.post(this.url, (function(_this) { return function() { var status; btn.removeClass('disabled'); - if ($('body').data('page') === 'projects:boards:show') { + if ($('html').hasClass('issue-boards-page')) { Vue.set(gl.issueBoards.BoardsStore.detail.issue, 'subscribed', !gl.issueBoards.BoardsStore.detail.issue.subscribed); } else { status = current_status === 'subscribed' ? 'unsubscribed' : 'subscribed'; diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 6626d730e87..da6a59bcf97 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -39,7 +39,7 @@ var updateIssueBoardsIssue = function () { $loading.fadeIn(); - gl.issueBoards.BoardsStore.detail.issue.update(issueURL) + gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update')) .then(function () { $loading.fadeOut(); }); diff --git a/app/views/projects/boards/components/_sidebar.html.haml b/app/views/projects/boards/components/_sidebar.html.haml index 9616ee5b795..f0c0c6953e0 100644 --- a/app/views/projects/boards/components/_sidebar.html.haml +++ b/app/views/projects/boards/components/_sidebar.html.haml @@ -1,6 +1,6 @@ %board-sidebar{ "inline-template" => true, ":current-user" => "#{current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) if current_user}" } - %aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-if" => "showSidebar" } + %aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" } .issuable-sidebar .block.issuable-sidebar-header %span.issuable-header-text.hide-collapsed.pull-left diff --git a/app/views/projects/boards/components/sidebar/_due_date.html.haml b/app/views/projects/boards/components/sidebar/_due_date.html.haml index 91b4a572ee4..c7da1d0d4ac 100644 --- a/app/views/projects/boards/components/sidebar/_due_date.html.haml +++ b/app/views/projects/boards/components/sidebar/_due_date.html.haml @@ -10,10 +10,11 @@ No due date %span.bold{ "v-if" => "issue.dueDate" } {{ issue.dueDate | due-date }} - %span.no-value.js-remove-due-date-holder{ "v-if" => "issue.dueDate" } - \- - %a.js-remove-due-date{ href: "#", role: "button" } - remove due date + - if can?(current_user, :admin_issue, @project) + %span.no-value.js-remove-due-date-holder{ "v-if" => "issue.dueDate" } + \- + %a.js-remove-due-date{ href: "#", role: "button" } + remove due date - if can?(current_user, :admin_issue, @project) .selectbox %input{ type: "hidden", diff --git a/app/views/projects/boards/components/sidebar/_labels.html.haml b/app/views/projects/boards/components/sidebar/_labels.html.haml index 0e2ea74ec41..a6d2c3c300c 100644 --- a/app/views/projects/boards/components/sidebar/_labels.html.haml +++ b/app/views/projects/boards/components/sidebar/_labels.html.haml @@ -5,7 +5,7 @@ - if can?(current_user, :admin_issue, @project) = link_to "Edit", "#", class: "edit-link pull-right" .value.issuable-show-labels - %span.no-value{ "v-if" => "issue.labels.length === 0" } + %span.no-value{ "v-if" => "issue.labels && issue.labels.length === 0" } None %a{ href: "#", "v-for" => "label in issue.labels" } -- cgit v1.2.1 From 033988cf32da4d7a0ecdffac6e5b8e1b88a264f4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 20 Oct 2016 08:57:21 +0100 Subject: Added button types --- app/views/projects/boards/components/sidebar/_assignee.html.haml | 2 +- app/views/projects/boards/components/sidebar/_milestone.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/boards/components/sidebar/_assignee.html.haml b/app/views/projects/boards/components/sidebar/_assignee.html.haml index 4307e8e7626..b90db421af0 100644 --- a/app/views/projects/boards/components/sidebar/_assignee.html.haml +++ b/app/views/projects/boards/components/sidebar/_assignee.html.haml @@ -28,7 +28,7 @@ ":value" => "issue.assignee.id", "v-if" => "issue.assignee" } .dropdown - %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-sidebar{ data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, null_user: "true" }, + %button.dropdown-menu-toggle.js-user-search.js-author-search.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", field_name: "issue[assignee_id]", first_user: (current_user.username if current_user), current_user: "true", project_id: @project.id, null_user: "true" }, ":data-issuable-id" => "issue.id", ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } Select assignee diff --git a/app/views/projects/boards/components/sidebar/_milestone.html.haml b/app/views/projects/boards/components/sidebar/_milestone.html.haml index ec1354509f2..3cd20d1c0f7 100644 --- a/app/views/projects/boards/components/sidebar/_milestone.html.haml +++ b/app/views/projects/boards/components/sidebar/_milestone.html.haml @@ -16,7 +16,7 @@ name: "issue[milestone_id]", "v-if" => "issue.milestone" } .dropdown - %button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true" }, + %button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true" }, ":data-issuable-id" => "issue.id", ":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" } Milestone -- cgit v1.2.1 From 5f0b7fe429d75de2dbcfef142d2389bf99d199ec Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Mon, 17 Oct 2016 11:44:25 +0200 Subject: Stop injecting field errors where they won't be used. --- app/assets/javascripts/gl_field_errors.js.es6 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/gl_field_errors.js.es6 b/app/assets/javascripts/gl_field_errors.js.es6 index 8657e7b4abf..8e8f9f29ab3 100644 --- a/app/assets/javascripts/gl_field_errors.js.es6 +++ b/app/assets/javascripts/gl_field_errors.js.es6 @@ -137,8 +137,11 @@ } initValidators () { - // select all non-hidden inputs in form - this.state.inputs = this.form.find(':input:not([type=hidden])').toArray() + // register selectors here as needed + const validateSelectors = [':text', ':password', '[type=email]'] + .map((selector) => `input${selector}`).join(','); + + this.state.inputs = this.form.find(validateSelectors).toArray() .filter((input) => !input.classList.contains(customValidationFlag)) .map((input) => new GlFieldError({ input, formErrors: this })); -- cgit v1.2.1 From 06564f9e049417087fa53cf8ec15c22ec65724d5 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Thu, 20 Oct 2016 12:47:32 +0200 Subject: Update gl_field_error tests for better input filtering. --- spec/javascripts/gl_field_errors_spec.js.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/gl_field_errors_spec.js.es6 b/spec/javascripts/gl_field_errors_spec.js.es6 index 36feb2b2aa5..da9259edd78 100644 --- a/spec/javascripts/gl_field_errors_spec.js.es6 +++ b/spec/javascripts/gl_field_errors_spec.js.es6 @@ -11,12 +11,12 @@ this.fieldErrors = new global.GlFieldErrors($form); }); - it('should properly initialize the form', function() { + it('should select the correct input elements', function() { expect(this.$form).toBeDefined(); expect(this.$form.length).toBe(1); expect(this.fieldErrors).toBeDefined(); const inputs = this.fieldErrors.state.inputs; - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(4); }); it('should ignore elements with custom error handling', function() { -- cgit v1.2.1 From 493367108eef14c8517c6d023ec46267c1e706cf Mon Sep 17 00:00:00 2001 From: Paco Guzman Date: Wed, 19 Oct 2016 14:30:17 +0200 Subject: Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method --- CHANGELOG.md | 2 ++ app/models/issue.rb | 8 +++++++- spec/models/issue_spec.rb | 8 +++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b018fc0d57..6c0fd97b070 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) + - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method + ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) diff --git a/app/models/issue.rb b/app/models/issue.rb index 133a5993815..89158a50353 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -211,7 +211,13 @@ class Issue < ActiveRecord::Base note.all_references(current_user, extractor: ext) end - ext.merge_requests.select { |mr| mr.open? && mr.closes_issue?(self) } + merge_requests = ext.merge_requests.select(&:open?) + if merge_requests.any? + ids = MergeRequestsClosingIssues.where(merge_request_id: merge_requests.map(&:id), issue_id: id).pluck(:merge_request_id) + merge_requests.select { |mr| mr.id.in?(ids) } + else + [] + end end def moved? diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 3b8b743af2d..60d30eb7418 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -100,11 +100,17 @@ describe Issue, models: true do end it 'returns the merge request to close this issue' do - allow(mr).to receive(:closes_issue?).with(issue).and_return(true) + mr expect(issue.closed_by_merge_requests).to eq([mr]) end + it "returns an empty array when the merge request is closed already" do + closed_mr + + expect(issue.closed_by_merge_requests).to eq([]) + end + it "returns an empty array when the current issue is closed already" do expect(closed_issue.closed_by_merge_requests).to eq([]) end -- cgit v1.2.1 From e9218c7e4e6c46d72d0860ba1107643a9f15f542 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 19 Oct 2016 10:04:05 +0200 Subject: Change target Ruby version for Rubocop to 2.1. We have to use the lowest common denominator to check the supported syntax and in our case it is Ruby 2.1. Please note that it will not help with unsupported syntax in HAML files because they are not checked by Rubocop. --- .rubocop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index bec2464c740..13df3f99613 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,7 @@ require: inherit_from: .rubocop_todo.yml AllCops: - TargetRubyVersion: 2.3 + TargetRubyVersion: 2.1 # Cop names are not d§splayed in offense messages by default. Change behavior # by overriding DisplayCopNames, or by giving the -D/--display-cop-names # option. -- cgit v1.2.1 From 5f276f353c08966c82431cb2a85ce743f779e111 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 20 Oct 2016 14:32:24 +0100 Subject: Alignment of toggle button --- app/assets/stylesheets/pages/boards.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 3dda2042cb3..83f899164a9 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -296,7 +296,7 @@ .gutter-toggle { position: absolute; top: 0; - bottom: 10px; + bottom: 15px; right: 0; width: 22px; color: $gray-darkest; -- cgit v1.2.1 From 0c0caede85b0ea6082799ae9fa6afd74a1186006 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 20 Oct 2016 17:33:12 +0300 Subject: Fix: Backup restore doesn't clear cache --- CHANGELOG.md | 1 + lib/tasks/gitlab/backup.rake | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98cffab8c03..20aef16b105 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Simpler arguments passed to named_route on toggle_award_url helper method + - Fix: Backup restore doesn't clear cache ## 8.13.0 (2016-10-22) diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index b43ee5b3383..a9f1255e8cf 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -51,6 +51,7 @@ namespace :gitlab do $progress.puts 'done'.color(:green) Rake::Task['gitlab:backup:db:restore'].invoke end + Rake::Task['gitlab:backup:repo:restore'].invoke unless backup.skipped?('repositories') Rake::Task['gitlab:backup:uploads:restore'].invoke unless backup.skipped?('uploads') Rake::Task['gitlab:backup:builds:restore'].invoke unless backup.skipped?('builds') @@ -58,6 +59,7 @@ namespace :gitlab do Rake::Task['gitlab:backup:lfs:restore'].invoke unless backup.skipped?('lfs') Rake::Task['gitlab:backup:registry:restore'].invoke unless backup.skipped?('registry') Rake::Task['gitlab:shell:setup'].invoke + Rake::Task['cache:clear'].invoke backup.cleanup end -- cgit v1.2.1 From 23e81bfde879dfd116cb70cb8034d0fce21fffb6 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Thu, 20 Oct 2016 16:40:24 +0200 Subject: Ignore external issues when bulk assigning issues to author of merge request. Fixes #23552 --- app/services/merge_requests/assign_issues_service.rb | 2 +- spec/services/merge_requests/assign_issues_service_spec.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/services/merge_requests/assign_issues_service.rb b/app/services/merge_requests/assign_issues_service.rb index f636e5fec4f..066efa1acc3 100644 --- a/app/services/merge_requests/assign_issues_service.rb +++ b/app/services/merge_requests/assign_issues_service.rb @@ -4,7 +4,7 @@ module MergeRequests @assignable_issues ||= begin if current_user == merge_request.author closes_issues.select do |issue| - !issue.assignee_id? && can?(current_user, :admin_issue, issue) + !issue.is_a?(ExternalIssue) && !issue.assignee_id? && can?(current_user, :admin_issue, issue) end else [] diff --git a/spec/services/merge_requests/assign_issues_service_spec.rb b/spec/services/merge_requests/assign_issues_service_spec.rb index 7aeb95a15ea..5034b6ef33f 100644 --- a/spec/services/merge_requests/assign_issues_service_spec.rb +++ b/spec/services/merge_requests/assign_issues_service_spec.rb @@ -46,4 +46,16 @@ describe MergeRequests::AssignIssuesService, services: true do it 'assigns these to the merge request owner' do expect { service.execute }.to change { issue.reload.assignee }.to(user) end + + it 'ignores external issues' do + external_issue = ExternalIssue.new('JIRA-123', project) + service = described_class.new( + project, + user, + merge_request: merge_request, + closes_issues: [external_issue] + ) + + expect(service.assignable_issues.count).to eq 0 + end end -- cgit v1.2.1 From f956de7c3991f229967339ad04aa5464c02b3ac6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 20 Oct 2016 20:16:35 +0200 Subject: Refactor and add new functionality to CI yaml reference [ci ski] --- doc/ci/yaml/README.md | 160 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 135 insertions(+), 25 deletions(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 84ea59ab687..5c0e1c44e3f 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -146,13 +146,17 @@ variables: ``` These variables can be later used in all executed commands and scripts. - The YAML-defined variables are also set to all created service containers, -thus allowing to fine tune them. +thus allowing to fine tune them. Variables can be also defined on a +[job level](#job-variables). -Variables can be also defined on [job level](#job-variables). +Except for the user defined variables, there are also the ones set up by the +Runner itself. One example would be `CI_BUILD_REF_NAME` which has the value of +the branch or tag name for which project is built. Apart from the variables +you can set in `.gitlab-ci.yml`, there are also the so called secret variables +which can be set in GitLab's UI. -[Learn more about variables.](../variables/README.md) +[Learn more about variables.][variables] ### cache @@ -541,20 +545,29 @@ An example usage of manual actions is deployment to production. > Introduced in GitLab 8.9. -`environment` is used to define that a job deploys to a specific [environment]. -This allows easy tracking of all deployments to your environments straight from -GitLab. +> You can read more about environments and find more examples in the +[documentation about environments][environment]. +`environment` is used to define that a job deploys to a specific environment. If `environment` is specified and no environment under that name exists, a new one will be created automatically. -The `environment` name must contain only letters, digits, '-', '_', '/', '$', '{', '}' and spaces. Common -names are `qa`, `staging`, and `production`, but you can use whatever name works -with your workflow. +The `environment` name can contain: ---- +- letters +- digits +- spaces +- `-` +- `_` +- `/` +- `$` +- `{` +- `}` -**Example configurations** +Common names are `qa`, `staging`, and `production`, but you can use whatever +name works with your workflow. + +In its simplest form, the `environment` keyword can be defined like: ``` deploy to production: @@ -563,39 +576,134 @@ deploy to production: environment: production ``` -The `deploy to production` job will be marked as doing deployment to -`production` environment. +In the above example, the `deploy to production` job will be marked as doing a +deployment to the `production` environment. + +#### environment:name + +> Introduced in GitLab 8.11. + +>**Note:** +Before GitLab 8.11, the name of an environment could be defined as a string like +`environment: production`. The recommended way now is to define it under the +`name` keyword. + +Instead of defining the name of the environment right after the `environment` +keyword, it is also possible to define it as a separate value. For that, use +the `name` keyword under `environment`: + +``` +deploy to production: + stage: deploy + script: git push production HEAD:master + environment: + name: production +``` + +#### environment:url + +> Introduced in GitLab 8.11. + +>**Note:** +Before GitLab 8.11, the URL could be added only in GitLab's UI. The +recommended way now is to define it in `.gitlab-ci.yml`. + +This is an optional value that when set, it exposes buttons in various places +in GitLab which when clicked take you to the defined URL. + +In the example below, if the job finishes successfully, it will create buttons +in the merge requests and in the environments/deployments pages which will point +to `https://prod.example.com`. + +``` +deploy to production: + stage: deploy + script: git push production HEAD:master + environment: + name: production + url: https://prod.example.com +``` + +#### environment:on_stop + +> [Introduced][ce-6669] in GitLab 8.13. + +Closing (stoping) environments can be achieved with the `on_stop` keyword defined under +`environment`. It declares a different job that runs in order to close +the environment. + +Read the `environment:action` section for an example. + +#### environment:action + +> [Introduced][ce-6669] in GitLab 8.13. + +The `action` keyword is to be used in conjunction with `on_stop` and is defined +in the job that is called to close the environment. + +Take for instance: + +```yaml +review_app: + stage: deploy + script: make deploy-app + environment: + name: review + on_stop: stop_review_app + +stop_review_app: + stage: deploy + script: make delete-app + when: manual + environment: + name: review + action: stop +``` + +In the above example we set up the `review_app` job to deploy to the `review` +environment, and we also defined a new `stop_review_app` job under `on_stop`. +Once the `review_app` job is successfully finished, it will trigger the +`stop_review_app` job based on what is defined under `when`. In this case we +set it up to `manual` so it will need a [manual action](#manual-actions) via +GitLab's web interface in order to run. + +The `stop_review_app` job is **required** to have the following keywords defined: + +- `when` - [reference](#when) +- `environment:name` +- `environment:action` #### dynamic environments > [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6. `environment` can also represent a configuration hash with `name` and `url`. -These parameters can use any of the defined CI [variables](#variables) +These parameters can use any of the defined [CI variables](#variables) (including predefined, secure variables and `.gitlab-ci.yml` variables). -The common use case is to create dynamic environments for branches and use them -as review apps. - ---- - -**Example configurations** +For example: ``` deploy as review app: stage: deploy - script: ... + script: make deploy environment: name: review-apps/$CI_BUILD_REF_NAME url: https://$CI_BUILD_REF_NAME.review.example.com/ ``` The `deploy as review app` job will be marked as deployment to dynamically -create the `review-apps/branch-name` environment. +create the `review-apps/$CI_BUILD_REF_NAME` environment, which `$CI_BUILD_REF_NAME` +is an [environment variable][variables] set by the Runner. If for example the +`deploy as review app` job was run in a branch named `pow`, this environment +should be accessible under `https://pow.review.example.com/`. -This environment should be accessible under `https://branch-name.review.example.com/`. +This of course implies that the underlying server which hosts the application +is properly configured. -You can see a simple example at https://gitlab.com/gitlab-examples/review-apps-nginx/. +The common use case is to create dynamic environments for branches and use them +as Review Apps. You can see a simple example using Review Apps at +https://gitlab.com/gitlab-examples/review-apps-nginx/. ### artifacts @@ -1105,3 +1213,5 @@ CI with various languages. [examples]: ../examples/README.md [ce-6323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6323 [environment]: ../environments.md +[ce-6669]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6669 +[variables]: ../variables/README.md -- cgit v1.2.1 From 4e03f4c40602b568cffd591dcd5af6bd4b9a281e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Oct 2016 14:57:42 +0100 Subject: Fixed issues with sticky mr tabs & sidebar Closes #23504 --- app/assets/javascripts/merge_request_tabs.js | 11 +--- app/assets/stylesheets/framework/sidebar.scss | 8 +++ app/assets/stylesheets/pages/merge_requests.scss | 13 ++++- app/views/projects/merge_requests/_show.html.haml | 68 ++++++++++++----------- 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index fd21aa1fefa..1a04a037210 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -388,8 +388,7 @@ // So we dont affix the tabs on these if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return; - var tabsWidth = $tabs.outerWidth(), - $diffTabs = $('#diff-notes-app'), + var $diffTabs = $('#diff-notes-app'), offsetTop = $tabs.offset().top - ($('.navbar-fixed-top').height() + $('.layout-nav').height()); $tabs.off('affix.bs.affix affix-top.bs.affix') @@ -398,18 +397,10 @@ top: offsetTop } }).on('affix.bs.affix', function () { - $tabs.css({ - left: $tabs.offset().left, - width: tabsWidth - }); $diffTabs.css({ marginTop: $tabs.height() }); }).on('affix-top.bs.affix', function () { - $tabs.css({ - left: '', - width: '' - }); $diffTabs.css({ marginTop: '' }); diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index ec52f326eb9..1d8e64a0e4b 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -185,6 +185,10 @@ header.header-sidebar-pinned { @media (min-width: $screen-sm-min) { padding-right: $sidebar_collapsed_width; + + .merge-request-tabs-holder.affix { + right: $sidebar_collapsed_width; + } } .sidebar-collapsed-icon { @@ -207,6 +211,10 @@ header.header-sidebar-pinned { @media (min-width: $screen-md-min) { padding-right: $gutter_width; + + .merge-request-tabs-holder.affix { + right: $gutter_width; + } } &.with-overlay { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 35a1877df95..70afa568554 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -183,11 +183,11 @@ .ci-coverage { float: right; } - + .stop-env-container { color: $gl-text-color; float: right; - + a { color: $gl-text-color; } @@ -438,11 +438,18 @@ } } -.merge-request-tabs { +.merge-request-tabs-holder { background-color: #fff; &.affix { top: 100px; + left: 0; z-index: 9; + transition: right .15s; + } + + &:not(.affix) .container-fluid { + padding-left: 0; + padding-right: 0; } } diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 0e19d224fcd..f57abe73977 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -47,39 +47,41 @@ = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" - if @commits_count.nonzero? - %ul.merge-request-tabs.nav-links.no-top.no-bottom{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') } - %li.notes-tab - = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do - Discussion - %span.badge= @merge_request.mr_and_commit_notes.user.count - - if @merge_request.source_project - %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do - Commits - %span.badge= @commits_count - - if @pipeline - %li.pipelines-tab - = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do - Pipelines - %span.badge= @pipelines.size - %li.builds-tab - = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do - Builds - %span.badge= @statuses.size - %li.diffs-tab - = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do - Changes - %span.badge= @merge_request.diff_size - %li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true } - %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" } - .line-resolve-all{ "v-show" => "discussionCount > 0", - ":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" } - %span.line-resolve-btn.is-disabled{ type: "button", - ":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" } - = render "shared/icons/icon_status_success.svg" - %span.line-resolve-text - {{ resolvedDiscussionCount }}/{{ discussionCount }} {{ discussionCount | pluralize 'discussion' }} resolved - = render "discussions/jump_to_next" + .merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') } + %div{ class: container_class } + %ul.merge-request-tabs.nav-links.no-top.no-bottom + %li.notes-tab + = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do + Discussion + %span.badge= @merge_request.mr_and_commit_notes.user.count + - if @merge_request.source_project + %li.commits-tab + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do + Commits + %span.badge= @commits_count + - if @pipeline + %li.pipelines-tab + = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do + Pipelines + %span.badge= @pipelines.size + %li.builds-tab + = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do + Builds + %span.badge= @statuses.size + %li.diffs-tab + = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do + Changes + %span.badge= @merge_request.diff_size + %li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true } + %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" } + .line-resolve-all{ "v-show" => "discussionCount > 0", + ":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" } + %span.line-resolve-btn.is-disabled{ type: "button", + ":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" } + = render "shared/icons/icon_status_success.svg" + %span.line-resolve-text + {{ resolvedDiscussionCount }}/{{ discussionCount }} {{ discussionCount | pluralize 'discussion' }} resolved + = render "discussions/jump_to_next" .tab-content#diff-notes-app #notes.notes.tab-pane.voting_notes -- cgit v1.2.1 From a28371dbe33c568c970c704b90760d2b540256af Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Oct 2016 17:24:24 +0100 Subject: Fixed issue when images are loading it would push off the tabs --- app/assets/javascripts/merge_request_tabs.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 1a04a037210..9f28738e06b 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -389,12 +389,18 @@ if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return; var $diffTabs = $('#diff-notes-app'), - offsetTop = $tabs.offset().top - ($('.navbar-fixed-top').height() + $('.layout-nav').height()); + $fixedNav = $('.navbar-fixed-top'), + $layoutNav = $('.layout-nav'); $tabs.off('affix.bs.affix affix-top.bs.affix') .affix({ offset: { - top: offsetTop + top: function () { + var tabsTop = $diffTabs.offset().top - $tabs.height(); + tabsTop = tabsTop - ($fixedNav.height() + $layoutNav.height()); + + return tabsTop; + } } }).on('affix.bs.affix', function () { $diffTabs.css({ -- cgit v1.2.1 From 8c4576418be18dc6143d029f8d51645fef951655 Mon Sep 17 00:00:00 2001 From: evhoffmann Date: Thu, 20 Oct 2016 15:00:02 -0400 Subject: updated some links in definitions --- doc/university/glossary/README.md | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md index 2cca5439a5a..c7cfebb7c7c 100644 --- a/doc/university/glossary/README.md +++ b/doc/university/glossary/README.md @@ -55,7 +55,7 @@ Entry level [subscription](https://about.gitlab.com/pricing/) for GitLab EE curr ### Bitbucket -Atlassian's web hosting service for Git and Mercurial Projects i.e. GitLab.com competitor. +Atlassian's web hosting service for Git and Mercurial Projects. Read about [migrating](https://docs.gitlab.com/ce/workflow/importing/import_projects_from_bitbucket.html) from BitBucket to a GitLab instance. ### Branch @@ -152,12 +152,16 @@ The difference between two commits, or saved changes. This will also be shown vi A folder used for storing multiple files. -### Docker +### Docker Container Registry -Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in. +A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of GitLab projects. Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in. ### Dynamic Environment +### ElasticSearch + +Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data. + ### Emacs ### Fork @@ -168,6 +172,10 @@ Your [own copy](https://docs.gitlab.com/ce/workflow/forking_workflow.html) of a A code review [tool](https://www.gerritcodereview.com/) built on top of Git. +### Git Attributes + +A [git attributes file](https://git-scm.com/docs/gitattributes) is a simple text file that gives attributes to pathnames. + ### Git Hooks [Scripts](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you can use to trigger actions at certain points. @@ -178,7 +186,7 @@ A single-tenant solution that provides GitLab CE or EE as a managed service. Git ### GitHub -A web-based Git repository hosting service with an enterprise offering. Its main features are: issue tracking, pull request with code review, abundancy of integrations and wiki. As of April 2016, the service has over 14 million users. It offers free public repos, private repos and enterprise services are paid. +A web-based Git repository hosting service with an enterprise offering. Its main features are: issue tracking, pull request with code review, abundancy of integrations and wiki. It offers free public repos, private repos and enterprise services are paid. Read about [importing a project](https://docs.gitlab.com/ce/workflow/importing/import_projects_from_github.html) from GitHub to GitLab. ### GitLab CE @@ -201,7 +209,7 @@ Our free SaaS for public and private repositories. Allows you to replicate your GitLab instance to other geographical locations as a read-only fully operational version. It [can be used](https://docs.gitlab.com/ee/gitlab-geo/README.html) for cloning and fetching projects, in addition to reading any data. This will make working with large repositories over large distances much faster. ### GitLab Pages -These allow you to [create websites](https://pages.gitlab.io/) for your GitLab projects, groups, or user account. +These allow you to [create websites](https://gitlab.com/help/pages/README.md) for your GitLab projects, groups, or user account. ### Gitolite @@ -249,7 +257,7 @@ An Open Source CI tool written using the Java programming language. [Jenkins](ht ### Jira -Atlassian's [project management software](https://www.atlassian.com/software/jira), i.e. a complex issue tracker. GitLab [can be configured](https://docs.gitlab.com/ee/project_services/jira.html) to interact with JIRA Core either using an on-premises instance or the SaaS solution that Atlassian offers. +Atlassian's [project management software](https://www.atlassian.com/software/jira), i.e. a complex issue tracker. GitLab [can be configured](https://docs.gitlab.com/ee/project_services/jira.html) to interact with JIRA Core either using an on-premise instance or the SaaS solution that Atlassian offers. ### JUnit @@ -353,7 +361,7 @@ A web [server](https://www.nginx.com/resources/wiki/) (pronounced "engine x"). I ### OAuth -An open [standard](https://oauth.net/) for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. +An open standard for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. GitLab [is](https://docs.gitlab.com/ce/integration/oauth_provider.html) an OAuth2 authentication service provider. ### Omnibus Packages @@ -413,7 +421,7 @@ A popular DevOps [automation tool](https://puppet.com/product/how-puppet-works). ### Push -Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. +Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. Read about [advanced push rules](https://gitlab.com/help/pages/README.md) in GitLab. ### RE Read Only @@ -447,6 +455,10 @@ A route table contains rules (called routes) that determine where network traffi Actual build machines/containers that [run and execute tests](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner) you have specified to be run on GitLab CI. +### Sidekiq + +The background job processor GitLab [uses](https://docs.gitlab.com/ce/administration/troubleshooting/sidekiq.html) to asynchronously run tasks. + ### Software as a service (SaaS) Software that is hosted centrally and accessed on-demand (i.e. whenever you want to). This applies to GitLab.com. @@ -465,7 +477,7 @@ The board used to track the status and progress of each of the sprint backlog it ### Shell -[Terminal](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. +Terminal on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. You [use git]() and make changes to GitLab projects in your shell. You [use git](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) and make changes to GitLab projects in your shell. ### Single-tenant @@ -517,7 +529,7 @@ A program that allows you to perform superuser/administrator actions on Unix Ope ### Subversion (SVN) -An open source version control system. +An open source version control system. Read about [migrating from SVN](https://docs.gitlab.com/ce/workflow/importing/migrating_from_svn.html) to GitLab using SubGit. ### Tag @@ -559,7 +571,7 @@ A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building softwa ### Webhooks -A way for for an app to [provide](https://docs.gitlab.com/ce/web_hooks/web_hooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) +A way for for an app to [provide](https://docs.gitlab.com/ce/web_hooks/web_hooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) Read about setting up [custom git hooks](https://gitlab.com/help/administration/custom_hooks.md) for when webhooks are insufficient. ### Wiki -- cgit v1.2.1 From 1ff140ea386d856c526b4797f38b4937e9b26f80 Mon Sep 17 00:00:00 2001 From: Linus G Thiel Date: Tue, 11 Oct 2016 11:30:32 +0200 Subject: Close any open tooltips before page:fetch --- CHANGELOG.md | 1 + app/assets/javascripts/application.js | 6 +----- app/assets/javascripts/lib/utils/common_utils.js | 8 ++++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dc323e02c..646426a437c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Don't include archived projects when creating group milestones. !4940 (Jeroen Jacobs) - Add tag shortcut from the Commit page. !6543 - Keep refs for each deployment + - Close open tooltips on page navigation (Linus Thiel) - Allow browsing branches that end with '.atom' - Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller) - Replace unique keyframes mixin with keyframe mixin with specific names (ClemMakesApps) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8a61669822c..b966a568bbd 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -123,15 +123,11 @@ return str.replace(/<(?:.|\n)*?>/gm, ''); }; - window.unbindEvents = function() { - return $(document).off('scroll'); - }; - window.shiftWindow = function() { return scrollBy(0, -100); }; - document.addEventListener("page:fetch", unbindEvents); + document.addEventListener("page:fetch", gl.utils.cleanupBeforeFetch); window.addEventListener("hashchange", shiftWindow); diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index b170e26eebf..698abae6228 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -43,6 +43,14 @@ parser.href = url; return parser; }; + + gl.utils.cleanupBeforeFetch = function() { + // Unbind scroll events + $(document).off('scroll'); + // Close any open tooltips + $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy'); + }; + return jQuery.timefor = function(time, suffix, expiredLabel) { var suffixFromNow, timefor; if (!time) { -- cgit v1.2.1 From 12991f84a2ceb157b7ddec94858ccc395c767006 Mon Sep 17 00:00:00 2001 From: evhoffmann Date: Thu, 20 Oct 2016 15:47:57 -0400 Subject: added skipped definition --- doc/university/glossary/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md index c7cfebb7c7c..cf836667fac 100644 --- a/doc/university/glossary/README.md +++ b/doc/university/glossary/README.md @@ -565,6 +565,8 @@ A [virtual machine](https://en.wikipedia.org/wiki/Virtual_private_server) sold a ### VM Instance +In object-oriented programming, an [instance](http://stackoverflow.com/questions/20461907/what-is-meaning-of-instance-in-programming) is a specific realization of any object. An object may be varied in a number of ways. Each realized variation of that object is an instance. Therefore, a VM instance is an instance of a virtual machine, which is an emulation of a computer system. + ### Waterfall A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the complete software to the customer that meets all the requirements they specified. -- cgit v1.2.1 From 57046eb0abf280594d6625db3429f13d45499c83 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 19 Oct 2016 14:40:58 +0200 Subject: Ensure custom provider tab labels don't break layout. (Also fix some issues for session views on small screens.) --- app/assets/stylesheets/pages/login.scss | 34 +++++++++++++++++++++++++- app/views/devise/sessions/two_factor.html.haml | 2 +- app/views/devise/shared/_tabs_ldap.html.haml | 2 +- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index bdb13bee178..9496234c773 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -143,6 +143,7 @@ &:not(.active) { background-color: $gray-light; + border-left: 1px solid $border-color; } a { @@ -170,6 +171,31 @@ } } + // Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long). + // These styles prevent this from breaking the layout, and only applied when providers are configured. + + .new-session-tabs.custom-provider-tabs { + flex-wrap: wrap; + + li { + min-width: 85px; + flex-basis: auto; + + // This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen. + // We are making somewhat of an assumption about the configuration here: that users do not have more than + // 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any + // of them. If either condition is false, this will work as expected. If both are true, there may be a missing border + // above one of the bottom row elements. If you know a better way, please implement it! + &:nth-child(n+5) { + border-top: 1px solid $border-color; + } + } + + a { + font-size: 16px; + } + } + .form-control { &:active, &:focus { @@ -203,6 +229,7 @@ .login-page { .col-sm-5.pull-right { float: none !important; + margin-bottom: 45px; } } } @@ -244,7 +271,11 @@ } .navless-container { - padding: 65px; // height of footer + bottom padding of email confirmation link + padding: 65px 15px; // height of footer + bottom padding of email confirmation link + + @media (max-width: $screen-xs-max) { + padding: 0 15px 65px; + } } } @@ -263,3 +294,4 @@ bottom: 0; } } + diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index 0e865b807c1..fd77cdbee2e 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -10,7 +10,7 @@ = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user show-gl-field-errors' }) do |f| - resource_params = params[resource_name].presence || params = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0) - .form-group + %div = f.label 'Two-Factor Authentication code', name: :otp_attempt = f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.' %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes. diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml index a057f126c45..1e957f0935f 100644 --- a/app/views/devise/shared/_tabs_ldap.html.haml +++ b/app/views/devise/shared/_tabs_ldap.html.haml @@ -1,4 +1,4 @@ -%ul.new-session-tabs.nav-links.nav-tabs +%ul.new-session-tabs.nav-links.nav-tabs{ class: ('custom-provider-tabs' if form_based_providers.any?) } - if crowd_enabled? %li.active = link_to "Crowd", "#crowd", 'data-toggle' => 'tab' -- cgit v1.2.1 From c1be12d0fcadd2b51557d53f87aace2152a97aec Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Mon, 17 Oct 2016 19:06:56 +0000 Subject: Tidied up pipelines.js.es6 and removed jQuery where acceptable. --- app/assets/javascripts/pipelines.js.es6 | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index a7624de6089..5a16def5e35 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -2,20 +2,22 @@ class Pipelines { constructor() { - $(document).off('click', '.toggle-pipeline-btn').on('click', '.toggle-pipeline-btn', this.toggleGraph); + this.initGraphToggle(); this.addMarginToBuildColumns(); } - toggleGraph() { - const $pipelineBtn = $(this).closest('.toggle-pipeline-btn'); - const $pipelineGraph = $(this).closest('.row-content-block').next('.pipeline-graph'); - const $btnText = $(this).find('.toggle-btn-text'); - const graphCollapsed = $pipelineGraph.hasClass('graph-collapsed'); - - $($pipelineBtn).add($pipelineGraph).toggleClass('graph-collapsed'); - + initGraphToggle() { + this.toggleButton = document.querySelector('.toggle-pipeline-btn'); + this.toggleButtonText = this.toggleButton.querySelector('.toggle-btn-text'); + this.pipelineGraph = document.querySelector('.pipeline-graph'); + this.toggleButton.addEventListener('click', this.toggleGraph.bind(this)); + } - graphCollapsed ? $btnText.text('Hide') : $btnText.text('Expand') + toggleGraph() { + const graphCollapsed = this.pipelineGraph.classList.contains('graph-collapsed'); + this.toggleButton.classList.toggle('graph-collapsed'); + this.pipelineGraph.classList.toggle('graph-collapsed'); + graphCollapsed ? this.toggleButtonText.textContent = 'Hide' : this.toggleButtonText.textContent = 'Expand'; } addMarginToBuildColumns() { @@ -31,7 +33,7 @@ if ($('.build', $this).length === 1) $this.addClass('no-margin'); }); } - $('.pipeline-graph').removeClass('hidden'); + this.pipelineGraph.classList.remove('hidden'); } } -- cgit v1.2.1 From 599f1cb023b19173f861ed304f284f66b4a5396d Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 20 Oct 2016 22:18:39 +0100 Subject: converted last method to jQuery --- app/assets/javascripts/extensions/element.js.es6 | 6 ++++++ app/assets/javascripts/pipelines.js.es6 | 27 ++++++++++++------------ 2 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 app/assets/javascripts/extensions/element.js.es6 diff --git a/app/assets/javascripts/extensions/element.js.es6 b/app/assets/javascripts/extensions/element.js.es6 new file mode 100644 index 00000000000..d5d4af3573c --- /dev/null +++ b/app/assets/javascripts/extensions/element.js.es6 @@ -0,0 +1,6 @@ +Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatches; + +Element.prototype.closest = function closest(selector, selectedElement = this) { + if (!selectedElement) return; + return selectedElement.matches(selector) ? selectedElement : Element.prototype.closest(selector, selectedElement.parentElement); +}; diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index 5a16def5e35..0fa56df0d2a 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -7,9 +7,9 @@ } initGraphToggle() { + this.pipelineGraph = document.querySelector('.pipeline-graph'); this.toggleButton = document.querySelector('.toggle-pipeline-btn'); this.toggleButtonText = this.toggleButton.querySelector('.toggle-btn-text'); - this.pipelineGraph = document.querySelector('.pipeline-graph'); this.toggleButton.addEventListener('click', this.toggleGraph.bind(this)); } @@ -17,21 +17,22 @@ const graphCollapsed = this.pipelineGraph.classList.contains('graph-collapsed'); this.toggleButton.classList.toggle('graph-collapsed'); this.pipelineGraph.classList.toggle('graph-collapsed'); - graphCollapsed ? this.toggleButtonText.textContent = 'Hide' : this.toggleButtonText.textContent = 'Expand'; + this.toggleButtonText.textContent = graphCollapsed ? 'Hide' : 'Expand'; } addMarginToBuildColumns() { - const $secondChildBuildNode = $('.build:nth-child(2)'); - if ($secondChildBuildNode.length) { - const $firstChildBuildNode = $secondChildBuildNode.prev('.build'); - const $multiBuildColumn = $secondChildBuildNode.closest('.stage-column'); - const $previousColumn = $multiBuildColumn.prev('.stage-column'); - $multiBuildColumn.addClass('left-margin'); - $firstChildBuildNode.addClass('left-connector'); - $previousColumn.each(function() { - $this = $(this); - if ($('.build', $this).length === 1) $this.addClass('no-margin'); - }); + const secondChildBuildNodes = this.pipelineGraph.querySelectorAll('.build:nth-child(2)'); + for (buildNodeIndex in secondChildBuildNodes) { + const buildNode = secondChildBuildNodes[buildNodeIndex]; + const firstChildBuildNode = buildNode.previousElementSibling; + if (!firstChildBuildNode || !firstChildBuildNode.matches('.build')) continue; + const multiBuildColumn = buildNode.closest('.stage-column'); + const previousColumn = multiBuildColumn.previousElementSibling; + if (!previousColumn || !previousColumn.matches('.stage-column')) continue; + multiBuildColumn.classList.add('left-margin'); + firstChildBuildNode.classList.add('left-connector'); + const columnBuilds = previousColumn.querySelectorAll('.build'); + if (columnBuilds.length === 1) previousColumn.classList.add('no-margin'); } this.pipelineGraph.classList.remove('hidden'); } -- cgit v1.2.1 From 1c668b125e8fd3e2959d0a2bd83447f09ea7fee4 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Fri, 21 Oct 2016 09:35:17 +1100 Subject: Add hover to trash icon in notes --- CHANGELOG.md | 1 + app/views/projects/notes/_note.html.haml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dc323e02c..128f5dec039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Fix HipChat notifications rendering (airatshigapov, eisnerd) + - Add hover to trash icon in notes !7008 (blackst0ne) - Simpler arguments passed to named_route on toggle_award_url helper method ## 8.13.0 (2016-10-22) diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 73fe6a715fa..ab719e38904 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -57,7 +57,7 @@ = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do = icon('pencil', class: 'link-highlight') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do - = icon('trash-o') + = icon('trash-o', class: 'danger-highlight') .note-body{class: note_editable ? 'js-task-list-container' : ''} .note-text.md = preserve do -- cgit v1.2.1 From f87124da1f3cf48415457b2c8bdae7ce4cb573ea Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Fri, 14 Oct 2016 11:42:08 -0700 Subject: fix font weight of project feature settings --- app/assets/stylesheets/pages/projects.scss | 16 +++- app/views/projects/edit.html.haml | 116 ++++++++++++++--------------- 2 files changed, 72 insertions(+), 60 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 1062d7effb0..fe7cf3c87e3 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -13,9 +13,18 @@ .new_project, .edit-project { + fieldset { - &.features .control-label { - font-weight: normal; + + &.features { + + .label-light { + margin-bottom: 0; + } + + .help-block { + margin-top: 0; + } } .form-group { @@ -40,6 +49,7 @@ } .input-group > div { + &:last-child { padding-right: 0; } @@ -47,6 +57,7 @@ @media (max-width: $screen-xs-max) { .input-group > div { + margin-bottom: 14px; &:last-child { @@ -60,6 +71,7 @@ } .input-group-addon { + &.static-namespace { height: 35px; border-radius: 3px; diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index fb776e3a3e7..55b6580e640 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -46,70 +46,70 @@ %h5.prepend-top-0 Feature Visibility - = f.fields_for :project_feature do |feature_fields| - .form_group.prepend-top-20 - .row - .col-md-9 - = feature_fields.label :repository_access_level, "Repository", class: 'label-light' - %span.help-block Push files to be stored in this project - .col-md-3.js-repo-access-level - = project_feature_access_select(:repository_access_level) + = f.fields_for :project_feature do |feature_fields| + .form_group.prepend-top-20 + .row + .col-md-9 + = feature_fields.label :repository_access_level, "Repository", class: 'label-light' + %span.help-block Push files to be stored in this project + .col-md-3.js-repo-access-level + = project_feature_access_select(:repository_access_level) - .col-sm-12 - .row - .col-md-9.project-feature-nested - = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light' - %span.help-block Submit changes to be merged upstream - .col-md-3 - = project_feature_access_select(:merge_requests_access_level) + .col-sm-12 + .row + .col-md-9.project-feature-nested + = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light' + %span.help-block Submit changes to be merged upstream + .col-md-3 + = project_feature_access_select(:merge_requests_access_level) - .row - .col-md-9.project-feature-nested - = feature_fields.label :builds_access_level, "Builds", class: 'label-light' - %span.help-block Submit, test and deploy your changes before merge - .col-md-3 - = project_feature_access_select(:builds_access_level) + .row + .col-md-9.project-feature-nested + = feature_fields.label :builds_access_level, "Builds", class: 'label-light' + %span.help-block Submit, test and deploy your changes before merge + .col-md-3 + = project_feature_access_select(:builds_access_level) - .row - .col-md-9 - = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light' - %span.help-block Share code pastes with others out of Git repository - .col-md-3 - = project_feature_access_select(:snippets_access_level) + .row + .col-md-9 + = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light' + %span.help-block Share code pastes with others out of Git repository + .col-md-3 + = project_feature_access_select(:snippets_access_level) - .row - .col-md-9 - = feature_fields.label :issues_access_level, "Issues", class: 'label-light' - %span.help-block Lightweight issue tracking system for this project - .col-md-3 - = project_feature_access_select(:issues_access_level) + .row + .col-md-9 + = feature_fields.label :issues_access_level, "Issues", class: 'label-light' + %span.help-block Lightweight issue tracking system for this project + .col-md-3 + = project_feature_access_select(:issues_access_level) - .row - .col-md-9 - = feature_fields.label :wiki_access_level, "Wiki", class: 'label-light' - %span.help-block Pages for project documentation - .col-md-3 - = project_feature_access_select(:wiki_access_level) - - - if Gitlab.config.lfs.enabled && current_user.admin? - .checkbox - = f.label :lfs_enabled do - = f.check_box :lfs_enabled - %strong LFS - %br - %span.descr - Git Large File Storage - = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') + .row + .col-md-9 + = feature_fields.label :wiki_access_level, "Wiki", class: 'label-light' + %span.help-block Pages for project documentation + .col-md-3 + = project_feature_access_select(:wiki_access_level) - if Gitlab.config.lfs.enabled && current_user.admin? - .form-group - .checkbox - = f.label :container_registry_enabled do - = f.check_box :container_registry_enabled - %strong Container Registry - %br - %span.descr Enable Container Registry for this project - = link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank' + .checkbox + = f.label :lfs_enabled do + = f.check_box :lfs_enabled + %strong LFS + %br + %span.descr + Git Large File Storage + = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') + + - if Gitlab.config.lfs.enabled && current_user.admin? + .form-group + .checkbox + = f.label :container_registry_enabled do + = f.check_box :container_registry_enabled + %strong Container Registry + %br + %span.descr Enable Container Registry for this project + = link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank' = render 'merge_request_settings', f: f %hr @@ -288,4 +288,4 @@ Saving project. %p Please wait a moment, this page will automatically refresh when ready. -= render 'shared/confirm_modal', phrase: @project.path += render 'shared/confirm_modal', phrase: @project.path \ No newline at end of file -- cgit v1.2.1 From cf31a0f0b2b5ba6d4445e3e5c767119f9cf5953a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 21 Oct 2016 01:03:16 -0700 Subject: Disable warming of the asset cache in Spinach tests under CI I suspect some combination of Knapsack tests cause no regular Rack tests to be loaded (i.e. all JavaScript tests), which leads to the error: ArgumentError: rack-test requires a rack application, but none was given In CI, we precompile all the assets so there is no need to warm the asset cache in any case. Closes #23613 --- features/support/capybara.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/support/capybara.rb b/features/support/capybara.rb index fe9e39cf509..dae0d0f918c 100644 --- a/features/support/capybara.rb +++ b/features/support/capybara.rb @@ -20,5 +20,5 @@ unless ENV['CI'] || ENV['CI_SERVER'] end Spinach.hooks.before_run do - TestEnv.warm_asset_cache + TestEnv.warm_asset_cache unless ENV['CI'] || ENV['CI_SERVER'] end -- cgit v1.2.1 From 168197cd5a179c961301225626ac1a175f892782 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 18 Oct 2016 16:49:19 +0300 Subject: Fix project member access levels --- CHANGELOG.md | 1 + .../20161018124658_make_project_owners_masters.rb | 15 +++++++++ db/schema.rb | 2 +- .../projects/project_members_controller_spec.rb | 36 ++++++++++++++++++++++ spec/requests/api/members_spec.rb | 11 +++++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20161018124658_make_project_owners_masters.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 670404e4fce..16ca2ff93e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -137,6 +137,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix buggy iOS tooltip layering behavior. - Make guests unable to view MRs on private projects - Fix broken Project API docs (Takuya Noguchi) + - Migrate invalid project members (owner -> master) ## 8.12.7 diff --git a/db/migrate/20161018124658_make_project_owners_masters.rb b/db/migrate/20161018124658_make_project_owners_masters.rb new file mode 100644 index 00000000000..a576bb7b622 --- /dev/null +++ b/db/migrate/20161018124658_make_project_owners_masters.rb @@ -0,0 +1,15 @@ +class MakeProjectOwnersMasters < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + update_column_in_batches(:members, :access_level, 40) do |table, query| + query.where(table[:access_level].eq(50).and(table[:source_type].eq('Project'))) + end + end + + def down + # do nothing + end +end diff --git a/db/schema.rb b/db/schema.rb index a3c7fc2fd57..f5c01511195 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -843,7 +843,7 @@ ActiveRecord::Schema.define(version: 20161019213545) do t.integer "builds_access_level" t.datetime "created_at" t.datetime "updated_at" - t.integer "repository_access_level", default: 20, null: false + t.integer "repository_access_level", default: 20, null: false end add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 8519ebc1d5f..5e487241d07 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -228,4 +228,40 @@ describe Projects::ProjectMembersController do end end end + + describe 'POST create' do + let(:stranger) { create(:user) } + + context 'when creating owner' do + before do + project.team << [user, :master] + sign_in(user) + end + + it 'does not create a member' do + expect do + post :create, user_ids: stranger.id, + namespace_id: project.namespace, + access_level: Member::OWNER, + project_id: project + end.to change { project.members.count }.by(0) + end + end + + context 'when create master' do + before do + project.team << [user, :master] + sign_in(user) + end + + it 'creates a member' do + expect do + post :create, user_ids: stranger.id, + namespace_id: project.namespace, + access_level: Member::MASTER, + project_id: project + end.to change { project.members.count }.by(1) + end + end + end end diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index d22e0595788..493c0a893d1 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -328,4 +328,15 @@ describe API::Members, api: true do it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'group' do let(:source) { group } end + + context 'Adding owner to project' do + it 'returns 403' do + expect do + post api("/projects/#{project.id}/members", master), + user_id: stranger.id, access_level: Member::OWNER + + expect(response).to have_http_status(422) + end.to change { project.members.count }.by(0) + end + end end -- cgit v1.2.1 From b332931af375a29fa0ff41d45315400beca44c7a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 20 Oct 2016 21:43:24 -0700 Subject: Fix broken label uniqueness label migration The previous implementation of the migration failed on staging because the migration was attempted to remove labels from projects that did not actually have duplicates. This occurred because the SQL query did not account for the project ID when selecting the labels. To replicate the problem: 1. Disable the uniqueness validation in app/models/label.rb. 2. Create a duplicate label "bug" in project A. 3. Create the same label in project B with label "bug". The migration will attempt to remove the label in B even if there are no duplicates. Closes #23609 --- db/migrate/20161017125927_add_unique_index_to_labels.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20161017125927_add_unique_index_to_labels.rb b/db/migrate/20161017125927_add_unique_index_to_labels.rb index 16ae38612de..f2b56ebfb7b 100644 --- a/db/migrate/20161017125927_add_unique_index_to_labels.rb +++ b/db/migrate/20161017125927_add_unique_index_to_labels.rb @@ -7,9 +7,9 @@ class AddUniqueIndexToLabels < ActiveRecord::Migration disable_ddl_transaction! def up - select_all('SELECT title, COUNT(id) as cnt FROM labels GROUP BY project_id, title HAVING COUNT(id) > 1').each do |label| + select_all('SELECT title, project_id, COUNT(id) as cnt FROM labels GROUP BY project_id, title HAVING COUNT(id) > 1').each do |label| label_title = quote_string(label['title']) - duplicated_ids = select_all("SELECT id FROM labels WHERE title = '#{label_title}' ORDER BY id ASC").map{ |label| label['id'] } + duplicated_ids = select_all("SELECT id FROM labels WHERE project_id = #{label['project_id']} AND title = '#{label_title}' ORDER BY id ASC").map{ |label| label['id'] } label_id = duplicated_ids.first duplicated_ids.delete(label_id) -- cgit v1.2.1 From c81ff152e08d58c13efbd50c40dd2e083ac65083 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Fri, 21 Oct 2016 13:53:38 +0200 Subject: Change "Group#web_url" to return "/groups/twitter" rather than "/twitter". Bring back the old behaviour which was changed by 6b90ccb9. Fixes #23527. --- app/models/group.rb | 2 +- config/routes/group.rb | 33 ++++++++++++++++++--------------- spec/models/group_spec.rb | 6 ++++++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 00a595d2705..6865e610718 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -68,7 +68,7 @@ class Group < Namespace end def web_url - Gitlab::Routing.url_helpers.group_url(self) + Gitlab::Routing.url_helpers.group_canonical_url(self) end def human_name diff --git a/config/routes/group.rb b/config/routes/group.rb index 4838c9d91c6..826048ba196 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -12,23 +12,26 @@ constraints(GroupUrlConstrainer.new) do end end -resources :groups, constraints: { id: /[a-zA-Z.0-9_\-]+(? 'groups#show', as: :group_canonical end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index ac862055ebc..47f89f744cb 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -265,4 +265,10 @@ describe Group, models: true do members end + + describe '#web_url' do + it 'returns the canonical URL' do + expect(group.web_url).to include("groups/#{group.name}") + end + end end -- cgit v1.2.1 From a74dfa301e77752e333467892073811677e82c89 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 21 Oct 2016 13:43:52 +0100 Subject: Fixed compare ellipsis messing with layout --- app/views/projects/compare/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index 76b68c544aa..7bde20c3286 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -10,7 +10,7 @@ = button_tag type: 'button', class: "form-control compare-dropdown-toggle js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do .dropdown-toggle-text= params[:from] || 'Select branch/tag' = render "ref_dropdown" - .compare-ellipsis ... + .compare-ellipsis.inline ... .form-group.dropdown.compare-form-group.to.js-compare-to-dropdown .input-group.inline-input-group %span.input-group-addon to -- cgit v1.2.1 From 1a53511a3454bf70786d72e59530bff42ae160e4 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 14 Oct 2016 16:29:54 -0500 Subject: Fix object data to be sent to fetch analytics data --- app/assets/javascripts/cycle_analytics.js.es6 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/cycle_analytics.js.es6 b/app/assets/javascripts/cycle_analytics.js.es6 index bd9accacb8c..20791bab942 100644 --- a/app/assets/javascripts/cycle_analytics.js.es6 +++ b/app/assets/javascripts/cycle_analytics.js.es6 @@ -36,7 +36,11 @@ method: 'GET', dataType: 'json', contentType: 'application/json', - data: { start_date: options.startDate } + data: { + cycle_analytics: { + start_date: options.startDate + } + } }).done((data) => { this.decorateData(data); this.initDropdown(); -- cgit v1.2.1 From 6c2ab27aeaf0cd59d87e14876492a4162d48e2d7 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 19 Oct 2016 10:27:24 -0500 Subject: Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c5c96c4528..42e3df435bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Update duration at the end of pipeline - ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup - Add group level labels. (!6425) + - Fix Cycle analytics not showing correct data when filtering by date. !6906 - Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun) - Cancelled pipelines could be retried. !6927 - Updating verbiage on git basics to be more intuitive -- cgit v1.2.1 From b939529c2a2c724f1471ab3b0ec2a5dac10c913c Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Fri, 21 Oct 2016 18:05:36 +0300 Subject: Fix wrong endpoint in api/users documentation, fix same typo in spec describe blocks --- doc/api/users.md | 2 +- spec/requests/api/users_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/users.md b/doc/api/users.md index 2b12770d5a5..a50ba5432fe 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -643,7 +643,7 @@ Parameters: | `id` | integer | yes | The ID of the user | ```bash -curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/user/:id/events +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/users/:id/events ``` Example response: diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index f83f4d2c9b1..d48752473f3 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -846,7 +846,7 @@ describe API::API, api: true do end end - describe 'PUT /user/:id/block' do + describe 'PUT /users/:id/block' do before { admin } it 'blocks existing user' do put api("/users/#{user.id}/block", admin) @@ -873,7 +873,7 @@ describe API::API, api: true do end end - describe 'PUT /user/:id/unblock' do + describe 'PUT /users/:id/unblock' do let(:blocked_user) { create(:user, state: 'blocked') } before { admin } @@ -914,7 +914,7 @@ describe API::API, api: true do end end - describe 'GET /user/:id/events' do + describe 'GET /users/:id/events' do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) } -- cgit v1.2.1 From 97731760d7252acf8ee94c707c0e107492b1ef24 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 21 Oct 2016 18:13:41 +0200 Subject: Re-organize queues to use for Sidekiq Dumping too many jobs in the same queue (e.g. the "default" queue) is a dangerous setup. Jobs that take a long time to process can effectively block any other work from being performed given there are enough of these jobs. Furthermore it becomes harder to monitor the jobs as a single queue could contain jobs for different workers. In such a setup the only reliable way of getting counts per job is to iterate over all jobs in a queue, which is a rather time consuming process. By using separate queues for various workers we have better control over throughput, we can add weight to queues, and we can monitor queues better. Some workers still use the same queue whenever their work is related. For example, the various CI pipeline workers use the same "pipeline" queue. This commit includes a Rails migration that moves Sidekiq jobs from the old queues to the new ones. This migration also takes care of doing the inverse if ever needed. This does require downtime as otherwise new jobs could be scheduled in the old queues after this migration completes. This commit also includes an RSpec test that blacklists the use of the "default" queue and ensures cron workers use the "cronjob" queue. Fixes gitlab-org/gitlab-ce#23370 --- CHANGELOG.md | 1 + app/workers/admin_email_worker.rb | 3 +- app/workers/build_coverage_worker.rb | 2 +- app/workers/build_email_worker.rb | 1 + app/workers/build_finished_worker.rb | 1 + app/workers/build_hooks_worker.rb | 2 +- app/workers/build_success_worker.rb | 2 +- app/workers/clear_database_cache_worker.rb | 1 + app/workers/concerns/build_queue.rb | 8 ++ app/workers/concerns/cronjob_queue.rb | 9 ++ app/workers/concerns/dedicated_sidekiq_queue.rb | 9 ++ app/workers/concerns/pipeline_queue.rb | 8 ++ app/workers/concerns/repository_check_queue.rb | 8 ++ app/workers/delete_user_worker.rb | 1 + app/workers/email_receiver_worker.rb | 3 +- app/workers/emails_on_push_worker.rb | 2 +- app/workers/expire_build_artifacts_worker.rb | 1 + .../expire_build_instance_artifacts_worker.rb | 1 + app/workers/git_garbage_collect_worker.rb | 3 +- app/workers/gitlab_shell_worker.rb | 3 +- app/workers/group_destroy_worker.rb | 3 +- .../import_export_project_cleanup_worker.rb | 3 +- app/workers/irker_worker.rb | 1 + app/workers/merge_worker.rb | 3 +- app/workers/new_note_worker.rb | 3 +- app/workers/pipeline_hooks_worker.rb | 2 +- app/workers/pipeline_metrics_worker.rb | 3 +- app/workers/pipeline_process_worker.rb | 3 +- app/workers/pipeline_success_worker.rb | 2 +- app/workers/pipeline_update_worker.rb | 3 +- app/workers/post_receive.rb | 3 +- app/workers/project_cache_worker.rb | 3 +- app/workers/project_destroy_worker.rb | 3 +- app/workers/project_export_worker.rb | 3 +- app/workers/project_service_worker.rb | 3 +- app/workers/project_web_hook_worker.rb | 3 +- app/workers/prune_old_events_worker.rb | 1 + app/workers/remove_expired_group_links_worker.rb | 1 + app/workers/remove_expired_members_worker.rb | 1 + app/workers/repository_archive_cache_worker.rb | 3 +- app/workers/repository_check/batch_worker.rb | 21 ++-- app/workers/repository_check/clear_worker.rb | 3 +- .../repository_check/single_repository_worker.rb | 3 +- app/workers/repository_fork_worker.rb | 3 +- app/workers/repository_import_worker.rb | 3 +- app/workers/requests_profiles_worker.rb | 3 +- app/workers/stuck_ci_builds_worker.rb | 1 + app/workers/system_hook_worker.rb | 3 +- app/workers/trending_projects_worker.rb | 3 +- app/workers/update_merge_requests_worker.rb | 1 + bin/background_jobs | 3 +- config/application.rb | 3 +- config/sidekiq_queues.yml | 46 +++++++++ ...19190736_migrate_sidekiq_queues_from_default.rb | 109 +++++++++++++++++++++ doc/development/README.md | 3 +- doc/development/sidekiq_style_guide.md | 38 +++++++ spec/workers/concerns/build_queue_spec.rb | 14 +++ spec/workers/concerns/cronjob_queue_spec.rb | 18 ++++ .../concerns/dedicated_sidekiq_queue_spec.rb | 20 ++++ spec/workers/concerns/pipeline_queue_spec.rb | 14 +++ .../concerns/repository_check_queue_spec.rb | 18 ++++ spec/workers/every_sidekiq_worker_spec.rb | 44 +++++++++ 62 files changed, 425 insertions(+), 68 deletions(-) create mode 100644 app/workers/concerns/build_queue.rb create mode 100644 app/workers/concerns/cronjob_queue.rb create mode 100644 app/workers/concerns/dedicated_sidekiq_queue.rb create mode 100644 app/workers/concerns/pipeline_queue.rb create mode 100644 app/workers/concerns/repository_check_queue.rb create mode 100644 config/sidekiq_queues.yml create mode 100644 db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb create mode 100644 doc/development/sidekiq_style_guide.md create mode 100644 spec/workers/concerns/build_queue_spec.rb create mode 100644 spec/workers/concerns/cronjob_queue_spec.rb create mode 100644 spec/workers/concerns/dedicated_sidekiq_queue_spec.rb create mode 100644 spec/workers/concerns/pipeline_queue_spec.rb create mode 100644 spec/workers/concerns/repository_check_queue_spec.rb create mode 100644 spec/workers/every_sidekiq_worker_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 518d0362d07..52d435df8f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) + - All Sidekiq workers now use their own queue - Avoid race condition when asynchronously removing expired artifacts. (!6881) - Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675) - Respond with 404 Not Found for non-existent tags (Linus Thiel) diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb index 667fff031dd..c2dc955b27c 100644 --- a/app/workers/admin_email_worker.rb +++ b/app/workers/admin_email_worker.rb @@ -1,7 +1,6 @@ class AdminEmailWorker include Sidekiq::Worker - - sidekiq_options retry: false # this job auto-repeats via sidekiq-cron + include CronjobQueue def perform repository_check_failed_count = Project.where(last_repository_check_failed: true).count diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb index 0680645a8db..def0ab1dde1 100644 --- a/app/workers/build_coverage_worker.rb +++ b/app/workers/build_coverage_worker.rb @@ -1,6 +1,6 @@ class BuildCoverageWorker include Sidekiq::Worker - sidekiq_options queue: :default + include BuildQueue def perform(build_id) Ci::Build.find_by(id: build_id) diff --git a/app/workers/build_email_worker.rb b/app/workers/build_email_worker.rb index 1c7a04a66a8..5fdb1f2baa0 100644 --- a/app/workers/build_email_worker.rb +++ b/app/workers/build_email_worker.rb @@ -1,5 +1,6 @@ class BuildEmailWorker include Sidekiq::Worker + include BuildQueue def perform(build_id, recipients, push_data) recipients.each do |recipient| diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb index e7286b77ac5..466410bf08c 100644 --- a/app/workers/build_finished_worker.rb +++ b/app/workers/build_finished_worker.rb @@ -1,5 +1,6 @@ class BuildFinishedWorker include Sidekiq::Worker + include BuildQueue def perform(build_id) Ci::Build.find_by(id: build_id).try do |build| diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb index e22ececb3fd..9965af935d4 100644 --- a/app/workers/build_hooks_worker.rb +++ b/app/workers/build_hooks_worker.rb @@ -1,6 +1,6 @@ class BuildHooksWorker include Sidekiq::Worker - sidekiq_options queue: :default + include BuildQueue def perform(build_id) Ci::Build.find_by(id: build_id) diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb index 500d357ce31..e0ad5268664 100644 --- a/app/workers/build_success_worker.rb +++ b/app/workers/build_success_worker.rb @@ -1,6 +1,6 @@ class BuildSuccessWorker include Sidekiq::Worker - sidekiq_options queue: :default + include BuildQueue def perform(build_id) Ci::Build.find_by(id: build_id).try do |build| diff --git a/app/workers/clear_database_cache_worker.rb b/app/workers/clear_database_cache_worker.rb index c541daba50e..c4cb4733482 100644 --- a/app/workers/clear_database_cache_worker.rb +++ b/app/workers/clear_database_cache_worker.rb @@ -1,6 +1,7 @@ # This worker clears all cache fields in the database, working in batches. class ClearDatabaseCacheWorker include Sidekiq::Worker + include DedicatedSidekiqQueue BATCH_SIZE = 1000 diff --git a/app/workers/concerns/build_queue.rb b/app/workers/concerns/build_queue.rb new file mode 100644 index 00000000000..cf0ead40a8b --- /dev/null +++ b/app/workers/concerns/build_queue.rb @@ -0,0 +1,8 @@ +# Concern for setting Sidekiq settings for the various CI build workers. +module BuildQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: :build + end +end diff --git a/app/workers/concerns/cronjob_queue.rb b/app/workers/concerns/cronjob_queue.rb new file mode 100644 index 00000000000..e918bb011e0 --- /dev/null +++ b/app/workers/concerns/cronjob_queue.rb @@ -0,0 +1,9 @@ +# Concern that sets various Sidekiq settings for workers executed using a +# cronjob. +module CronjobQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: :cronjob, retry: false + end +end diff --git a/app/workers/concerns/dedicated_sidekiq_queue.rb b/app/workers/concerns/dedicated_sidekiq_queue.rb new file mode 100644 index 00000000000..132bae6022b --- /dev/null +++ b/app/workers/concerns/dedicated_sidekiq_queue.rb @@ -0,0 +1,9 @@ +# Concern that sets the queue of a Sidekiq worker based on the worker's class +# name/namespace. +module DedicatedSidekiqQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: name.sub(/Worker\z/, '').underscore.tr('/', '_') + end +end diff --git a/app/workers/concerns/pipeline_queue.rb b/app/workers/concerns/pipeline_queue.rb new file mode 100644 index 00000000000..ca3860e1d38 --- /dev/null +++ b/app/workers/concerns/pipeline_queue.rb @@ -0,0 +1,8 @@ +# Concern for setting Sidekiq settings for the various CI pipeline workers. +module PipelineQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: :pipeline + end +end diff --git a/app/workers/concerns/repository_check_queue.rb b/app/workers/concerns/repository_check_queue.rb new file mode 100644 index 00000000000..a597321ccf4 --- /dev/null +++ b/app/workers/concerns/repository_check_queue.rb @@ -0,0 +1,8 @@ +# Concern for setting Sidekiq settings for the various repository check workers. +module RepositoryCheckQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: :repository_check, retry: false + end +end diff --git a/app/workers/delete_user_worker.rb b/app/workers/delete_user_worker.rb index 6ff361e4d80..3194c389b3d 100644 --- a/app/workers/delete_user_worker.rb +++ b/app/workers/delete_user_worker.rb @@ -1,5 +1,6 @@ class DeleteUserWorker include Sidekiq::Worker + include DedicatedSidekiqQueue def perform(current_user_id, delete_user_id, options = {}) delete_user = User.find(delete_user_id) diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index 842eebdea9e..d3f7e479a8d 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -1,7 +1,6 @@ class EmailReceiverWorker include Sidekiq::Worker - - sidekiq_options queue: :incoming_email + include DedicatedSidekiqQueue def perform(raw) return unless Gitlab::IncomingEmail.enabled? diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb index 1dc7e0adef7..b9cd49985dc 100644 --- a/app/workers/emails_on_push_worker.rb +++ b/app/workers/emails_on_push_worker.rb @@ -1,7 +1,7 @@ class EmailsOnPushWorker include Sidekiq::Worker + include DedicatedSidekiqQueue - sidekiq_options queue: :mailers attr_reader :email, :skip_premailer def perform(project_id, recipients, push_data, options = {}) diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb index 174eabff9fd..a27585fd389 100644 --- a/app/workers/expire_build_artifacts_worker.rb +++ b/app/workers/expire_build_artifacts_worker.rb @@ -1,5 +1,6 @@ class ExpireBuildArtifactsWorker include Sidekiq::Worker + include CronjobQueue def perform Rails.logger.info 'Scheduling removal of build artifacts' diff --git a/app/workers/expire_build_instance_artifacts_worker.rb b/app/workers/expire_build_instance_artifacts_worker.rb index d9e2cc37bb3..eb403c134d1 100644 --- a/app/workers/expire_build_instance_artifacts_worker.rb +++ b/app/workers/expire_build_instance_artifacts_worker.rb @@ -1,5 +1,6 @@ class ExpireBuildInstanceArtifactsWorker include Sidekiq::Worker + include DedicatedSidekiqQueue def perform(build_id) build = Ci::Build diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb index a6cefd4d601..65f8093b5b0 100644 --- a/app/workers/git_garbage_collect_worker.rb +++ b/app/workers/git_garbage_collect_worker.rb @@ -1,8 +1,9 @@ class GitGarbageCollectWorker include Sidekiq::Worker include Gitlab::ShellAdapter + include DedicatedSidekiqQueue - sidekiq_options queue: :gitlab_shell, retry: false + sidekiq_options retry: false def perform(project_id) project = Project.find(project_id) diff --git a/app/workers/gitlab_shell_worker.rb b/app/workers/gitlab_shell_worker.rb index cfeda88bbc5..964287a1793 100644 --- a/app/workers/gitlab_shell_worker.rb +++ b/app/workers/gitlab_shell_worker.rb @@ -1,8 +1,7 @@ class GitlabShellWorker include Sidekiq::Worker include Gitlab::ShellAdapter - - sidekiq_options queue: :gitlab_shell + include DedicatedSidekiqQueue def perform(action, *arg) gitlab_shell.send(action, *arg) diff --git a/app/workers/group_destroy_worker.rb b/app/workers/group_destroy_worker.rb index 5048746f09b..a49a5fd0855 100644 --- a/app/workers/group_destroy_worker.rb +++ b/app/workers/group_destroy_worker.rb @@ -1,7 +1,6 @@ class GroupDestroyWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue def perform(group_id, user_id) begin diff --git a/app/workers/import_export_project_cleanup_worker.rb b/app/workers/import_export_project_cleanup_worker.rb index 72e3a9ae734..7957ed807ab 100644 --- a/app/workers/import_export_project_cleanup_worker.rb +++ b/app/workers/import_export_project_cleanup_worker.rb @@ -1,7 +1,6 @@ class ImportExportProjectCleanupWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include CronjobQueue def perform ImportExportCleanUpService.new.execute diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb index 19f38358eb5..7e44b241743 100644 --- a/app/workers/irker_worker.rb +++ b/app/workers/irker_worker.rb @@ -3,6 +3,7 @@ require 'socket' class IrkerWorker include Sidekiq::Worker + include DedicatedSidekiqQueue def perform(project_id, chans, colors, push_data, settings) project = Project.find(project_id) diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb index c87c0a252b1..79efca4f2f9 100644 --- a/app/workers/merge_worker.rb +++ b/app/workers/merge_worker.rb @@ -1,7 +1,6 @@ class MergeWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue def perform(merge_request_id, current_user_id, params) params = params.with_indifferent_access diff --git a/app/workers/new_note_worker.rb b/app/workers/new_note_worker.rb index 1b3232cd365..c3e62bb88c0 100644 --- a/app/workers/new_note_worker.rb +++ b/app/workers/new_note_worker.rb @@ -1,7 +1,6 @@ class NewNoteWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue def perform(note_id, note_params) note = Note.find(note_id) diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb index ab5e9f6daad..7e36eacebf8 100644 --- a/app/workers/pipeline_hooks_worker.rb +++ b/app/workers/pipeline_hooks_worker.rb @@ -1,6 +1,6 @@ class PipelineHooksWorker include Sidekiq::Worker - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb index 7bb92df3bbd..34f6ef161fb 100644 --- a/app/workers/pipeline_metrics_worker.rb +++ b/app/workers/pipeline_metrics_worker.rb @@ -1,7 +1,6 @@ class PipelineMetricsWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline| diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb index f44227d7086..357e4a9a1c3 100644 --- a/app/workers/pipeline_process_worker.rb +++ b/app/workers/pipeline_process_worker.rb @@ -1,7 +1,6 @@ class PipelineProcessWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb index 5dd443fea59..2aa6fff24da 100644 --- a/app/workers/pipeline_success_worker.rb +++ b/app/workers/pipeline_success_worker.rb @@ -1,6 +1,6 @@ class PipelineSuccessWorker include Sidekiq::Worker - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline| diff --git a/app/workers/pipeline_update_worker.rb b/app/workers/pipeline_update_worker.rb index 44a7f24e401..96c4152c674 100644 --- a/app/workers/pipeline_update_worker.rb +++ b/app/workers/pipeline_update_worker.rb @@ -1,7 +1,6 @@ class PipelineUpdateWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index a9a2b716005..eee0ca12af9 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -1,7 +1,6 @@ class PostReceive include Sidekiq::Worker - - sidekiq_options queue: :post_receive + include DedicatedSidekiqQueue def perform(repo_path, identifier, changes) if path = Gitlab.config.repositories.storages.find { |p| repo_path.start_with?(p[1].to_s) } diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb index 0d524e88dc3..71b274e0c99 100644 --- a/app/workers/project_cache_worker.rb +++ b/app/workers/project_cache_worker.rb @@ -5,8 +5,7 @@ # storage engine as much. class ProjectCacheWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue LEASE_TIMEOUT = 15.minutes.to_i diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb index 3062301a9b1..b462327490e 100644 --- a/app/workers/project_destroy_worker.rb +++ b/app/workers/project_destroy_worker.rb @@ -1,7 +1,6 @@ class ProjectDestroyWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue def perform(project_id, user_id, params) begin diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb index 615311e63f5..6009aa1b191 100644 --- a/app/workers/project_export_worker.rb +++ b/app/workers/project_export_worker.rb @@ -1,7 +1,8 @@ class ProjectExportWorker include Sidekiq::Worker + include DedicatedSidekiqQueue - sidekiq_options queue: :gitlab_shell, retry: 3 + sidekiq_options retry: 3 def perform(current_user_id, project_id) current_user = User.find(current_user_id) diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb index 64d39c4d3f7..fdfdeab7b41 100644 --- a/app/workers/project_service_worker.rb +++ b/app/workers/project_service_worker.rb @@ -1,7 +1,6 @@ class ProjectServiceWorker include Sidekiq::Worker - - sidekiq_options queue: :project_web_hook + include DedicatedSidekiqQueue def perform(hook_id, data) data = data.with_indifferent_access diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb index fb878965288..efb85eafd15 100644 --- a/app/workers/project_web_hook_worker.rb +++ b/app/workers/project_web_hook_worker.rb @@ -1,7 +1,6 @@ class ProjectWebHookWorker include Sidekiq::Worker - - sidekiq_options queue: :project_web_hook + include DedicatedSidekiqQueue def perform(hook_id, data, hook_name) data = data.with_indifferent_access diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb index 5883cafe1d1..392abb9c21b 100644 --- a/app/workers/prune_old_events_worker.rb +++ b/app/workers/prune_old_events_worker.rb @@ -1,5 +1,6 @@ class PruneOldEventsWorker include Sidekiq::Worker + include CronjobQueue def perform # Contribution calendar shows maximum 12 months of events. diff --git a/app/workers/remove_expired_group_links_worker.rb b/app/workers/remove_expired_group_links_worker.rb index 246c8b6650a..2a619f83410 100644 --- a/app/workers/remove_expired_group_links_worker.rb +++ b/app/workers/remove_expired_group_links_worker.rb @@ -1,5 +1,6 @@ class RemoveExpiredGroupLinksWorker include Sidekiq::Worker + include CronjobQueue def perform ProjectGroupLink.expired.destroy_all diff --git a/app/workers/remove_expired_members_worker.rb b/app/workers/remove_expired_members_worker.rb index cf765af97ce..31f652e5f9b 100644 --- a/app/workers/remove_expired_members_worker.rb +++ b/app/workers/remove_expired_members_worker.rb @@ -1,5 +1,6 @@ class RemoveExpiredMembersWorker include Sidekiq::Worker + include CronjobQueue def perform Member.expired.find_each do |member| diff --git a/app/workers/repository_archive_cache_worker.rb b/app/workers/repository_archive_cache_worker.rb index a2e49c61f59..e47069df189 100644 --- a/app/workers/repository_archive_cache_worker.rb +++ b/app/workers/repository_archive_cache_worker.rb @@ -1,7 +1,6 @@ class RepositoryArchiveCacheWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include CronjobQueue def perform RepositoryArchiveCleanUpService.new.execute diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb index a3e16fa5212..c3e7491ec4e 100644 --- a/app/workers/repository_check/batch_worker.rb +++ b/app/workers/repository_check/batch_worker.rb @@ -1,14 +1,13 @@ module RepositoryCheck class BatchWorker include Sidekiq::Worker - + include CronjobQueue + RUN_TIME = 3600 - - sidekiq_options retry: false - + def perform start = Time.now - + # This loop will break after a little more than one hour ('a little # more' because `git fsck` may take a few minutes), or if it runs out of # projects to check. By default sidekiq-cron will start a new @@ -17,15 +16,15 @@ module RepositoryCheck project_ids.each do |project_id| break if Time.now - start >= RUN_TIME break unless current_settings.repository_checks_enabled - + next unless try_obtain_lease(project_id) - + SingleRepositoryWorker.new.perform(project_id) end end - + private - + # Project.find_each does not support WHERE clauses and # Project.find_in_batches does not support ordering. So we just build an # array of ID's. This is OK because we do it only once an hour, because @@ -39,7 +38,7 @@ module RepositoryCheck reorder('last_repository_check_at ASC').limit(limit).pluck(:id) never_checked_projects + old_check_projects end - + def try_obtain_lease(id) # Use a 24-hour timeout because on servers/projects where 'git fsck' is # super slow we definitely do not want to run it twice in parallel. @@ -48,7 +47,7 @@ module RepositoryCheck timeout: 24.hours ).try_obtain end - + def current_settings # No caching of the settings! If we cache them and an admin disables # this feature, an active RepositoryCheckWorker would keep going for up diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb index b7202ddff34..1f1b38540ee 100644 --- a/app/workers/repository_check/clear_worker.rb +++ b/app/workers/repository_check/clear_worker.rb @@ -1,8 +1,7 @@ module RepositoryCheck class ClearWorker include Sidekiq::Worker - - sidekiq_options retry: false + include RepositoryCheckQueue def perform # Do small batched updates because these updates will be slow and locking diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb index 98ddf5d0688..3d8bfc6fc6c 100644 --- a/app/workers/repository_check/single_repository_worker.rb +++ b/app/workers/repository_check/single_repository_worker.rb @@ -1,8 +1,7 @@ module RepositoryCheck class SingleRepositoryWorker include Sidekiq::Worker - - sidekiq_options retry: false + include RepositoryCheckQueue def perform(project_id) project = Project.find(project_id) diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index 61ed1c38ac4..efc99ec962a 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -1,8 +1,7 @@ class RepositoryForkWorker include Sidekiq::Worker include Gitlab::ShellAdapter - - sidekiq_options queue: :gitlab_shell + include DedicatedSidekiqQueue def perform(project_id, forked_from_repository_storage_path, source_path, target_path) Gitlab::Metrics.add_event(:fork_repository, diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index d2ca8813ab9..c8a77e21c12 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -1,8 +1,7 @@ class RepositoryImportWorker include Sidekiq::Worker include Gitlab::ShellAdapter - - sidekiq_options queue: :gitlab_shell + include DedicatedSidekiqQueue attr_accessor :project, :current_user diff --git a/app/workers/requests_profiles_worker.rb b/app/workers/requests_profiles_worker.rb index 9dd228a2483..703b025d76e 100644 --- a/app/workers/requests_profiles_worker.rb +++ b/app/workers/requests_profiles_worker.rb @@ -1,7 +1,6 @@ class RequestsProfilesWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include CronjobQueue def perform Gitlab::RequestProfiler.remove_all_profiles diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb index 6828013b377..b70df5a1afa 100644 --- a/app/workers/stuck_ci_builds_worker.rb +++ b/app/workers/stuck_ci_builds_worker.rb @@ -1,5 +1,6 @@ class StuckCiBuildsWorker include Sidekiq::Worker + include CronjobQueue BUILD_STUCK_TIMEOUT = 1.day diff --git a/app/workers/system_hook_worker.rb b/app/workers/system_hook_worker.rb index a122c274763..baf2f12eeac 100644 --- a/app/workers/system_hook_worker.rb +++ b/app/workers/system_hook_worker.rb @@ -1,7 +1,6 @@ class SystemHookWorker include Sidekiq::Worker - - sidekiq_options queue: :system_hook + include DedicatedSidekiqQueue def perform(hook_id, data, hook_name) SystemHook.find(hook_id).execute(data, hook_name) diff --git a/app/workers/trending_projects_worker.rb b/app/workers/trending_projects_worker.rb index df4c4a6628b..0531630d13a 100644 --- a/app/workers/trending_projects_worker.rb +++ b/app/workers/trending_projects_worker.rb @@ -1,7 +1,6 @@ class TrendingProjectsWorker include Sidekiq::Worker - - sidekiq_options queue: :trending_projects + include CronjobQueue def perform Rails.logger.info('Refreshing trending projects') diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb index 03f0528cdae..acc4d858136 100644 --- a/app/workers/update_merge_requests_worker.rb +++ b/app/workers/update_merge_requests_worker.rb @@ -1,5 +1,6 @@ class UpdateMergeRequestsWorker include Sidekiq::Worker + include DedicatedSidekiqQueue def perform(project_id, user_id, oldrev, newrev, ref) project = Project.find_by(id: project_id) diff --git a/bin/background_jobs b/bin/background_jobs index 25a578a1c49..f28e2f722dc 100755 --- a/bin/background_jobs +++ b/bin/background_jobs @@ -4,6 +4,7 @@ cd $(dirname $0)/.. app_root=$(pwd) sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid" sidekiq_logfile="$app_root/log/sidekiq.log" +sidekiq_config="$app_root/config/sidekiq_queues.yml" gitlab_user=$(ls -l config.ru | awk '{print $3}') warn() @@ -37,7 +38,7 @@ start_no_deamonize() start_sidekiq() { - exec bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile "$@" + exec bundle exec sidekiq -C "${sidekiq_config}" -e $RAILS_ENV -P $sidekiq_pidfile "$@" } load_ok() diff --git a/config/application.rb b/config/application.rb index f3337b00dc6..92c8467e7f4 100644 --- a/config/application.rb +++ b/config/application.rb @@ -24,7 +24,8 @@ module Gitlab #{config.root}/app/models/ci #{config.root}/app/models/hooks #{config.root}/app/models/members - #{config.root}/app/models/project_services)) + #{config.root}/app/models/project_services + #{config.root}/app/workers/concerns)) config.generators.templates.push("#{config.root}/generator_templates") diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml new file mode 100644 index 00000000000..c2e880e891f --- /dev/null +++ b/config/sidekiq_queues.yml @@ -0,0 +1,46 @@ +# This configuration file should be exclusively used to set queue settings for +# Sidekiq. Any other setting should be specified using the Sidekiq CLI or the +# Sidekiq Ruby API (see config/initializers/sidekiq.rb). +--- +# All the queues to process and their weights. Every queue _must_ have a weight +# defined. +# +# The available weights are as follows +# +# 1: low priority +# 2: medium priority +# 3: high priority +# 5: _super_ high priority, this should only be used for _very_ important queues +# +# As per http://stackoverflow.com/a/21241357/290102 the formula for calculating +# the likelihood of a job being popped off a queue (given all queues have work +# to perform) is: +# +# chance = (queue weight / total weight of all queues) * 100 +:queues: + - [post_receive, 5] + - [merge, 5] + - [update_merge_requests, 3] + - [new_note, 2] + - [build, 2] + - [pipeline, 2] + - [gitlab_shell, 2] + - [email_receiver, 2] + - [emails_on_push, 2] + - [repository_fork, 1] + - [repository_import, 1] + - [project_service, 1] + - [clear_database_cache, 1] + - [delete_user, 1] + - [expire_build_instance_artifacts, 1] + - [group_destroy, 1] + - [irker, 1] + - [project_cache, 1] + - [project_destroy, 1] + - [project_export, 1] + - [project_web_hook, 1] + - [repository_check, 1] + - [system_hook, 1] + - [git_garbage_collect, 1] + - [cronjob, 1] + - [default, 1] diff --git a/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb b/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb new file mode 100644 index 00000000000..e875213ab96 --- /dev/null +++ b/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb @@ -0,0 +1,109 @@ +require 'json' + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class MigrateSidekiqQueuesFromDefault < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = true + + DOWNTIME_REASON = <<-EOF + Moving Sidekiq jobs from queues requires Sidekiq to be stopped. Not stopping + Sidekiq will result in the loss of jobs that are scheduled after this + migration completes. + EOF + + disable_ddl_transaction! + + # Jobs for which the queue names have been changed (e.g. multiple workers + # using the same non-default queue). + # + # The keys are the old queue names, the values the jobs to move and their new + # queue names. + RENAMED_QUEUES = { + gitlab_shell: { + 'GitGarbageCollectorWorker' => :git_garbage_collector, + 'ProjectExportWorker' => :project_export, + 'RepositoryForkWorker' => :repository_fork, + 'RepositoryImportWorker' => :repository_import + }, + project_web_hook: { + 'ProjectServiceWorker' => :project_service + }, + incoming_email: { + 'EmailReceiverWorker' => :email_receiver + }, + mailers: { + 'EmailsOnPushWorker' => :emails_on_push + }, + default: { + 'AdminEmailWorker' => :cronjob, + 'BuildCoverageWorker' => :build, + 'BuildEmailWorker' => :build, + 'BuildFinishedWorker' => :build, + 'BuildHooksWorker' => :build, + 'BuildSuccessWorker' => :build, + 'ClearDatabaseCacheWorker' => :clear_database_cache, + 'DeleteUserWorker' => :delete_user, + 'ExpireBuildArtifactsWorker' => :cronjob, + 'ExpireBuildInstanceArtifactsWorker' => :expire_build_instance_artifacts, + 'GroupDestroyWorker' => :group_destroy, + 'ImportExportProjectCleanupWorker' => :cronjob, + 'IrkerWorker' => :irker, + 'MergeWorker' => :merge, + 'NewNoteWorker' => :new_note, + 'PipelineHooksWorker' => :pipeline, + 'PipelineMetricsWorker' => :pipeline, + 'PipelineProcessWorker' => :pipeline, + 'PipelineSuccessWorker' => :pipeline, + 'PipelineUpdateWorker' => :pipeline, + 'ProjectCacheWorker' => :project_cache, + 'ProjectDestroyWorker' => :project_destroy, + 'PruneOldEventsWorker' => :cronjob, + 'RemoveExpiredGroupLinksWorker' => :cronjob, + 'RemoveExpiredMembersWorker' => :cronjob, + 'RepositoryArchiveCacheWorker' => :cronjob, + 'RepositoryCheck::BatchWorker' => :cronjob, + 'RepositoryCheck::ClearWorker' => :repository_check, + 'RepositoryCheck::SingleRepositoryWorker' => :repository_check, + 'RequestsProfilesWorker' => :cronjob, + 'StuckCiBuildsWorker' => :cronjob, + 'UpdateMergeRequestsWorker' => :update_merge_requests + } + } + + def up + Sidekiq.redis do |redis| + RENAMED_QUEUES.each do |queue, jobs| + migrate_from_queue(redis, queue, jobs) + end + end + end + + def down + Sidekiq.redis do |redis| + RENAMED_QUEUES.each do |dest_queue, jobs| + jobs.each do |worker, from_queue| + migrate_from_queue(redis, from_queue, worker => dest_queue) + end + end + end + end + + def migrate_from_queue(redis, queue, job_mapping) + while job = redis.lpop("queue:#{queue}") + payload = JSON.load(job) + new_queue = job_mapping[payload['class']] + + # If we have no target queue to migrate to we're probably dealing with + # some ancient job for which the worker no longer exists. In that case + # there's no sane option we can take, other than just dropping the job. + next unless new_queue + + payload['queue'] = new_queue + + redis.lpush("queue:#{new_queue}", JSON.dump(payload)) + end + end +end diff --git a/doc/development/README.md b/doc/development/README.md index 9706cb1de7f..fb6a8a5b095 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -14,7 +14,8 @@ - [Testing standards and style guidelines](testing.md) - [UI guide](ui_guide.md) for building GitLab with existing CSS styles and elements - [Frontend guidelines](frontend.md) -- [SQL guidelines](sql.md) for SQL guidelines +- [SQL guidelines](sql.md) for working with SQL queries +- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers ## Process diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md new file mode 100644 index 00000000000..e3a20f29a09 --- /dev/null +++ b/doc/development/sidekiq_style_guide.md @@ -0,0 +1,38 @@ +# Sidekiq Style Guide + +This document outlines various guidelines that should be followed when adding or +modifying Sidekiq workers. + +## Default Queue + +Use of the "default" queue is not allowed. Every worker should use a queue that +matches the worker's purpose the closest. For example, workers that are to be +executed periodically should use the "cronjob" queue. + +A list of all available queues can be found in `config/sidekiq_queues.yml`. + +## Dedicated Queues + +Most workers should use their own queue. To ease this process a worker can +include the `DedicatedSidekiqQueue` concern as follows: + +```ruby +class ProcessSomethingWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue +end +``` + +This will set the queue name based on the class' name, minus the `Worker` +suffix. In the above example this would lead to the queue being +`process_something`. + +In some cases multiple workers do use the same queue. For example, the various +workers for updating CI pipelines all use the `pipeline` queue. Adding workers +to existing queues should be done with care, as adding more workers can lead to +slow jobs blocking work (even for different jobs) on the shared queue. + +## Tests + +Each Sidekiq worker must be tested using RSpec, just like any other class. These +tests should be placed in `spec/workers`. diff --git a/spec/workers/concerns/build_queue_spec.rb b/spec/workers/concerns/build_queue_spec.rb new file mode 100644 index 00000000000..6bf955e0be2 --- /dev/null +++ b/spec/workers/concerns/build_queue_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe BuildQueue do + let(:worker) do + Class.new do + include Sidekiq::Worker + include BuildQueue + end + end + + it 'sets the queue name of a worker' do + expect(worker.sidekiq_options['queue'].to_s).to eq('build') + end +end diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb new file mode 100644 index 00000000000..5d1336c21a6 --- /dev/null +++ b/spec/workers/concerns/cronjob_queue_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe CronjobQueue do + let(:worker) do + Class.new do + include Sidekiq::Worker + include CronjobQueue + end + end + + it 'sets the queue name of a worker' do + expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob') + end + + it 'disables retrying of failed jobs' do + expect(worker.sidekiq_options['retry']).to eq(false) + end +end diff --git a/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb b/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb new file mode 100644 index 00000000000..512baec8b7e --- /dev/null +++ b/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe DedicatedSidekiqQueue do + let(:worker) do + Class.new do + def self.name + 'Foo::Bar::DummyWorker' + end + + include Sidekiq::Worker + include DedicatedSidekiqQueue + end + end + + describe 'queue names' do + it 'sets the queue name based on the class name' do + expect(worker.sidekiq_options['queue']).to eq('foo_bar_dummy') + end + end +end diff --git a/spec/workers/concerns/pipeline_queue_spec.rb b/spec/workers/concerns/pipeline_queue_spec.rb new file mode 100644 index 00000000000..40794d0e42a --- /dev/null +++ b/spec/workers/concerns/pipeline_queue_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe PipelineQueue do + let(:worker) do + Class.new do + include Sidekiq::Worker + include PipelineQueue + end + end + + it 'sets the queue name of a worker' do + expect(worker.sidekiq_options['queue'].to_s).to eq('pipeline') + end +end diff --git a/spec/workers/concerns/repository_check_queue_spec.rb b/spec/workers/concerns/repository_check_queue_spec.rb new file mode 100644 index 00000000000..8868e969829 --- /dev/null +++ b/spec/workers/concerns/repository_check_queue_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe RepositoryCheckQueue do + let(:worker) do + Class.new do + include Sidekiq::Worker + include RepositoryCheckQueue + end + end + + it 'sets the queue name of a worker' do + expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check') + end + + it 'disables retrying of failed jobs' do + expect(worker.sidekiq_options['retry']).to eq(false) + end +end diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb new file mode 100644 index 00000000000..fc9adf47c1e --- /dev/null +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe 'Every Sidekiq worker' do + let(:workers) do + root = Rails.root.join('app', 'workers') + concerns = root.join('concerns').to_s + + workers = Dir[root.join('**', '*.rb')]. + reject { |path| path.start_with?(concerns) } + + workers.map do |path| + ns = Pathname.new(path).relative_path_from(root).to_s.gsub('.rb', '') + + ns.camelize.constantize + end + end + + it 'does not use the default queue' do + workers.each do |worker| + expect(worker.sidekiq_options['queue'].to_s).not_to eq('default') + end + end + + it 'uses the cronjob queue when the worker runs as a cronjob' do + cron_workers = Settings.cron_jobs. + map { |job_name, options| options['job_class'].constantize }. + to_set + + workers.each do |worker| + next unless cron_workers.include?(worker) + + expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob') + end + end + + it 'defines the queue in the Sidekiq configuration file' do + config = YAML.load_file(Rails.root.join('config', 'sidekiq_queues.yml').to_s) + queue_names = config[:queues].map { |(queue, _)| queue }.to_set + + workers.each do |worker| + expect(queue_names).to include(worker.sidekiq_options['queue'].to_s) + end + end +end -- cgit v1.2.1 From e32858e7734a13b820d2f1439e189c1586af29d8 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 21 Oct 2016 23:08:44 +0600 Subject: removes extra line for empty issue description --- app/assets/stylesheets/framework/common.scss | 2 ++ app/views/projects/issues/show.html.haml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 81e4e264560..0d00b6a0d9c 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -372,3 +372,5 @@ table { margin-right: -$gl-padding; border-top: 1px solid $border-color; } + +.hide-bottom-border { border-bottom: none;} diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 6f3f238a436..6defd7834bf 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -53,7 +53,7 @@ .issue-details.issuable-details - .detail-page-description.content-block + .detail-page-description.content-block{class: ('hide-bottom-border' unless @issue.description.present? )} %h2.title = markdown_field(@issue, :title) - if @issue.description.present? -- cgit v1.2.1 From af669cbea386d900ec86510f7d43de6ca4c1771e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 17 Oct 2016 18:52:14 +0200 Subject: Change the approach to check if patches apply cleanly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .gitlab-ci.yml | 2 +- lib/gitlab/ee_compat_check.rb | 260 ++++++++++++++++++++++++++++++++++++ lib/tasks/ce_to_ee_merge_check.rake | 4 - lib/tasks/ee_compat_check.rake | 4 + lib/tasks/gitlab/dev.rake | 109 ++------------- 5 files changed, 277 insertions(+), 102 deletions(-) create mode 100644 lib/gitlab/ee_compat_check.rb delete mode 100644 lib/tasks/ce_to_ee_merge_check.rake create mode 100644 lib/tasks/ee_compat_check.rake diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 76117a48730..9c4b4acbaf5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -210,7 +210,7 @@ rake brakeman: *exec rake flay: *exec license_finder: *exec rake downtime_check: *exec -rake ce_to_ee_merge_check: +rake ee_compat_check: <<: *exec only: - branches diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb new file mode 100644 index 00000000000..5e8a0c14eba --- /dev/null +++ b/lib/gitlab/ee_compat_check.rb @@ -0,0 +1,260 @@ +module Gitlab + # Checks if a set of migrations requires downtime or not. + class EeCompatCheck + EE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze + + attr_reader :ce_branch, :check_dir, :ce_repo + + def initialize(branch:, check_dir:, ce_repo: nil) + @ce_branch = branch + @check_dir = check_dir + @ce_repo = ce_repo || 'https://gitlab.com/gitlab-org/gitlab-ce.git' + end + + def check + ensure_ee_repo + delete_patches + + generate_patch(ce_branch, ce_patch_full_path) + + Dir.chdir(check_dir) do + step("In the #{check_dir} directory") + + step("Pulling latest master", %w[git pull --ff-only origin master]) + + status = catch(:halt_check) do + ce_branch_compat_check! + + delete_ee_branch_locally + + ee_branch_presence_check! + + ee_branch_compat_check! + end + + delete_ee_branch_locally + delete_patches + + if status.nil? + true + else + false + end + end + end + + private + + def ensure_ee_repo + if Dir.exist?(check_dir) + step("#{check_dir} already exists") + else + cmd = %W[git clone --branch master --single-branch --depth 1 #{EE_REPO} #{check_dir}] + step("Cloning #{EE_REPO} into #{check_dir}", cmd) + end + end + + def ce_branch_compat_check! + cmd = %W[git apply --check #{ce_patch_full_path}] + status = step("Checking if #{ce_patch_name} applies cleanly to EE/master", cmd) + + if status.zero? + puts ce_applies_cleanly_msg(ce_branch) + throw(:halt_check) + end + end + + def ee_branch_presence_check! + status = step("Fetching origin/#{ee_branch}", %W[git fetch origin #{ee_branch}]) + + unless status.zero? + puts + puts ce_branch_doesnt_apply_cleanly_and_no_ee_branch_msg + + throw(:halt_check, :ko) + end + end + + def ee_branch_compat_check! + step("Checking out origin/#{ee_branch}", %W[git checkout -b #{ee_branch} FETCH_HEAD]) + + generate_patch(ee_branch, ee_patch_full_path) + cmd = %W[git apply --check #{ee_patch_full_path}] + status = step("Checking if #{ee_patch_name} applies cleanly to EE/master", cmd) + + unless status.zero? + puts + puts ee_branch_doesnt_apply_cleanly_msg + + throw(:halt_check, :ko) + end + + puts + puts ee_applies_cleanly_msg + end + + def generate_patch(branch, filepath) + FileUtils.rm(filepath, force: true) + + depth = 0 + loop do + depth += 10 + step("Fetching origin/master", %W[git fetch origin master --depth=#{depth}]) + status = step("Finding merge base with master", %W[git merge-base FETCH_HEAD #{branch}]) + + break if status.zero? || depth > 500 + end + + raise "#{branch} is too far behind master, please rebase it!" if depth > 500 + + step("Generating the patch against master") + output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout]) + throw(:halt_check, :ko) unless status.zero? + + File.open(filepath, 'w+') { |f| f.write(output) } + throw(:halt_check, :ko) unless File.exist?(filepath) + end + + def delete_ee_branch_locally + step("Checking out origin/master", %w[git checkout master]) + step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}]) + end + + def delete_patches + step("Deleting #{ce_patch_full_path}") + FileUtils.rm(ce_patch_full_path, force: true) + + step("Deleting #{ee_patch_full_path}") + FileUtils.rm(ee_patch_full_path, force: true) + end + + def ce_patch_name + @ce_patch_name ||= "#{ce_branch}.patch" + end + + def ce_patch_full_path + @ce_patch_full_path ||= File.expand_path(ce_patch_name, check_dir) + end + + def ee_branch + @ee_branch ||= "#{ce_branch}-ee" + end + + def ee_patch_name + @ee_patch_name ||= "#{ee_branch}.patch" + end + + def ee_patch_full_path + @ee_patch_full_path ||= File.expand_path(ee_patch_name, check_dir) + end + + def step(desc, cmd = nil) + puts "\n=> #{desc}\n" + + if cmd + puts "\n$ #{cmd.join(' ')}" + command(cmd) + end + end + + def command(cmd) + output, status = Gitlab::Popen.popen(cmd) + puts output + + status + end + + def ce_applies_cleanly_msg(ce_branch) + <<-MSG.strip_heredoc + ================================================================= + 🎉 Congratulations!! 🎉 + + The #{ce_branch} branch applies cleanly to EE/master! + + Much ❤️!! + =================================================================\n + MSG + end + + def ce_branch_doesnt_apply_cleanly_and_no_ee_branch_msg + <<-MSG.strip_heredoc + ================================================================= + 💥 Oh no! 💥 + + The #{ce_branch} branch does not apply cleanly to the current + EE/master, and no #{ee_branch} branch was found in the EE repository. + + Please create a #{ee_branch} branch that includes changes from + #{ce_branch} but also specific changes than can be applied cleanly + to EE/master. + + There are different ways to create such branch: + + 1. Create a new branch based on the CE branch and rebase it on top of EE/master + + # In the EE repo + $ git fetch #{ce_repo} #{ce_branch} + $ git checkout -b #{ee_branch} FETCH_HEAD + + # You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE" commit + # before rebasing to limit the conflicts-resolving steps during the rebase + $ git fetch origin + $ git rebase origin/master + + At this point you will likely have conflicts. + Solve them, and continue/finish the rebase. + + You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE". + + 2. Create a new branch from master and cherry-pick your CE commits + + # In the EE repo + $ git fetch origin + $ git checkout -b #{ee_branch} FETCH_HEAD + $ git fetch #{ce_repo} #{ce_branch} + $ git cherry-pick SHA # Repeat for all the commits you want to pick + + You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE" commit. + + Don't forget to push your branch to #{EE_REPO}: + + # In the EE repo + $ git push origin #{ee_branch} + + You can then retry this failed build, and hopefully it should pass. + + Stay 💪 ! + =================================================================\n + MSG + end + + def ee_branch_doesnt_apply_cleanly_msg + <<-MSG.strip_heredoc + ================================================================= + 💥 Oh no! 💥 + + The #{ce_branch} does not apply cleanly to the current + EE/master, and even though a #{ee_branch} branch exists in the EE + repository, it does not apply cleanly either to EE/master! + + Please update the #{ee_branch}, push it again to #{EE_REPO}, and + retry this build. + + Stay 💪 ! + =================================================================\n + MSG + end + + def ee_applies_cleanly_msg + <<-MSG.strip_heredoc + ================================================================= + 🎉 Congratulations!! 🎉 + + The #{ee_branch} branch applies cleanly to EE/master! + + Much ❤️!! + =================================================================\n + MSG + end + end +end diff --git a/lib/tasks/ce_to_ee_merge_check.rake b/lib/tasks/ce_to_ee_merge_check.rake deleted file mode 100644 index 424e7883060..00000000000 --- a/lib/tasks/ce_to_ee_merge_check.rake +++ /dev/null @@ -1,4 +0,0 @@ -desc 'Checks if the branch would apply cleanly to EE' -task ce_to_ee_merge_check: :environment do - Rake::Task['gitlab:dev:ce_to_ee_merge_check'].invoke -end diff --git a/lib/tasks/ee_compat_check.rake b/lib/tasks/ee_compat_check.rake new file mode 100644 index 00000000000..f494fa5c5c2 --- /dev/null +++ b/lib/tasks/ee_compat_check.rake @@ -0,0 +1,4 @@ +desc 'Checks if the branch would apply cleanly to EE' +task ee_compat_check: :environment do + Rake::Task['gitlab:dev:ee_compat_check'].invoke +end diff --git a/lib/tasks/gitlab/dev.rake b/lib/tasks/gitlab/dev.rake index 47bdb2d32d2..5ee99dfc810 100644 --- a/lib/tasks/gitlab/dev.rake +++ b/lib/tasks/gitlab/dev.rake @@ -1,106 +1,21 @@ namespace :gitlab do namespace :dev do desc 'Checks if the branch would apply cleanly to EE' - task ce_to_ee_merge_check: :environment do + task ee_compat_check: :environment do return if defined?(Gitlab::License) return unless ENV['CI'] - ce_repo = ENV['CI_BUILD_REPO'] - ce_branch = ENV['CI_BUILD_REF_NAME'] - - ee_repo = 'https://gitlab.com/gitlab-org/gitlab-ee.git' - ee_branch = "#{ce_branch}-ee" - ee_dir = 'gitlab-ee-merge-check' - - puts "\n=> Cloning #{ee_repo} into #{ee_dir}\n" - `git clone #{ee_repo} #{ee_dir} --depth 1` - Dir.chdir(ee_dir) do - puts "\n => Fetching #{ce_repo}/#{ce_branch}\n" - `git fetch #{ce_repo} #{ce_branch} --depth 1` - - # Try to merge the current tested branch to EE/master... - puts "\n => Merging #{ce_repo}/#{ce_branch} into #{ee_repo}/master\n" - `git merge FETCH_HEAD` - - exit 0 if $?.success? - - # Check if the -ee branch exists... - puts "\n => Check if #{ee_repo}/#{ee_branch} exists\n" - `git rev-parse --verify #{ee_branch}` - - # The -ee doesn't exist - unless $?.success? - puts - puts <<-MSG.strip_heredoc - ================================================================= - The #{ce_branch} branch cannot be merged without conflicts to the - current EE/master, and no #{ee_branch} branch was detected in - the EE repository. - - Please create a #{ee_branch} branch that includes changes from - #{ce_branch} but also specific changes than can be applied cleanly - to EE/master. - - You can create this branch as follows: - - 1. In the EE repo: - $ git fetch origin - $ git fetch #{ce_repo} #{ce_branch} - $ git checkout -b #{ee_branch} FETCH_HEAD - $ git rebase origin/master - 2. At this point you will likely have conflicts, solve them, and - continue/finish the rebase. Note: You can squash the CE commits - before rebasing. - 3. You can squash all the original #{ce_branch} commits into a - single "Port of #{ce_branch} to EE". - 4. Push your branch to #{ee_repo}: - $ git push origin #{ee_branch} - =================================================================\n - MSG - - exit 1 - end - - # Try to merge the -ee branch to EE/master... - puts "\n => Merging #{ee_repo}/#{ee_branch} into #{ee_repo}/master\n" - `git merge #{ee_branch} master` - - # The -ee cannot be merged cleanly to EE/master... - unless $?.success? - puts - puts <<-MSG.strip_heredoc - ================================================================= - The #{ce_branch} branch cannot be merged without conflicts to - EE/master, and even though the #{ee_branch} branch exists in the EE - repository, it cannot be merged without conflicts to EE/master. - - Please update the #{ee_branch}, push it again to #{ee_repo}, and - retry this job. - =================================================================\n - MSG - - exit 2 - end - - puts "\n => Merging #{ce_repo}/#{ce_branch} into #{ee_repo}/master\n" - `git merge FETCH_HEAD` - exit 0 if $?.success? - - # The -ee can be merged cleanly to EE/master, but still - # cannot be merged cleanly to EE/master... - puts - puts <<-MSG.strip_heredoc - ================================================================= - The #{ce_branch} branch cannot be merged without conflicts to EE, and - even though the #{ee_branch} branch exists in the EE repository and - applies cleanly to EE/master, it doesn't prevent conflicts when - merging #{ce_branch} into EE. - - We may be in a complex situation here. - =================================================================\n - MSG - - exit 3 + success = + Gitlab::EeCompatCheck.new( + branch: ENV['CI_BUILD_REF_NAME'], + check_dir: File.expand_path('ee-compat-check', __dir__), + ce_repo: ENV['CI_BUILD_REPO'] + ).check + + if success + exit 0 + else + exit 1 end end end -- cgit v1.2.1 From 0ca0697a9c2501db23458aa8ef35a8b030625f93 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 21 Oct 2016 23:21:31 +0600 Subject: adds entry in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 518d0362d07..9207d98327c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method ## 8.13.0 (2016-10-22) - + - Removes extra line for empty issue description. (!7045) - Fix save button on project pipeline settings page. (!6955) - Avoid race condition when asynchronously removing expired artifacts. (!6881) - Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675) -- cgit v1.2.1 From 3a29ea9da0abd6cfd0788f6d717a08862ed6b062 Mon Sep 17 00:00:00 2001 From: Lemures Lemniscati Date: Sat, 22 Oct 2016 03:07:26 +0900 Subject: Fix documents and comments on Build API `scope`. #23146 #19131 --- CHANGELOG.md | 1 + doc/api/builds.md | 8 ++++---- lib/api/builds.rb | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc3dd569aea..fa9f4dc6091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix: Backup restore doesn't clear cache - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method + - Fix documents and comments on Build API `scope` ## 8.13.0 (2016-10-22) diff --git a/doc/api/builds.md b/doc/api/builds.md index e40f198696d..0476cac0eda 100644 --- a/doc/api/builds.md +++ b/doc/api/builds.md @@ -11,10 +11,10 @@ GET /projects/:id/builds | Attribute | Type | Required | Description | |-----------|---------|----------|---------------------| | `id` | integer | yes | The ID of a project | -| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided | +| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided | ``` -curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds" +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/builds?scope%5B0%5D=pending&scope%5B1%5D=running' ``` Example of response @@ -132,10 +132,10 @@ GET /projects/:id/repository/commits/:sha/builds |-----------|---------|----------|---------------------| | `id` | integer | yes | The ID of a project | | `sha` | string | yes | The SHA id of a commit | -| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided | +| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided | ``` -curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds" +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds?scope%5B0%5D=pending&scope%5B1%5D=running' ``` Example of response diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 52bdbcae5a8..7b00c5037f1 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -8,7 +8,7 @@ module API # # Parameters: # id (required) - The ID of a project - # scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled; + # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; # if none provided showing all builds) # Example Request: # GET /projects/:id/builds @@ -25,7 +25,7 @@ module API # Parameters: # id (required) - The ID of a project # sha (required) - The SHA id of a commit - # scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled; + # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; # if none provided showing all builds) # Example Request: # GET /projects/:id/repository/commits/:sha/builds -- cgit v1.2.1 From 68af55c3f93be79841957ff12b736e104fdc40de Mon Sep 17 00:00:00 2001 From: Daniel Klischies Date: Fri, 21 Oct 2016 18:38:11 +0000 Subject: Add a note regarding syncing the git submodule conf to CI doc. --- doc/user/project/new_ci_build_permissions_model.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index 8827b501901..608475116a1 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -254,6 +254,12 @@ test: This will make GitLab CI initialize (fetch) and update (checkout) all your submodules recursively. +If git does not use the newly added relative URLs but still uses your old URLs, +you might need to add `git submodule sync --recursive` to your `.gitlab-ci.yml`, +prior to running `git submodule update --init --recursive`. This transfers the +changes from your `.gitmodules` file into the `.git` folder, which is kept by +runners between runs. + In case your environment or your Docker image doesn't have Git installed, you have to either ask your Administrator or install the missing dependency yourself: -- cgit v1.2.1 From 94ceadb4a32a4a5128d43983206518d3159354e0 Mon Sep 17 00:00:00 2001 From: Winnie Date: Fri, 21 Oct 2016 20:18:03 +0000 Subject: Document link syntax introduced by !5586 --- doc/user/markdown.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 56e5b802a52..7a7a0b864bd 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -501,6 +501,10 @@ There are two ways to create links, inline-style and reference-style. [I'm a reference-style link][Arbitrary case-insensitive reference text] [I'm a relative reference to a repository file](LICENSE) + + [I am an absolute reference within the repository](/doc/user/markdown.md) + + [I link to the Milestones page](/../milestones) [You can use numbers for reference-style link definitions][1] @@ -518,6 +522,10 @@ There are two ways to create links, inline-style and reference-style. [I'm a relative reference to a repository file](LICENSE)[^1] +[I am an absolute reference within the repository](/doc/user/markdown.md) + +[I link to the Milestones page](/../milestones) + [You can use numbers for reference-style link definitions][1] Or leave it empty and use the [link text itself][] -- cgit v1.2.1 From 7664c76a165e4f151bec3e5f3a25b5eb1f962bb4 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 21 Oct 2016 21:40:18 +0100 Subject: Removed append logic Updated test --- app/assets/javascripts/blob/template_selector.js.es6 | 7 +------ .../javascripts/templates/issuable_template_selector.js.es6 | 12 +++++------- spec/features/projects/issuable_templates_spec.rb | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/blob/template_selector.js.es6 b/app/assets/javascripts/blob/template_selector.js.es6 index 4e309e480b0..2d5c6ade053 100644 --- a/app/assets/javascripts/blob/template_selector.js.es6 +++ b/app/assets/javascripts/blob/template_selector.js.es6 @@ -68,14 +68,10 @@ // To be implemented on the extending class // e.g. // Api.gitignoreText item.name, @requestFileSuccess.bind(@) - requestFileSuccess(file, { skipFocus, append } = {}) { + requestFileSuccess(file, { skipFocus } = {}) { const oldValue = this.editor.getValue(); let newValue = file.content; - if (append && oldValue.length && oldValue !== newValue) { - newValue = oldValue + '\n\n' + newValue; - } - this.editor.setValue(newValue, 1); if (!skipFocus) this.editor.focus(); @@ -99,4 +95,3 @@ global.TemplateSelector = TemplateSelector; })(window.gl || ( window.gl = {})); - diff --git a/app/assets/javascripts/templates/issuable_template_selector.js.es6 b/app/assets/javascripts/templates/issuable_template_selector.js.es6 index bd4e3c3d00d..fa1b79c8415 100644 --- a/app/assets/javascripts/templates/issuable_template_selector.js.es6 +++ b/app/assets/javascripts/templates/issuable_template_selector.js.es6 @@ -32,24 +32,22 @@ this.currentTemplate = currentTemplate; if (err) return; // Error handled by global AJAX error handler this.stopLoadingSpinner(); - this.setInputValueToTemplateContent(true); + this.setInputValueToTemplateContent(); }); return; } - setInputValueToTemplateContent(append) { + setInputValueToTemplateContent() { // `this.requestFileSuccess` sets the value of the description input field - // to the content of the template selected. If `append` is true, the - // template content will be appended to the previous value of the field, - // separated by a blank line if the previous value is non-empty. + // to the content of the template selected. if (this.titleInput.val() === '') { // If the title has not yet been set, focus the title input and // skip focusing the description input by setting `true` as the // `skipFocus` option to `requestFileSuccess`. - this.requestFileSuccess(this.currentTemplate, {skipFocus: true, append}); + this.requestFileSuccess(this.currentTemplate, {skipFocus: true}); this.titleInput.focus(); } else { - this.requestFileSuccess(this.currentTemplate, {skipFocus: false, append}); + this.requestFileSuccess(this.currentTemplate, {skipFocus: false}); } return; } diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb index d886909ce85..2f377312ea5 100644 --- a/spec/features/projects/issuable_templates_spec.rb +++ b/spec/features/projects/issuable_templates_spec.rb @@ -77,7 +77,7 @@ feature 'issuable templates', feature: true, js: true do scenario 'user selects "bug" template' do select_template 'bug' wait_for_ajax - preview_template("#{prior_description}\n\n#{template_content}") + preview_template("#{template_content}") save_changes end end -- cgit v1.2.1 From fadaba000a2faba191177793ff6aba5a0ecdbe24 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Fri, 21 Oct 2016 11:31:15 -0700 Subject: Add an example of how to run the backups when using docker to the docs --- doc/raketasks/backup_restore.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 26baffdf792..fc0cd1b8af2 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -30,6 +30,10 @@ Use this if you've installed GitLab from source: ``` sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ``` +If you are running GitLab within a Docker container, you can run the backup from the host: +``` +docker -t exec gitlab-rake gitlab:backup:create +``` You can specify that portions of the application data be skipped using the environment variable `SKIP`. You can skip: -- cgit v1.2.1 From d79c41e7629b6e983c4cf5c0670a1fbab37528ed Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 21 Oct 2016 22:28:39 -0700 Subject: Fix bug where e-mails were not being sent out via Sidekiq By default, ActionMailer uses the "mailers" queue, but this entry was not included in the list of queues for Sidekiq to use. For more details: * https://github.com/plataformatec/devise/wiki/How-To:-Send-devise-emails-in-background-(Resque,-Sidekiq-and-Delayed::Job) * http://guides.rubyonrails.org/active_job_basics.html --- config/sidekiq_queues.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index c2e880e891f..f36fe893fd0 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -27,6 +27,7 @@ - [gitlab_shell, 2] - [email_receiver, 2] - [emails_on_push, 2] + - [mailers, 2] - [repository_fork, 1] - [repository_import, 1] - [project_service, 1] -- cgit v1.2.1 From 0890aeb61a5378ec3bb98511de236ee01eee8711 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 22 Oct 2016 01:59:40 -0700 Subject: Fix error in generating labels Attempting to generate default set of labels would result in an error: ArgumentError: wrong number of arguments (given 1, expected 0) Closes #23649 --- CHANGELOG.md | 3 +++ lib/gitlab/issues_labels.rb | 2 +- spec/controllers/projects/labels_controller_spec.rb | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c39ddca7cf..bfc6a586ade 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ Please view this file on the master branch, on stable branches it's out of date. - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method +## 8.13.1 (unreleased) + - Fix error in generating labels + ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) diff --git a/lib/gitlab/issues_labels.rb b/lib/gitlab/issues_labels.rb index 01a2c19ab23..dbc759367eb 100644 --- a/lib/gitlab/issues_labels.rb +++ b/lib/gitlab/issues_labels.rb @@ -19,7 +19,7 @@ module Gitlab ] labels.each do |params| - ::Labels::FindOrCreateService.new(project.owner, project).execute(params) + ::Labels::FindOrCreateService.new(project.owner, project, params).execute end end end diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 622ab154493..7ba4406c1f6 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -70,4 +70,19 @@ describe Projects::LabelsController do get :index, namespace_id: project.namespace.to_param, project_id: project.to_param end end + + describe 'POST #generate' do + let(:admin) { create(:admin) } + let(:project) { create(:empty_project) } + + before do + sign_in(admin) + end + + it 'creates labels' do + post :generate, namespace_id: project.namespace.to_param, project_id: project.to_param + + expect(response.code).to eq(302) + end + end end -- cgit v1.2.1 From c0eb2cbbb4b14e3abb843a65d685bee400b5fffe Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Sat, 22 Oct 2016 12:42:19 +0100 Subject: Stop clearing the database cache on rake cache:clear --- lib/tasks/cache.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index a95a3455a4a..78ae187817a 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -29,5 +29,5 @@ namespace :cache do task all: [:db, :redis] end - task clear: 'cache:clear:all' + task clear: 'cache:clear:redis' end -- cgit v1.2.1 From e6968964870286af5ce6a1f7cf1152c057fd5c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Sat, 22 Oct 2016 13:46:23 +0200 Subject: Fix status code expectation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/controllers/projects/labels_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 7ba4406c1f6..41df63d445a 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -82,7 +82,7 @@ describe Projects::LabelsController do it 'creates labels' do post :generate, namespace_id: project.namespace.to_param, project_id: project.to_param - expect(response.code).to eq(302) + expect(response).to have_http_status(302) end end end -- cgit v1.2.1 From 3b7ca69e530d684faf89a8dc1ea48c26eed5ccb7 Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Sat, 22 Oct 2016 14:20:13 -0500 Subject: Fix sign in page Forgot your password link overlap --- CHANGELOG.md | 1 + app/assets/stylesheets/pages/login.scss | 6 ++++++ app/views/devise/sessions/_new_base.html.haml | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c39ddca7cf..d4a975e6c3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix: Backup restore doesn't clear cache - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method + - Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens ## 8.13.0 (2016-10-22) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index bdb13bee178..51048649383 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -255,6 +255,12 @@ .new_user { position: relative; padding-bottom: 35px; + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + .forgot-password { + float: none !important; + margin-top: 5px; + } + } } .move-submit-down { diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index 525e7d99d71..5fd896f6835 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -12,5 +12,5 @@ %label{for: "user_remember_me"} = f.check_box :remember_me %span Remember me - .pull-right + .pull-right.forgot-password = link_to "Forgot your password?", new_password_path(resource_name) -- cgit v1.2.1 From 2eb452402f6d9ac7dc6a5b083dab824fa9767c4d Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Sat, 22 Oct 2016 14:38:58 -0500 Subject: Add empty line before media query --- app/assets/stylesheets/pages/login.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 51048649383..02c4bbf3710 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -255,6 +255,7 @@ .new_user { position: relative; padding-bottom: 35px; + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { .forgot-password { float: none !important; -- cgit v1.2.1 From 204da00ea0dc52c6746b160fd698e07f55a7fc00 Mon Sep 17 00:00:00 2001 From: Jared Ready Date: Thu, 29 Sep 2016 20:18:46 -0500 Subject: Move spec/mailers/shared/notify.rb to spec/support --- spec/mailers/emails/builds_spec.rb | 1 - spec/mailers/emails/merge_requests_spec.rb | 1 - spec/mailers/emails/profile_spec.rb | 1 - spec/mailers/notify_spec.rb | 1 - spec/mailers/shared/notify.rb | 200 ----------------------------- spec/support/notify_shared_examples.rb | 200 +++++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 204 deletions(-) delete mode 100644 spec/mailers/shared/notify.rb create mode 100644 spec/support/notify_shared_examples.rb diff --git a/spec/mailers/emails/builds_spec.rb b/spec/mailers/emails/builds_spec.rb index 0df89938e97..d968096783c 100644 --- a/spec/mailers/emails/builds_spec.rb +++ b/spec/mailers/emails/builds_spec.rb @@ -1,6 +1,5 @@ require 'spec_helper' require 'email_spec' -require 'mailers/shared/notify' describe Notify do include EmailSpec::Matchers diff --git a/spec/mailers/emails/merge_requests_spec.rb b/spec/mailers/emails/merge_requests_spec.rb index 4d3811af254..e22858d1d8f 100644 --- a/spec/mailers/emails/merge_requests_spec.rb +++ b/spec/mailers/emails/merge_requests_spec.rb @@ -1,6 +1,5 @@ require 'spec_helper' require 'email_spec' -require 'mailers/shared/notify' describe Notify, "merge request notifications" do include EmailSpec::Matchers diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb index 781472d0c00..14bc062ef12 100644 --- a/spec/mailers/emails/profile_spec.rb +++ b/spec/mailers/emails/profile_spec.rb @@ -1,6 +1,5 @@ require 'spec_helper' require 'email_spec' -require 'mailers/shared/notify' describe Notify do include EmailSpec::Matchers diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index c8207e58e90..f5f3f58613d 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -1,6 +1,5 @@ require 'spec_helper' require 'email_spec' -require 'mailers/shared/notify' describe Notify do include EmailSpec::Helpers diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb deleted file mode 100644 index 3956d05060b..00000000000 --- a/spec/mailers/shared/notify.rb +++ /dev/null @@ -1,200 +0,0 @@ -shared_context 'gitlab email notification' do - let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name } - let(:gitlab_sender) { Gitlab.config.gitlab.email_from } - let(:gitlab_sender_reply_to) { Gitlab.config.gitlab.email_reply_to } - let(:recipient) { create(:user, email: 'recipient@example.com') } - let(:project) { create(:project) } - let(:new_user_address) { 'newguy@example.com' } - - before do - ActionMailer::Base.deliveries.clear - email = recipient.emails.create(email: "notifications@example.com") - recipient.update_attribute(:notification_email, email.email) - stub_incoming_email_setting(enabled: true, address: "reply+%{key}@#{Gitlab.config.gitlab.host}") - end -end - -shared_context 'reply-by-email is enabled with incoming address without %{key}' do - before do - stub_incoming_email_setting(enabled: true, address: "reply@#{Gitlab.config.gitlab.host}") - end -end - -shared_examples 'a multiple recipients email' do - it 'is sent to the given recipient' do - is_expected.to deliver_to recipient.notification_email - end -end - -shared_examples 'an email sent from GitLab' do - it 'is sent from GitLab' do - sender = subject.header[:from].addrs[0] - expect(sender.display_name).to eq(gitlab_sender_display_name) - expect(sender.address).to eq(gitlab_sender) - end - - it 'has a Reply-To address' do - reply_to = subject.header[:reply_to].addresses - expect(reply_to).to eq([gitlab_sender_reply_to]) - end - - context 'when custom suffix for email subject is set' do - before do - stub_config_setting(email_subject_suffix: 'A Nice Suffix') - end - - it 'ends the subject with the suffix' do - is_expected.to have_subject /\ \| A Nice Suffix$/ - end - end -end - -shared_examples 'an email that contains a header with author username' do - it 'has X-GitLab-Author header containing author\'s username' do - is_expected.to have_header 'X-GitLab-Author', user.username - end -end - -shared_examples 'an email with X-GitLab headers containing project details' do - it 'has X-GitLab-Project* headers' do - is_expected.to have_header 'X-GitLab-Project', /#{project.name}/ - is_expected.to have_header 'X-GitLab-Project-Id', /#{project.id}/ - is_expected.to have_header 'X-GitLab-Project-Path', /#{project.path_with_namespace}/ - end -end - -shared_examples 'a new thread email with reply-by-email enabled' do - let(:regex) { /\A\Z/ } - - it 'has a Message-ID header' do - is_expected.to have_header 'Message-ID', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>" - end - - it 'has a References header' do - is_expected.to have_header 'References', regex - end -end - -shared_examples 'a thread answer email with reply-by-email enabled' do - include_examples 'an email with X-GitLab headers containing project details' - let(:regex) { /\A<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}> \Z/ } - - it 'has a Message-ID header' do - is_expected.to have_header 'Message-ID', /\A<(.*)@#{Gitlab.config.gitlab.host}>\Z/ - end - - it 'has a In-Reply-To header' do - is_expected.to have_header 'In-Reply-To', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>" - end - - it 'has a References header' do - is_expected.to have_header 'References', regex - end - - it 'has a subject that begins with Re: ' do - is_expected.to have_subject /^Re: / - end -end - -shared_examples 'an email starting a new thread with reply-by-email enabled' do - include_examples 'an email with X-GitLab headers containing project details' - include_examples 'a new thread email with reply-by-email enabled' - - context 'when reply-by-email is enabled with incoming address with %{key}' do - it 'has a Reply-To header' do - is_expected.to have_header 'Reply-To', /\Z/ - end - end - - context 'when reply-by-email is enabled with incoming address without %{key}' do - include_context 'reply-by-email is enabled with incoming address without %{key}' - include_examples 'a new thread email with reply-by-email enabled' - - it 'has a Reply-To header' do - is_expected.to have_header 'Reply-To', /\Z/ - end - end -end - -shared_examples 'an answer to an existing thread with reply-by-email enabled' do - include_examples 'an email with X-GitLab headers containing project details' - include_examples 'a thread answer email with reply-by-email enabled' - - context 'when reply-by-email is enabled with incoming address with %{key}' do - it 'has a Reply-To header' do - is_expected.to have_header 'Reply-To', /\Z/ - end - end - - context 'when reply-by-email is enabled with incoming address without %{key}' do - include_context 'reply-by-email is enabled with incoming address without %{key}' - include_examples 'a thread answer email with reply-by-email enabled' - - it 'has a Reply-To header' do - is_expected.to have_header 'Reply-To', /\Z/ - end - end -end - -shared_examples 'a new user email' do - it 'is sent to the new user' do - is_expected.to deliver_to new_user_address - end - - it 'has the correct subject' do - is_expected.to have_subject /^Account was created for you$/i - end - - it 'contains the new user\'s login name' do - is_expected.to have_body_text /#{new_user_address}/ - end -end - -shared_examples 'it should have Gmail Actions links' do - it { is_expected.to have_body_text '