summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md6
-rw-r--r--PROCESS.md38
-rw-r--r--app/assets/javascripts/job.js50
-rw-r--r--app/assets/javascripts/notes/components/issue_comment_form.vue3
-rw-r--r--app/assets/stylesheets/framework/header.scss22
-rw-r--r--app/controllers/dashboard/todos_controller.rb2
-rw-r--r--app/models/concerns/avatarable.rb25
-rw-r--r--app/workers/update_merge_requests_worker.rb9
-rw-r--r--changelogs/unreleased/.yml5
-rw-r--r--changelogs/unreleased/39109-reenable-scroll-job.yml5
-rw-r--r--changelogs/unreleased/39895-cant-set-mattermost-username-channel-from-api.yml5
-rw-r--r--changelogs/unreleased/dm-avatarable-with-asset-host.yml6
-rw-r--r--changelogs/unreleased/fix-mysql-grant-check.yml5
-rw-r--r--changelogs/unreleased/fix-todos-last-page.yml5
-rw-r--r--changelogs/unreleased/winh-subgroups-api.yml5
-rw-r--r--doc/api/groups.md49
-rw-r--r--doc/api/services.md2
-rw-r--r--doc/university/glossary/README.md149
-rw-r--r--lib/api/groups.rb66
-rw-r--r--lib/api/services.rb6
-rw-r--r--lib/gitlab/github_import/client.rb6
-rw-r--r--lib/gitlab/metrics/sidekiq_middleware.rb2
-rw-r--r--lib/tasks/import.rake6
-rw-r--r--qa/.gitignore1
-rwxr-xr-xqa/bin/qa2
-rw-r--r--qa/qa.rb1
-rw-r--r--qa/qa/page/mattermost/login.rb2
-rw-r--r--qa/qa/page/mattermost/main.rb2
-rw-r--r--qa/qa/runtime/scenario.rb22
-rw-r--r--qa/qa/scenario/bootable.rb47
-rw-r--r--qa/qa/scenario/entrypoint.rb2
-rw-r--r--qa/qa/scenario/test/integration/mattermost.rb3
-rw-r--r--qa/spec/runtime/scenario_spec.rb27
-rw-r--r--qa/spec/scenario/bootable_spec.rb24
-rw-r--r--spec/controllers/dashboard/todos_controller_spec.rb4
-rw-r--r--spec/features/merge_requests/filter_by_labels_spec.rb16
-rw-r--r--spec/helpers/application_helper_spec.rb73
-rw-r--r--spec/helpers/groups_helper_spec.rb32
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js19
-rw-r--r--spec/javascripts/notes/components/issue_comment_form_spec.js19
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb27
-rw-r--r--spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb40
-rw-r--r--spec/models/concerns/avatarable_spec.rb44
-rw-r--r--spec/models/group_spec.rb10
-rw-r--r--spec/models/project_spec.rb12
-rw-r--r--spec/models/user_spec.rb11
-rw-r--r--spec/requests/api/groups_spec.rb136
-rw-r--r--spec/requests/api/services_spec.rb21
48 files changed, 795 insertions, 279 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2f13eca2caf..2482f0124bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 10.1.2 (2017-11-08)
+
+- [SECURITY] Add X-Content-Type-Options header in API responses to make it more difficult to find other vulnerabilities.
+- [SECURITY] Properly translate IP addresses written in decimal, octal, or other formats in SSRF protections in project imports.
+- [FIXED] Fix TRIGGER checks for MySQL.
+
## 10.1.1 (2017-10-31)
- [FIXED] Auto Devops kubernetes default namespace is now correctly built out of gitlab project group-name. !14642 (Mircea Danila Dumitrescu)
diff --git a/PROCESS.md b/PROCESS.md
index 06963243b25..7c8db689256 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -141,21 +141,29 @@ the stable branch are:
* Fixes for security issues
* New or updated translations (as long as they do not touch application code)
-During the feature freeze all merge requests that are meant to go into the upcoming
-release should have the correct milestone assigned _and_ have the label
-~"Pick into Stable" set, so that release managers can find and pick them.
-Merge requests without a milestone and this label will
-not be merged into any stable branches.
-
-Fixes marked like this will be shipped in the next RC for that release. Once
-the final RC has been prepared ready for release on the 22nd, further fixes
-marked ~"Pick into Stable" will go into a patch for that release.
-
-If a merge request is to be picked into more than one release it will also need
-the ~"Pick into Backports" label set to remind the release manager to change
-the milestone after cherry-picking. As before, it should still have the
-~"Pick into Stable" label and the milestone of the highest release it will be
-picked into.
+During the feature freeze all merge requests that are meant to go into the
+upcoming release should have the correct milestone assigned _and_ the
+`Pick into X.Y` label where `X.Y` is equal to the milestone, so that release
+managers can find and pick them.
+Merge requests without this label will not be picked into the stable release.
+
+For example, if the upcoming release is `10.2.0` you will need to set the
+`Pick into 10.2` label.
+
+Fixes marked like this will be shipped in the next RC (before the 22nd), or the
+next patch release.
+
+If a merge request is to be picked into more than one release it will need one
+`Pick into X.Y` label per release where the merge request should be back-ported
+to.
+
+For example, if the current patch release is `10.1.1` and a regression fix needs
+to be backported down to the `9.5` release, you will need to assign it the
+`10.1` milestone and the following labels:
+
+- `Pick into 10.1`
+- `Pick into 10.0`
+- `Pick into 9.5`
### Asking for an exception
diff --git a/app/assets/javascripts/job.js b/app/assets/javascripts/job.js
index c6b5844dff6..cf8fda9a4fa 100644
--- a/app/assets/javascripts/job.js
+++ b/app/assets/javascripts/job.js
@@ -14,8 +14,8 @@ export default class Job {
this.state = this.options.logState;
this.buildStage = this.options.buildStage;
this.$document = $(document);
+ this.$window = $(window);
this.logBytes = 0;
- this.hasBeenScrolled = false;
this.updateDropdown = this.updateDropdown.bind(this);
this.$buildTrace = $('#build-trace');
@@ -54,23 +54,18 @@ export default class Job {
this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
- $(window)
+ this.$window
.off('scroll')
.on('scroll', () => {
- const contentHeight = this.$buildTraceOutput.height();
- if (contentHeight > this.windowSize) {
- // means the user did not scroll, the content was updated.
- this.windowSize = contentHeight;
- } else {
- // User scrolled
- this.hasBeenScrolled = true;
+ if (!this.isScrolledToBottom()) {
this.toggleScrollAnimation(false);
+ } else if (this.isScrolledToBottom() && !this.isLogComplete) {
+ this.toggleScrollAnimation(true);
}
-
this.scrollThrottled();
});
- $(window)
+ this.$window
.off('resize.build')
.on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
@@ -99,14 +94,14 @@ export default class Job {
// eslint-disable-next-line class-methods-use-this
canScroll() {
- return $(document).height() > $(window).height();
+ return this.$document.height() > this.$window.height();
}
toggleScroll() {
- const currentPosition = $(document).scrollTop();
- const scrollHeight = $(document).height();
+ const currentPosition = this.$document.scrollTop();
+ const scrollHeight = this.$document.height();
- const windowHeight = $(window).height();
+ const windowHeight = this.$window.height();
if (this.canScroll()) {
if (currentPosition > 0 &&
(scrollHeight - currentPosition !== windowHeight)) {
@@ -119,7 +114,7 @@ export default class Job {
this.toggleDisableButton(this.$scrollTopBtn, true);
this.toggleDisableButton(this.$scrollBottomBtn, false);
- } else if (scrollHeight - currentPosition === windowHeight) {
+ } else if (this.isScrolledToBottom()) {
// User is at the bottom of the build log.
this.toggleDisableButton(this.$scrollTopBtn, false);
@@ -131,9 +126,17 @@ export default class Job {
}
}
+ isScrolledToBottom() {
+ const currentPosition = this.$document.scrollTop();
+ const scrollHeight = this.$document.height();
+
+ const windowHeight = this.$window.height();
+ return scrollHeight - currentPosition === windowHeight;
+ }
+
// eslint-disable-next-line class-methods-use-this
scrollDown() {
- $(document).scrollTop($(document).height());
+ this.$document.scrollTop(this.$document.height());
}
scrollToBottom() {
@@ -143,7 +146,7 @@ export default class Job {
}
scrollToTop() {
- $(document).scrollTop(0);
+ this.$document.scrollTop(0);
this.hasBeenScrolled = true;
this.toggleScroll();
}
@@ -174,7 +177,7 @@ export default class Job {
this.state = log.state;
}
- this.windowSize = this.$buildTraceOutput.height();
+ this.isScrollInBottom = this.isScrolledToBottom();
if (log.append) {
this.$buildTraceOutput.append(log.html);
@@ -194,14 +197,9 @@ export default class Job {
} else {
this.$truncatedInfo.addClass('hidden');
}
+ this.isLogComplete = log.complete;
if (!log.complete) {
- if (!this.hasBeenScrolled) {
- this.toggleScrollAnimation(true);
- } else {
- this.toggleScrollAnimation(false);
- }
-
this.timeout = setTimeout(() => {
this.getBuildTrace();
}, 4000);
@@ -218,7 +216,7 @@ export default class Job {
this.$buildRefreshAnimation.remove();
})
.then(() => {
- if (!this.hasBeenScrolled) {
+ if (this.isScrollInBottom) {
this.scrollDown();
}
})
diff --git a/app/assets/javascripts/notes/components/issue_comment_form.vue b/app/assets/javascripts/notes/components/issue_comment_form.vue
index db8f85759b2..30e02554b65 100644
--- a/app/assets/javascripts/notes/components/issue_comment_form.vue
+++ b/app/assets/javascripts/notes/components/issue_comment_form.vue
@@ -357,7 +357,8 @@
@click="handleSave(true)"
v-if="canUpdateIssue"
:class="actionButtonClassNames"
- class="btn btn-comment btn-comment-and-close">
+ :disabled="isSubmitting"
+ class="btn btn-comment btn-comment-and-close js-action-button">
{{issueActionButtonTitle}}
</button>
<button
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 1cdfa904374..f0139b5f33a 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -401,10 +401,13 @@
.breadcrumbs-list {
display: -webkit-flex;
display: flex;
- flex-wrap: wrap;
margin-bottom: 0;
line-height: 16px;
+ @media (max-width: $screen-xs-max) {
+ flex-wrap: wrap;
+ }
+
> li {
display: flex;
align-items: center;
@@ -412,24 +415,35 @@
padding: 2px 0;
&:not(:last-child) {
- margin-right: 20px;
+ padding-right: 20px;
+
+ &:not(.dropdown) {
+ overflow: hidden;
+ }
}
> a {
font-size: 12px;
color: currentColor;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ flex: 0 1 auto;
}
}
}
.breadcrumb-item-text {
- @include str-truncated(128px);
text-decoration: inherit;
+
+ @media (max-width: $screen-xs-max) {
+ @include str-truncated(128px);
+ }
}
.breadcrumbs-list-angle {
position: absolute;
- right: -12px;
+ right: 7px;
top: 50%;
color: $gl-text-color-tertiary;
transform: translateY(-50%);
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 02c5857eea7..e89eaf7edda 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -76,7 +76,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def redirect_out_of_range(todos)
total_pages =
if todo_params.except(:sort, :page).empty?
- (current_user.todos_pending_count / todos.limit_value).ceil
+ (current_user.todos_pending_count.to_f / todos.limit_value).ceil
else
todos.total_pages
end
diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb
index 2ec70203710..10659030910 100644
--- a/app/models/concerns/avatarable.rb
+++ b/app/models/concerns/avatarable.rb
@@ -4,15 +4,26 @@ module Avatarable
def avatar_path(only_path: true)
return unless self[:avatar].present?
- # If only_path is true then use the relative path of avatar.
- # Otherwise use full path (including host).
asset_host = ActionController::Base.asset_host
- gitlab_host = only_path ? gitlab_config.relative_url_root : gitlab_config.url
+ use_asset_host = asset_host.present?
- # If asset_host is set then it is expected that assets are handled by a standalone host.
- # That means we do not want to get GitLab's relative_url_root option anymore.
- host = (asset_host.present? && (!respond_to?(:public?) || public?)) ? asset_host : gitlab_host
+ # Avatars for private and internal groups and projects require authentication to be viewed,
+ # which means they can only be served by Rails, on the regular GitLab host.
+ # If an asset host is configured, we need to return the fully qualified URL
+ # instead of only the avatar path, so that Rails doesn't prefix it with the asset host.
+ if use_asset_host && respond_to?(:public?) && !public?
+ use_asset_host = false
+ only_path = false
+ end
- [host, avatar.url].join
+ url_base = ""
+ if use_asset_host
+ url_base << asset_host unless only_path
+ else
+ url_base << gitlab_config.base_url unless only_path
+ url_base << gitlab_config.relative_url_root
+ end
+
+ url_base + avatar.url
end
end
diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb
index 150788ca611..89ae17cef37 100644
--- a/app/workers/update_merge_requests_worker.rb
+++ b/app/workers/update_merge_requests_worker.rb
@@ -2,10 +2,6 @@ class UpdateMergeRequestsWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
- def metrics_tags
- @metrics_tags || {}
- end
-
def perform(project_id, user_id, oldrev, newrev, ref)
project = Project.find_by(id: project_id)
return unless project
@@ -13,11 +9,6 @@ class UpdateMergeRequestsWorker
user = User.find_by(id: user_id)
return unless user
- @metrics_tags = {
- project_id: project_id,
- user_id: user_id
- }
-
MergeRequests::RefreshService.new(project, user).execute(oldrev, newrev, ref)
end
end
diff --git a/changelogs/unreleased/.yml b/changelogs/unreleased/.yml
new file mode 100644
index 00000000000..acf0bb80c72
--- /dev/null
+++ b/changelogs/unreleased/.yml
@@ -0,0 +1,5 @@
+---
+title: Remove update merge request worker tagging.
+merge_request:
+author:
+type: removed
diff --git a/changelogs/unreleased/39109-reenable-scroll-job.yml b/changelogs/unreleased/39109-reenable-scroll-job.yml
new file mode 100644
index 00000000000..a771f8f8941
--- /dev/null
+++ b/changelogs/unreleased/39109-reenable-scroll-job.yml
@@ -0,0 +1,5 @@
+---
+title: Enables scroll to bottom once user has scrolled back to bottom in job log
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/39895-cant-set-mattermost-username-channel-from-api.yml b/changelogs/unreleased/39895-cant-set-mattermost-username-channel-from-api.yml
new file mode 100644
index 00000000000..358c007387e
--- /dev/null
+++ b/changelogs/unreleased/39895-cant-set-mattermost-username-channel-from-api.yml
@@ -0,0 +1,5 @@
+---
+title: Fix acceptance of username for Mattermost service update
+merge_request: 15275
+author:
+type: fixed
diff --git a/changelogs/unreleased/dm-avatarable-with-asset-host.yml b/changelogs/unreleased/dm-avatarable-with-asset-host.yml
new file mode 100644
index 00000000000..6cf8d719afb
--- /dev/null
+++ b/changelogs/unreleased/dm-avatarable-with-asset-host.yml
@@ -0,0 +1,6 @@
+---
+title: Always return full avatar URL for private/internal groups/projects when asset
+ host is set
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-mysql-grant-check.yml b/changelogs/unreleased/fix-mysql-grant-check.yml
deleted file mode 100644
index a1c1aa67d79..00000000000
--- a/changelogs/unreleased/fix-mysql-grant-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix TRIGGER checks for MySQL
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-todos-last-page.yml b/changelogs/unreleased/fix-todos-last-page.yml
new file mode 100644
index 00000000000..efcdbb75e6e
--- /dev/null
+++ b/changelogs/unreleased/fix-todos-last-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix access to the final page of todos
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/winh-subgroups-api.yml b/changelogs/unreleased/winh-subgroups-api.yml
new file mode 100644
index 00000000000..c49e3621e9c
--- /dev/null
+++ b/changelogs/unreleased/winh-subgroups-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add /groups/:id/subgroups endpoint to API
+merge_request: 15142
+author: marbemac
+type: added
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 16db9c2f259..6a6e94195a7 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -9,13 +9,13 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `skip_groups` | array of integers | no | Skip the group IDs passes |
-| `all_available` | boolean | no | Show all the groups you have access to |
-| `search` | string | no | Return list of authorized groups matching the search criteria |
+| `skip_groups` | array of integers | no | Skip the group IDs passed |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users) |
+| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
-| `owned` | boolean | no | Limit by groups owned by the current user |
+| `owned` | boolean | no | Limit to groups owned by the current user |
```
GET /groups
@@ -80,6 +80,47 @@ You can filter by [custom attributes](custom_attributes.md) with:
GET /groups?custom_attributes[key]=value&custom_attributes[other_key]=other_value
```
+## List a groups's subgroups
+
+Get a list of visible direct subgroups in this group.
+When accessed without authentication, only public groups are returned.
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
+| `skip_groups` | array of integers | no | Skip the group IDs passed |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users) |
+| `search` | string | no | Return the list of authorized groups matching the search criteria |
+| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
+| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
+| `statistics` | boolean | no | Include group statistics (admins only) |
+| `owned` | boolean | no | Limit to groups owned by the current user |
+
+```
+GET /groups/:id/subgroups
+```
+
+```json
+[
+ {
+ "id": 1,
+ "name": "Foobar Group",
+ "path": "foo-bar",
+ "description": "An interesting group",
+ "visibility": "public",
+ "lfs_enabled": true,
+ "avatar_url": "http://gitlab.example.com/uploads/group/avatar/1/foo.jpg",
+ "web_url": "http://gitlab.example.com/groups/foo-bar",
+ "request_access_enabled": false,
+ "full_name": "Foobar Group",
+ "full_path": "foo-bar",
+ "parent_id": 123
+ }
+]
+```
+
## List a group's projects
Get a list of projects in this group. When accessed without authentication, only
diff --git a/doc/api/services.md b/doc/api/services.md
index e642ec964de..08a2bee1518 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -572,7 +572,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `token` | string | yes | The Mattermost token |
-
+| `username` | string | no | The username to use to post the message |
### Delete Mattermost slash command service
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index c6a91c8d5c2..076fbf6f710 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -23,6 +23,10 @@ A Microsoft-based [directory service](https://msdn.microsoft.com/en-us/library/b
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.
+### Amazon RDS
+
+External reference: <http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html>
+
### Application Lifecycle Management (ALM)
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.
@@ -68,7 +72,7 @@ A branch is a parallel version of a repository. This allows you to work on the r
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.
-### Job triggers
+### Job triggers (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) job triggers.
### CEPH
@@ -109,15 +113,15 @@ Atlassian's product for collaboration on documents and projects.
### Continuous Delivery
-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.
+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. [Amazon moves toward continuous delivery](https://www.youtube.com/watch?v=esEFaY0FDKc)
### Continuous Deployment
-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.
+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. [The difference between Continuous Delivery and Continuous Integration.](https://www.youtube.com/watch?v=igwFj8PPSnw)
### Continuous Integration
-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.
+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. [Thoughtworks discusses continuous integration.](https://www.thoughtworks.com/continuous-integration)
### Contributor
@@ -127,6 +131,10 @@ Term used for a person contributing to an open source project.
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 Analytics
+
+See <https://gitlab.com/gitlab-org/gitlab-ce/issues/22458>
+
### 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).
@@ -135,6 +143,10 @@ The time it takes to move from [idea to production](https://about.gitlab.com/201
Atlassian product for High Availability.
+### Dependencies
+
+As in "specify [dependencies](https://gitlab.com/gitlab-org/gitlab-ce/issues/14728) between stages."
+
### Deploy Keys
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.
@@ -151,15 +163,17 @@ The intersection of software engineering, quality assurance, and technology oper
The difference between two commits, or saved changes. This will also be shown visually after the changes.
-#### Directory
+### Directory
A folder used for storing multiple files.
### Docker Container Registry
-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.
+A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of [GitLab projects](https://about.gitlab.com/2016/05/23/gitlab-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.
-### Dynamic Environment
+### Dynamic Environment (review apps)
+
+### EC2 Instance
### ElasticSearch
@@ -167,10 +181,26 @@ Elasticsearch is a flexible, scalable and powerful search service. When [enabled
### Emacs
+External reference: <https://www.masteringemacs.org/article/mastering-key-bindings-emacs>
+
+### First Byte
+
+External reference: <https://en.wikipedia.org/wiki/Time_To_First_Byte>
+
+First Byte (sometimes referred to as time to first byte or [TTFB](https://en.wikipedia.org/wiki/Time_To_First_Byte)) measures the time between making a request and receiving the first byte of information in return. As a result, First Byte encompasses everything that is the backend as well as network transit issues. It differs from [_Speed Index_](#speed-index) mostly by frontend related issues which are included in Speed Index such as javascript loading, page rendering, and so on.
+
### Fork
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.
+### Funnel, or: TOFU, MOFU, BOFU
+
+External reference: [Blog post](https://www.weidert.com/whole_brain_marketing_blog/bid/113688/ToFu-MoFu-BoFu-Serving-Up-The-Right-Content-for-Lead-Nurturing)
+
+TOFU: top of funnel
+MOFU: middle of funnel
+BOFU: bottom of funnel
+
### Gerrit
A code review [tool](https://www.gerritcodereview.com/) built on top of Git.
@@ -183,6 +213,8 @@ A [git attributes file](https://git-scm.com/docs/gitattributes) is a simple text
[Scripts](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you can use to trigger actions at certain points.
+Difference between a [webhook](#webhooks) and a git hook: a git hook is local to its repo (usually) while a webhook is not (it can make API or http calls). So for example if you want your linter to fire before you commit, you can set that up with a git hook. If the linter fails, the commit does not go through. A git hook _can_ be configured to go beyond its repo, e.g. by having it make an API call.
+
### GitHost.io
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.
@@ -211,9 +243,20 @@ 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 High Availability
+
+### GitLab Master Plan
+
+Related blog post: <https://about.gitlab.com/2016/09/13/gitlab-master-plan/>.
+
### GitLab Pages
+
These allow you to [create websites](https://gitlab.com/help/pages/README.md) for your GitLab projects, groups, or user account.
+### GitLab Runner
+
+Related project: <https://gitlab.com/gitlab-org/gitlab-runner>
+
### Gitolite
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.
@@ -226,6 +269,10 @@ A web-based hosting service for projects using Git. It was acquired by GitLab an
An open source programming [language](https://golang.org/).
+### Gogs
+
+External reference: <https://gogs.io/>
+
### 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.
@@ -256,7 +303,7 @@ A [tool](https://docs.gitlab.com/ee/integration/external-issue-tracker.html) use
### Jenkins
-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.
+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. Related [documentation](https://docs.gitlab.com/ee/integration/jenkins.html).
### Jira
@@ -290,6 +337,10 @@ GitLab [integrates](https://docs.gitlab.com/ce/administration/auth/ldap.html) wi
Allows you to synchronize the members of a GitLab group with one or more LDAP groups.
+### Lint
+
+Static code analysis for our various file types. For example, we use [scss-lint](https://github.com/brigade/scss-lint) to ensure that a consistent code styling is respected. Similar tools: rubocop / eslint.
+
### Load Balancer
A [device](https://en.wikipedia.org/wiki/Load_balancing_(computing)) that distributes network or application traffic across multiple servers.
@@ -330,6 +381,10 @@ Takes changes from one branch, and [applies them](https://git-scm.com/docs/git-m
[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.
+#### Merge Request
+
+[Takes changes](https://docs.gitlab.com/ce/gitlab-basics/add-merge-request.html) from one branch, and applies them into another branch.
+
### Meteor
A [platform](https://www.meteor.com) for building javascript apps.
@@ -350,6 +405,14 @@ A type of software license. It lets people do anything with your code with prope
A free disaster recovery [software](https://help.ubuntu.com/community/MondoMindi).
+#### Mount
+
+External reference:
+
+As stated on the [wikipedia page](https://en.wikipedia.org/wiki/Mount_(Unix)), "Mounting makes file systems, files, directories, devices and special files available for use and available to the user."
+
+For example, we have NFS servers where the _git files_ reside. In order for a worker node to "see" or "use" the git files, the NFS server needs to be _mounted_ on the worker; that is, the worker needs to know that the NFS server exists and how to connect to it. Think of it as getting a shared drive to show up in your Finder (on Mac) or Explorer (on Windows).
+
### MySQL
A relational [database](http://www.mysql.com/) owned by Oracle. Currently only supported if you are using EE.
@@ -360,7 +423,7 @@ A set of symbols that are used to organize objects of various kinds so that thes
### Nginx
-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.
+A web [server](https://www.nginx.com/resources/wiki/) (pronounced "engine x"). [It can act]((https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/nginx.md) as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache.
### OAuth
@@ -380,7 +443,11 @@ GitLab's [business model](https://about.gitlab.com/2016/07/20/gitlab-is-open-cor
### Open Source Software
-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/).
+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/). Including to providing access to the source code, open source software must comply with a number of criteria, among them free distribution and no discrimination against persons, groups, or fields of endeavor.
+
+#### Open Source Stewardship
+
+[Related blog post](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/).
### Owner
@@ -430,6 +497,8 @@ A popular DevOps [automation tool](https://puppet.com/product/how-puppet-works).
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.
+### Raketasks
+
### RE Read Only
Permissions to see a file and its contents, but not change it.
@@ -438,10 +507,24 @@ Permissions to see a file and its contents, but not change it.
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.
+### Regression
+
+A regression is something that used to work one way in the last release and then we made a **breaking change** and it no longer works the same way.
+
+_or_
+
+A regression is defined as a change that results in a negative impact on the functionality of an existing feature due to recent changes, i.e. the latest release.
+
+### Remote mirroring
+
### (Git) Repository
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."
+##### Remote repository
+
+A [repository](https://about.gitlab.com/2015/05/18/simple-words-for-a-gitlab-newbie/) that is not-on-your-machine, so it's anything that is not your computer. Usually, it is online, GitLab.com for instance. The main remote repository is usually called “Origin”.
+
### Requirements management
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.
@@ -474,7 +557,11 @@ Software that is hosted centrally and accessed on-demand (i.e. whenever you want
This term is often used by people when they mean "Version Control."
-## Scrum
+#### SCLAU
+
+Abbreviation for SQO Count [Large And Up](https://about.gitlab.com/handbook/sales/#market-segmentation). This is the number of opportunities in large and strategic organizations passed from marketing to sales.
+
+### Scrum
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.
@@ -484,7 +571,9 @@ The board used to track the status and progress of each of the sprint backlog it
### Shell
-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.
+Terminal on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. You [use git](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) 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.
+
+### Shell command runner
### Single-tenant
@@ -494,6 +583,8 @@ The tenant purchases their own copy of the software and the software can be cust
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.
+### Slash commands
+
### Slave Servers
Also known as secondary servers, these help to spread the load over multiple machines. They also provide backups when the master/primary server crashes.
@@ -502,6 +593,10 @@ Also known as secondary servers, these help to spread the load over multiple mac
Program code as typed by a computer programmer (i.e. it has not yet been compiled/translated by the computer to machine language).
+### Speed Index
+
+[Speed Index](https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/metrics/speed-index) is "the average time at which visible parts of the page are displayed".
+
### 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](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.)
@@ -542,6 +637,16 @@ An open source version control system. Read about [migrating from SVN](https://d
[Represents](https://docs.gitlab.com/ce/api/tags.html) a version of a particular branch at a moment in time.
+### Tenancy
+
+#### Multi-tenant
+
+A [multi-tenant](http://whatis.techtarget.com/definition/multi-tenancy) GitLab instance can have any number of customers - such as companies or groups of users using it. GitLab.com is an example of a multi-tenant GitLab instance.
+
+#### Single-tenant
+
+A [single-tenant](http://searchcloudapplications.techtarget.com/definition/single-tenancy) GitLab instance has only one customer - such as a company - using it. On premise GitLab instances are almost exclusively single-tenant.
+
### Tool Stack
The set of tools used in a process to achieve a common outcome (e.g. set of tools used in Application Lifecycle Management).
@@ -550,9 +655,17 @@ The set of tools used in a process to achieve a common outcome (e.g. set of tool
An open source project management and bug tracking web [application](https://trac.edgewall.org/).
+### True-Up licensing model
+
+### Ubuntu
+
### 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.
+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. Add them by using the command "git add [file path]"
+
+### Upstream repository vs. GitLab repository
+
+[External conversation](https://news.ycombinator.com/item?id=12487112)
### User
@@ -560,11 +673,11 @@ Anyone interacting with the software.
### 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.
+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](https://en.wikipedia.org/wiki/Distributed_version_control) like Git, Mercurial, Bazaar, and Darcs. If any server dies, and these systems were collaborating via it, any of the client repositories can be copied back up to the server to restore it.
### 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/).
+A [VPC](https://docs.gitlab.com/ce/university/glossary/README.html#virtual-private-cloud-vpc) is 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/).
### Virtual private server (VPS)
@@ -572,7 +685,7 @@ 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.
+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](https://cloud.google.com/compute/docs/instances/). 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
@@ -586,6 +699,10 @@ A way for for an app to [provide](https://docs.gitlab.com/ce/user/project/integr
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 area
+
+Files that have been modified but are not committed. Check them by using the command "git status".
+
### Working Tree
[Consists of files](http://stackoverflow.com/questions/3689838/difference-between-head-working-tree-index-in-git) that you are currently working on.
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 340a7cecf09..bcf2e6dae1d 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -25,24 +25,7 @@ module API
optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
end
- def present_groups(groups, options = {})
- options = options.reverse_merge(
- with: Entities::Group,
- current_user: current_user
- )
-
- groups = groups.with_statistics if options[:statistics]
- present paginate(groups), options
- end
- end
-
- resource :groups do
- include CustomAttributesEndpoints
-
- desc 'Get a groups list' do
- success Entities::Group
- end
- params do
+ params :group_list_params do
use :statistics_params
optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list'
optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
@@ -52,19 +35,47 @@ module API
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
use :pagination
end
- get do
+
+ def find_groups(params)
find_params = {
all_available: params[:all_available],
- owned: params[:owned],
- custom_attributes: params[:custom_attributes]
+ custom_attributes: params[:custom_attributes],
+ owned: params[:owned]
}
+ find_params[:parent] = find_group!(params[:id]) if params[:id]
groups = GroupsFinder.new(current_user, find_params).execute
groups = groups.search(params[:search]) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
groups = groups.reorder(params[:order_by] => params[:sort])
- present_groups groups, statistics: params[:statistics] && current_user.admin?
+ groups
+ end
+
+ def present_groups(params, groups)
+ options = {
+ with: Entities::Group,
+ current_user: current_user,
+ statistics: params[:statistics] && current_user.admin?
+ }
+
+ groups = groups.with_statistics if options[:statistics]
+ present paginate(groups), options
+ end
+ end
+
+ resource :groups do
+ include CustomAttributesEndpoints
+
+ desc 'Get a groups list' do
+ success Entities::Group
+ end
+ params do
+ use :group_list_params
+ end
+ get do
+ groups = find_groups(params)
+ present_groups params, groups
end
desc 'Create a group. Available only for users who can create groups.' do
@@ -166,6 +177,17 @@ module API
present paginate(projects), with: entity, current_user: current_user
end
+ desc 'Get a list of subgroups in this group.' do
+ success Entities::Group
+ end
+ params do
+ use :group_list_params
+ end
+ get ":id/subgroups" do
+ groups = find_groups(params)
+ present_groups params, groups
+ end
+
desc 'Transfer a project to the group namespace. Available only for admin.' do
success Entities::GroupDetail
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 6454e475036..bbcc851d07a 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -522,6 +522,12 @@ module API
name: :webhook,
type: String,
desc: 'The Mattermost webhook. e.g. http://mattermost_host/hooks/...'
+ },
+ {
+ required: false,
+ name: :username,
+ type: String,
+ desc: 'The username to use to post the message'
}
],
'teamcity' => [
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index c1c338487a7..5da9befa08e 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -129,6 +129,8 @@ module Gitlab
# whether we are running in parallel mode or not. For more information see
# `#rate_or_wait_for_rate_limit`.
def with_rate_limit
+ return yield unless rate_limiting_enabled?
+
request_count_counter.increment
raise_or_wait_for_rate_limit unless requests_remaining?
@@ -170,6 +172,10 @@ module Gitlab
octokit.rate_limit.resets_in + 5
end
+ def rate_limiting_enabled?
+ @rate_limiting_enabled ||= api_endpoint.include?('.github.com')
+ end
+
def api_endpoint
custom_api_endpoint || default_api_endpoint
end
diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb
index 55c707d5386..df4bdf16847 100644
--- a/lib/gitlab/metrics/sidekiq_middleware.rb
+++ b/lib/gitlab/metrics/sidekiq_middleware.rb
@@ -11,8 +11,6 @@ module Gitlab
# Old gitlad-shell messages don't provide enqueued_at/created_at attributes
trans.set(:sidekiq_queue_duration, Time.now.to_f - (message['enqueued_at'] || message['created_at'] || 0))
trans.run { yield }
-
- worker.metrics_tags.each { |tag, value| trans.add_tag(tag, value) } if worker.respond_to?(:metrics_tags)
rescue Exception => error # rubocop: disable Lint/RescueException
trans.add_event(:sidekiq_exception)
diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake
index 943cbe6d80c..aafbe52e5f8 100644
--- a/lib/tasks/import.rake
+++ b/lib/tasks/import.rake
@@ -101,8 +101,8 @@ end
class GithubRepos
def initialize(token, current_user, github_repo)
- @client = Octokit::Client
- .new(access_token: token, auto_paginate: true, per_page: 100)
+ @client = Gitlab::GithubImport::Client.new(token)
+ @client.octokit.auto_paginate = true
@current_user = current_user
@github_repo = github_repo
@@ -130,7 +130,7 @@ class GithubRepos
end
def repos
- @client.list_repositories
+ @client.octokit.list_repositories
end
end
diff --git a/qa/.gitignore b/qa/.gitignore
index 3fec32c8427..19ec17d0005 100644
--- a/qa/.gitignore
+++ b/qa/.gitignore
@@ -1 +1,2 @@
tmp/
+.ruby-version
diff --git a/qa/bin/qa b/qa/bin/qa
index cecdeac14db..f1704dc54e9 100755
--- a/qa/bin/qa
+++ b/qa/bin/qa
@@ -4,4 +4,4 @@ require_relative '../qa'
QA::Scenario
.const_get(ARGV.shift)
- .perform(*ARGV)
+ .launch!(*ARGV)
diff --git a/qa/qa.rb b/qa/qa.rb
index e8689a44f4d..a0d46c7c283 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -18,6 +18,7 @@ module QA
##
# Support files
#
+ autoload :Bootable, 'qa/scenario/bootable'
autoload :Actable, 'qa/scenario/actable'
autoload :Entrypoint, 'qa/scenario/entrypoint'
autoload :Template, 'qa/scenario/template'
diff --git a/qa/qa/page/mattermost/login.rb b/qa/qa/page/mattermost/login.rb
index 2001dc5b230..42ab9c6f675 100644
--- a/qa/qa/page/mattermost/login.rb
+++ b/qa/qa/page/mattermost/login.rb
@@ -3,7 +3,7 @@ module QA
module Mattermost
class Login < Page::Base
def initialize
- visit(Runtime::Scenario.mattermost + '/login')
+ visit(Runtime::Scenario.mattermost_address + '/login')
end
def sign_in_using_oauth
diff --git a/qa/qa/page/mattermost/main.rb b/qa/qa/page/mattermost/main.rb
index e636d7676f4..4b8fc28e53f 100644
--- a/qa/qa/page/mattermost/main.rb
+++ b/qa/qa/page/mattermost/main.rb
@@ -3,7 +3,7 @@ module QA
module Mattermost
class Main < Page::Base
def initialize
- visit(Runtime::Scenario.mattermost)
+ visit(Runtime::Scenario.mattermost_address)
end
end
end
diff --git a/qa/qa/runtime/scenario.rb b/qa/qa/runtime/scenario.rb
index 0c5e9787e17..7ef59046640 100644
--- a/qa/qa/runtime/scenario.rb
+++ b/qa/qa/runtime/scenario.rb
@@ -1,8 +1,28 @@
module QA
module Runtime
+ ##
+ # Singleton approach to global test scenario arguments.
+ #
module Scenario
extend self
- attr_accessor :mattermost
+
+ attr_reader :attributes
+
+ def define(attribute, value)
+ (@attributes ||= {}).store(attribute.to_sym, value)
+
+ define_singleton_method(attribute) do
+ @attributes[attribute.to_sym].tap do |value|
+ if value.to_s.empty?
+ raise ArgumentError, "Empty `#{attribute}` attribute!"
+ end
+ end
+ end
+ end
+
+ def method_missing(name, *)
+ raise ArgumentError, "Scenario attribute `#{name}` not defined!"
+ end
end
end
end
diff --git a/qa/qa/scenario/bootable.rb b/qa/qa/scenario/bootable.rb
new file mode 100644
index 00000000000..22496bcc2fc
--- /dev/null
+++ b/qa/qa/scenario/bootable.rb
@@ -0,0 +1,47 @@
+require 'optparse'
+
+module QA
+ module Scenario
+ module Bootable
+ Option = Struct.new(:name, :arg, :desc)
+
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ def launch!(argv)
+ arguments = OptionParser.new do |parser|
+ options.to_a.each do |opt|
+ parser.on(opt.arg, opt.desc) do |value|
+ Runtime::Scenario.define(opt.name, value)
+ end
+ end
+ end
+
+ arguments.parse!(argv)
+
+ if has_attributes?
+ self.perform(**Runtime::Scenario.attributes)
+ else
+ self.perform(*argv)
+ end
+ end
+
+ private
+
+ def attribute(name, arg, desc)
+ options.push(Option.new(name, arg, desc))
+ end
+
+ def options
+ @options ||= []
+ end
+
+ def has_attributes?
+ options.any?
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/entrypoint.rb b/qa/qa/scenario/entrypoint.rb
index 33cb2696f8f..8f708fe38ab 100644
--- a/qa/qa/scenario/entrypoint.rb
+++ b/qa/qa/scenario/entrypoint.rb
@@ -5,6 +5,8 @@ module QA
# including staging and on-premises installation.
#
class Entrypoint < Template
+ include Bootable
+
def self.tags(*tags)
@tags = tags
end
diff --git a/qa/qa/scenario/test/integration/mattermost.rb b/qa/qa/scenario/test/integration/mattermost.rb
index 0220da929ce..7d0702afdb1 100644
--- a/qa/qa/scenario/test/integration/mattermost.rb
+++ b/qa/qa/scenario/test/integration/mattermost.rb
@@ -10,7 +10,8 @@ module QA
tags :core, :mattermost
def perform(address, mattermost, *files)
- Runtime::Scenario.mattermost = mattermost
+ Runtime::Scenario.define(:mattermost_address, mattermost)
+
super(address, *files)
end
end
diff --git a/qa/spec/runtime/scenario_spec.rb b/qa/spec/runtime/scenario_spec.rb
new file mode 100644
index 00000000000..7009192bcc0
--- /dev/null
+++ b/qa/spec/runtime/scenario_spec.rb
@@ -0,0 +1,27 @@
+describe QA::Runtime::Scenario do
+ subject do
+ Module.new.extend(described_class)
+ end
+
+ it 'makes it possible to define global scenario attributes' do
+ subject.define(:my_attribute, 'some-value')
+ subject.define(:another_attribute, 'another-value')
+
+ expect(subject.my_attribute).to eq 'some-value'
+ expect(subject.another_attribute).to eq 'another-value'
+ expect(subject.attributes)
+ .to eq(my_attribute: 'some-value', another_attribute: 'another-value')
+ end
+
+ it 'raises error when attribute is not known' do
+ expect { subject.invalid_accessor }
+ .to raise_error ArgumentError, /invalid_accessor/
+ end
+
+ it 'raises error when attribute is empty' do
+ subject.define(:empty_attribute, '')
+
+ expect { subject.empty_attribute }
+ .to raise_error ArgumentError, /empty_attribute/
+ end
+end
diff --git a/qa/spec/scenario/bootable_spec.rb b/qa/spec/scenario/bootable_spec.rb
new file mode 100644
index 00000000000..273aac7677e
--- /dev/null
+++ b/qa/spec/scenario/bootable_spec.rb
@@ -0,0 +1,24 @@
+describe QA::Scenario::Bootable do
+ subject do
+ Class.new(QA::Scenario::Template)
+ .include(described_class)
+ end
+
+ it 'makes it possible to define the scenario attribute' do
+ subject.class_eval do
+ attribute :something, '--something SOMETHING', 'Some attribute'
+ attribute :another, '--another ANOTHER', 'Some other attribute'
+ end
+
+ expect(subject).to receive(:perform)
+ .with(something: 'test', another: 'other')
+
+ subject.launch!(%w[--another other --something test])
+ end
+
+ it 'does not require attributes to be defined' do
+ expect(subject).to receive(:perform).with('some', 'argv')
+
+ subject.launch!(%w[some argv])
+ end
+end
diff --git a/spec/controllers/dashboard/todos_controller_spec.rb b/spec/controllers/dashboard/todos_controller_spec.rb
index d862e1447e3..f9faa4fa59a 100644
--- a/spec/controllers/dashboard/todos_controller_spec.rb
+++ b/spec/controllers/dashboard/todos_controller_spec.rb
@@ -44,11 +44,11 @@ describe Dashboard::TodosController do
context 'when using pagination' do
let(:last_page) { user.todos.page.total_pages }
- let!(:issues) { create_list(:issue, 2, project: project, assignees: [user]) }
+ let!(:issues) { create_list(:issue, 3, project: project, assignees: [user]) }
before do
issues.each { |issue| todo_service.new_issue(issue, user) }
- allow(Kaminari.config).to receive(:default_per_page).and_return(1)
+ allow(Kaminari.config).to receive(:default_per_page).and_return(2)
end
it 'redirects to last_page if page number is larger than number of pages' do
diff --git a/spec/features/merge_requests/filter_by_labels_spec.rb b/spec/features/merge_requests/filter_by_labels_spec.rb
index 9912e8165e6..7adae08e499 100644
--- a/spec/features/merge_requests/filter_by_labels_spec.rb
+++ b/spec/features/merge_requests/filter_by_labels_spec.rb
@@ -79,22 +79,6 @@ feature 'Merge Request filtering by Labels', :js do
end
end
- context 'clear button' do
- before do
- input_filtered_search('label:~bug')
- end
-
- it 'allows user to remove filtered labels' do
- first('.clear-search').click
- filtered_search.send_keys(:enter)
-
- expect(page).to have_issuable_counts(open: 3, closed: 0, all: 3)
- expect(page).to have_content "Bugfix2"
- expect(page).to have_content "Feature1"
- expect(page).to have_content "Bugfix1"
- end
- end
-
context 'filter dropdown' do
it 'filters by label name' do
init_label_search
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 7a241b02d28..5c5d53877a6 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -4,8 +4,6 @@ require 'spec_helper'
describe ApplicationHelper do
include UploadHelpers
- let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
-
describe 'current_controller?' do
it 'returns true when controller matches argument' do
stub_controller_name('foo')
@@ -57,30 +55,11 @@ describe ApplicationHelper do
end
describe 'project_icon' do
- let(:asset_host) { 'http://assets' }
-
it 'returns an url for the avatar' do
project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
- avatar_url = "/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif"
-
- expect(helper.project_icon(project.full_path).to_s)
- .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
-
- allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
- avatar_url = "#{asset_host}/uploads/-/system/project/avatar/#{project.id}/banana_sample.gif"
-
- expect(helper.project_icon(project.full_path).to_s)
- .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
- end
-
- it 'gives uploaded icon when present' do
- project = create(:project)
- allow_any_instance_of(Project).to receive(:avatar_in_git).and_return(true)
-
- avatar_url = "#{gitlab_host}#{project_avatar_path(project)}"
expect(helper.project_icon(project.full_path).to_s)
- .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+ .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
end
end
@@ -91,40 +70,7 @@ describe ApplicationHelper do
context 'when there is a matching user' do
it 'returns a relative URL for the avatar' do
expect(helper.avatar_icon(user.email).to_s)
- .to eq("/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
- end
-
- context 'when an asset_host is set in the config' do
- let(:asset_host) { 'http://assets' }
-
- before do
- allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
- end
-
- it 'returns an absolute URL on that asset host' do
- expect(helper.avatar_icon(user.email, only_path: false).to_s)
- .to eq("#{asset_host}/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
- end
- end
-
- context 'when only_path is set to false' do
- it 'returns an absolute URL for the avatar' do
- expect(helper.avatar_icon(user.email, only_path: false).to_s)
- .to eq("#{gitlab_host}/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
- end
- end
-
- context 'when the GitLab instance is at a relative URL' do
- before do
- stub_config_setting(relative_url_root: '/gitlab')
- # Must be stubbed after the stub above, and separately
- stub_config_setting(url: Settings.send(:build_gitlab_url))
- end
-
- it 'returns a relative URL with the correct prefix' do
- expect(helper.avatar_icon(user.email).to_s)
- .to eq("/gitlab/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
- end
+ .to eq(user.avatar.url)
end
end
@@ -138,18 +84,9 @@ describe ApplicationHelper do
end
describe 'using a user' do
- context 'when only_path is true' do
- it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon(user, only_path: true).to_s)
- .to eq("/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
- end
- end
-
- context 'when only_path is false' do
- it 'returns an absolute URL for the avatar' do
- expect(helper.avatar_icon(user, only_path: false).to_s)
- .to eq("#{gitlab_host}/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif")
- end
+ it 'returns a relative URL for the avatar' do
+ expect(helper.avatar_icon(user).to_s)
+ .to eq(user.avatar.url)
end
end
end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 97f0ed4904e..32432ee1e81 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -3,8 +3,6 @@ require 'spec_helper'
describe GroupsHelper do
include ApplicationHelper
- let(:asset_host) { 'http://assets' }
-
describe 'group_icon' do
avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
@@ -13,16 +11,8 @@ describe GroupsHelper do
group.avatar = fixture_file_upload(avatar_file_path)
group.save!
- avatar_url = "/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif"
-
- expect(helper.group_icon(group).to_s)
- .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
-
- allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
- avatar_url = "#{asset_host}/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif"
-
expect(helper.group_icon(group).to_s)
- .to eq "<img data-src=\"#{avatar_url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+ .to eq "<img data-src=\"#{group.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
end
end
@@ -34,25 +24,7 @@ describe GroupsHelper do
group.avatar = fixture_file_upload(avatar_file_path)
group.save!
expect(group_icon_url(group.path).to_s)
- .to match("/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif")
- end
-
- it 'returns an CDN url for the avatar' do
- allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
- group = create(:group)
- group.avatar = fixture_file_upload(avatar_file_path)
- group.save!
- expect(group_icon_url(group.path).to_s)
- .to match("#{asset_host}/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif")
- end
-
- it 'returns an based url for the avatar if private' do
- allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
- group = create(:group, :private)
- group.avatar = fixture_file_upload(avatar_file_path)
- group.save!
- expect(group_icon_url(group.path).to_s)
- .to match("/uploads/-/system/group/avatar/#{group.id}/banana_sample.gif")
+ .to match(group.avatar.url)
end
it 'gives default avatar_icon when no avatar is present' do
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index f209328dee1..230c15e5de6 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -396,6 +396,25 @@ describe('Filtered Search Manager', () => {
});
});
+ describe('Clearing search', () => {
+ beforeEach(() => {
+ initializeManager();
+ });
+
+ it('Clicking the "x" clear button, clears the input', () => {
+ const inputValue = 'label:~bug ';
+ manager.filteredSearchInput.value = inputValue;
+ manager.filteredSearchInput.dispatchEvent(new Event('input'));
+
+ expect(gl.DropdownUtils.getSearchQuery()).toEqual(inputValue);
+
+ manager.clearSearchButton.click();
+
+ expect(manager.filteredSearchInput.value).toEqual('');
+ expect(gl.DropdownUtils.getSearchQuery()).toEqual('');
+ });
+ });
+
describe('toggleInputContainerFocus', () => {
beforeEach(() => {
initializeManager();
diff --git a/spec/javascripts/notes/components/issue_comment_form_spec.js b/spec/javascripts/notes/components/issue_comment_form_spec.js
index a26fc8f63cc..db75262b562 100644
--- a/spec/javascripts/notes/components/issue_comment_form_spec.js
+++ b/spec/javascripts/notes/components/issue_comment_form_spec.js
@@ -55,6 +55,25 @@ describe('issue_comment_form component', () => {
expect(vm.toggleIssueState).toHaveBeenCalled();
});
+
+ it('should disable action button whilst submitting', (done) => {
+ const saveNotePromise = Promise.resolve();
+ vm.note = 'hello world';
+ spyOn(vm, 'saveNote').and.returnValue(saveNotePromise);
+ spyOn(vm, 'stopPolling');
+
+ const actionButton = vm.$el.querySelector('.js-action-button');
+
+ vm.handleSave();
+
+ Vue.nextTick()
+ .then(() => expect(actionButton.disabled).toBeTruthy())
+ .then(saveNotePromise)
+ .then(Vue.nextTick)
+ .then(() => expect(actionButton.disabled).toBeFalsy())
+ .then(done)
+ .catch(done.fail);
+ });
});
describe('textarea', () => {
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index 9cbd9bcc14e..5b2642d9473 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -185,6 +185,17 @@ describe Gitlab::GithubImport::Client do
client.with_rate_limit { }
end
+
+ it 'ignores rate limiting when disabled' do
+ expect(client)
+ .to receive(:rate_limiting_enabled?)
+ .and_return(false)
+
+ expect(client)
+ .not_to receive(:requests_remaining?)
+
+ expect(client.with_rate_limit { 10 }).to eq(10)
+ end
end
describe '#requests_remaining?' do
@@ -362,4 +373,20 @@ describe Gitlab::GithubImport::Client do
end
end
end
+
+ describe '#rate_limiting_enabled?' do
+ let(:client) { described_class.new('foo') }
+
+ it 'returns true when using GitHub.com' do
+ expect(client.rate_limiting_enabled?).to eq(true)
+ end
+
+ it 'returns false for GitHub enterprise installations' do
+ expect(client)
+ .to receive(:api_endpoint)
+ .and_return('https://github.kittens.com/')
+
+ expect(client.rate_limiting_enabled?).to eq(false)
+ end
+ end
end
diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
index 6d69b5305d2..ae1d8b47fe9 100644
--- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
@@ -4,32 +4,40 @@ describe Gitlab::Metrics::SidekiqMiddleware do
let(:middleware) { described_class.new }
let(:message) { { 'args' => ['test'], 'enqueued_at' => Time.new(2016, 6, 23, 6, 59).to_f } }
- def run(worker, message)
- expect(Gitlab::Metrics::BackgroundTransaction).to receive(:new)
- .with(worker.class)
- .and_call_original
-
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
- .with(:sidekiq_queue_duration, instance_of(Float))
+ describe '#call' do
+ it 'tracks the transaction' do
+ worker = double(:worker, class: double(:class, name: 'TestWorker'))
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
+ expect(Gitlab::Metrics::BackgroundTransaction).to receive(:new)
+ .with(worker.class)
+ .and_call_original
- middleware.call(worker, message, :test) { nil }
- end
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
+ .with(:sidekiq_queue_duration, instance_of(Float))
- describe '#call' do
- let(:test_worker_class) { double(:class, name: 'TestWorker') }
- let(:worker) { double(:worker, class: test_worker_class) }
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
- it 'tracks the transaction' do
- run(worker, message)
+ middleware.call(worker, message, :test) { nil }
end
it 'tracks the transaction (for messages without `enqueued_at`)' do
- run(worker, {})
+ worker = double(:worker, class: double(:class, name: 'TestWorker'))
+
+ expect(Gitlab::Metrics::BackgroundTransaction).to receive(:new)
+ .with(worker.class)
+ .and_call_original
+
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
+ .with(:sidekiq_queue_duration, instance_of(Float))
+
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
+
+ middleware.call(worker, {}, :test) { nil }
end
it 'tracks any raised exceptions' do
+ worker = double(:worker, class: double(:class, name: 'TestWorker'))
+
expect_any_instance_of(Gitlab::Metrics::Transaction)
.to receive(:run).and_raise(RuntimeError)
diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb
new file mode 100644
index 00000000000..cbdc438be0b
--- /dev/null
+++ b/spec/models/concerns/avatarable_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe Avatarable do
+ subject { create(:project, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
+
+ let(:gitlab_host) { "https://gitlab.example.com" }
+ let(:relative_url_root) { "/gitlab" }
+ let(:asset_host) { "https://gitlab-assets.example.com" }
+
+ before do
+ stub_config_setting(base_url: gitlab_host)
+ stub_config_setting(relative_url_root: relative_url_root)
+ end
+
+ describe '#avatar_path' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:has_asset_host, :visibility_level, :only_path, :avatar_path) do
+ true | Project::PRIVATE | true | [gitlab_host, relative_url_root, subject.avatar.url]
+ true | Project::PRIVATE | false | [gitlab_host, relative_url_root, subject.avatar.url]
+ true | Project::INTERNAL | true | [gitlab_host, relative_url_root, subject.avatar.url]
+ true | Project::INTERNAL | false | [gitlab_host, relative_url_root, subject.avatar.url]
+ true | Project::PUBLIC | true | [subject.avatar.url]
+ true | Project::PUBLIC | false | [asset_host, subject.avatar.url]
+ false | Project::PRIVATE | true | [relative_url_root, subject.avatar.url]
+ false | Project::PRIVATE | false | [gitlab_host, relative_url_root, subject.avatar.url]
+ false | Project::INTERNAL | true | [relative_url_root, subject.avatar.url]
+ false | Project::INTERNAL | false | [gitlab_host, relative_url_root, subject.avatar.url]
+ false | Project::PUBLIC | true | [relative_url_root, subject.avatar.url]
+ false | Project::PUBLIC | false | [gitlab_host, relative_url_root, subject.avatar.url]
+ end
+
+ with_them do
+ before do
+ allow(ActionController::Base).to receive(:asset_host).and_return(has_asset_host ? asset_host : nil)
+ subject.visibility_level = visibility_level
+ end
+
+ it 'returns the expected avatar path' do
+ expect(subject.avatar_path(only_path: only_path)).to eq(avatar_path.join)
+ end
+ end
+ end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index d4052a64570..5e82a2988ce 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -247,8 +247,6 @@ describe Group do
describe '#avatar_url' do
let!(:group) { create(:group, :access_requestable, :with_avatar) }
let(:user) { create(:user) }
- let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
- let(:avatar_path) { "/uploads/-/system/group/avatar/#{group.id}/dk.png" }
context 'when avatar file is uploaded' do
before do
@@ -256,12 +254,8 @@ describe Group do
end
it 'shows correct avatar url' do
- expect(group.avatar_url).to eq(avatar_path)
- expect(group.avatar_url(only_path: false)).to eq([gitlab_host, avatar_path].join)
-
- allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
-
- expect(group.avatar_url).to eq([gitlab_host, avatar_path].join)
+ expect(group.avatar_url).to eq(group.avatar.url)
+ expect(group.avatar_url(only_path: false)).to eq([Gitlab.config.gitlab.url, group.avatar.url].join)
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e684389af5d..4db417c8793 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -883,20 +883,14 @@ describe Project do
context 'when avatar file is uploaded' do
let(:project) { create(:project, :public, :with_avatar) }
- let(:avatar_path) { "/uploads/-/system/project/avatar/#{project.id}/dk.png" }
- let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
it 'shows correct url' do
- expect(project.avatar_url).to eq(avatar_path)
- expect(project.avatar_url(only_path: false)).to eq([gitlab_host, avatar_path].join)
-
- allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
-
- expect(project.avatar_url).to eq([gitlab_host, avatar_path].join)
+ expect(project.avatar_url).to eq(project.avatar.url)
+ expect(project.avatar_url(only_path: false)).to eq([Gitlab.config.gitlab.url, project.avatar.url].join)
end
end
- context 'When avatar file in git' do
+ context 'when avatar file in git' do
before do
allow(project).to receive(:avatar_in_git) { true }
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a3ebf649aa8..65dccf9638d 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1149,16 +1149,9 @@ describe User do
let(:user) { create(:user, :with_avatar) }
context 'when avatar file is uploaded' do
- let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
- let(:avatar_path) { "/uploads/-/system/user/avatar/#{user.id}/dk.png" }
-
it 'shows correct avatar url' do
- expect(user.avatar_url).to eq(avatar_path)
- expect(user.avatar_url(only_path: false)).to eq([gitlab_host, avatar_path].join)
-
- allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
-
- expect(user.avatar_url).to eq([gitlab_host, avatar_path].join)
+ expect(user.avatar_url).to eq(user.avatar.url)
+ expect(user.avatar_url(only_path: false)).to eq([Gitlab.config.gitlab.url, user.avatar.url].join)
end
end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 780dbce6488..04a658cd6c3 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -427,6 +427,142 @@ describe API::Groups do
end
end
+ describe 'GET /groups/:id/subgroups', :nested_groups do
+ let!(:subgroup1) { create(:group, parent: group1) }
+ let!(:subgroup2) { create(:group, :private, parent: group1) }
+ let!(:subgroup3) { create(:group, :private, parent: group2) }
+
+ context 'when unauthenticated' do
+ it 'returns only public subgroups' do
+ get api("/groups/#{group1.id}/subgroups")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(subgroup1.id)
+ expect(json_response.first['parent_id']).to eq(group1.id)
+ end
+
+ it 'returns 404 for a private group' do
+ get api("/groups/#{group2.id}/subgroups")
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when authenticated as user' do
+ context 'when user is not member of a public group' do
+ it 'returns no subgroups for the public group' do
+ get api("/groups/#{group1.id}/subgroups", user2)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
+ context 'when using all_available in request' do
+ it 'returns public subgroups' do
+ get api("/groups/#{group1.id}/subgroups", user2), all_available: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response[0]['id']).to eq(subgroup1.id)
+ expect(json_response[0]['parent_id']).to eq(group1.id)
+ end
+ end
+ end
+
+ context 'when user is not member of a private group' do
+ it 'returns 404 for the private group' do
+ get api("/groups/#{group2.id}/subgroups", user1)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when user is member of public group' do
+ before do
+ group1.add_guest(user2)
+ end
+
+ it 'returns private subgroups' do
+ get api("/groups/#{group1.id}/subgroups", user2)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ private_subgroups = json_response.select { |group| group['visibility'] == 'private' }
+ expect(private_subgroups.length).to eq(1)
+ expect(private_subgroups.first['id']).to eq(subgroup2.id)
+ expect(private_subgroups.first['parent_id']).to eq(group1.id)
+ end
+
+ context 'when using statistics in request' do
+ it 'does not include statistics' do
+ get api("/groups/#{group1.id}/subgroups", user2), statistics: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include 'statistics'
+ end
+ end
+ end
+
+ context 'when user is member of private group' do
+ before do
+ group2.add_guest(user1)
+ end
+
+ it 'returns subgroups' do
+ get api("/groups/#{group2.id}/subgroups", user1)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(subgroup3.id)
+ expect(json_response.first['parent_id']).to eq(group2.id)
+ end
+ end
+ end
+
+ context 'when authenticated as admin' do
+ it 'returns private subgroups of a public group' do
+ get api("/groups/#{group1.id}/subgroups", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ end
+
+ it 'returns subgroups of a private group' do
+ get api("/groups/#{group2.id}/subgroups", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ end
+
+ it 'does not include statistics by default' do
+ get api("/groups/#{group1.id}/subgroups", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('statistics')
+ end
+
+ it 'includes statistics if requested' do
+ get api("/groups/#{group1.id}/subgroups", admin), statistics: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).to include('statistics')
+ end
+ end
+ end
+
describe "POST /groups" do
context "when authenticated as user without group permissions" do
it "does not create group" do
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index dfe48e45d49..ba697e2b305 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -175,4 +175,25 @@ describe API::Services do
end
end
end
+
+ describe 'Mattermost service' do
+ let(:service_name) { 'mattermost' }
+ let(:params) do
+ { webhook: 'https://hook.example.com', username: 'username' }
+ end
+
+ before do
+ project.create_mattermost_service(
+ active: true,
+ properties: params
+ )
+ end
+
+ it 'accepts a username for update' do
+ put api("/projects/#{project.id}/services/mattermost", user), params.merge(username: 'new_username')
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['properties']['username']).to eq('new_username')
+ end
+ end
end