Commit 1cb9fe25 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-07-12

# Conflicts:
#	app/views/projects/new.html.haml
#	doc/user/project/import/index.md
#	locale/gitlab.pot

[ci skip]
parents 6bdf064d eddf34f4
......@@ -23,7 +23,6 @@ class ProjectWiki
@user = user
end
delegate :empty?, to: :pages
delegate :repository_storage, :hashed_storage?, to: :project
def path
......@@ -82,6 +81,10 @@ class ProjectWiki
!!find_page('home')
end
def empty?
pages(limit: 1).empty?
end
# Returns an Array of Gitlab WikiPage instances or an
# empty Array if this Wiki has no pages.
def pages(limit: nil)
......
.explore-title.text-center
%h2
Explore GitLab
= _("Explore GitLab")
%p.lead
Discover projects, groups and snippets. Share your projects with others
= _("Discover projects, groups and snippets. Share your projects with others")
%br
......@@ -2,7 +2,7 @@
%ul.nav-links.nav.nav-tabs
= nav_link(page: explore_groups_path) do
= link_to explore_groups_path do
Explore Groups
= _("Explore Groups")
.nav-controls
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
- @hide_top_links = true
- page_title "Groups"
- header_title "Groups", dashboard_groups_path
- page_title _("Groups")
- header_title _("Groups"), dashboard_groups_path
- if current_user
= render 'dashboard/groups_head'
......@@ -10,14 +10,14 @@
- if cookies[:explore_groups_landing_dismissed] != 'true'
.explore-groups.landing.content-block.js-explore-groups-landing.hide
%button.dismiss-button{ type: 'button', 'aria-label' => 'Dismiss' }= icon('times')
%button.dismiss-button{ type: 'button', 'aria-label' => _('Dismiss') }= icon('times')
.svg-container
= custom_icon('icon_explore_groups_splash')
.inner-content
%p Below you will find all the groups that are public.
%p You can easily contribute to them by requesting to join these groups.
%p= _("Below you will find all the groups that are public.")
%p= _("You can easily contribute to them by requesting to join these groups.")
- if params[:filter].blank? && @groups.empty?
.nothing-here-block No public groups
.nothing-here-block= _("No public groups")
- else
= render 'groups'
......@@ -2,16 +2,16 @@
.dropdown
%button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe')
%span.light Visibility:
%span.light= _("Visibility:")
- if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i)
- else
Any
= _('Any')
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right
%li
= link_to filter_projects_path(visibility_level: nil) do
Any
= _('Any')
- Gitlab::VisibilityLevel.values.each do |level|
%li{ class: active_when(level.to_s == params[:visibility_level]) || 'light' }
= link_to filter_projects_path(visibility_level: level) do
......
......@@ -2,13 +2,13 @@
%ul.nav-links.nav.nav-tabs
= nav_link(page: [trending_explore_projects_path, explore_root_path]) do
= link_to trending_explore_projects_path do
Trending
= _('Trending')
= nav_link(page: starred_explore_projects_path) do
= link_to starred_explore_projects_path do
Most stars
= _('Most stars')
= nav_link(page: explore_projects_path) do
= link_to explore_projects_path do
All
= _('All')
.nav-controls
- unless current_user
......
- @hide_top_links = true
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
......
- @hide_top_links = true
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
......
- @hide_top_links = true
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
- if current_user
= render 'dashboard/projects_head'
......
......@@ -69,10 +69,13 @@
.nothing-here-block
%h4 No import options available
%p Contact an administrator to enable options for importing your project.
<<<<<<< HEAD
-# EE-specific start
= render 'new_ci_cd_only_project_pane', active_tab: active_tab
-# EE-specific end
=======
>>>>>>> upstream/master
.save-project-loader.d-none
.center
......
---
title: Fix updated_at if created_at is set for Note API
merge_request:
author:
type: fixed
---
title: Use appropriate timeout on Gitaly server info checks, avoid error on timeout
merge_request: 20552
author:
type: fixed
---
title: Optimize ProjectWiki#empty? check
merge_request: 20573
author:
type: performance
......@@ -30,7 +30,7 @@ class Gitlab::Seeder::Environments
def create_merge_request_review_deployments!
@project
.merge_requests
.select { |mr| mr.source_branch.match(/[^a-zA-Z0-9]+/) }
.select { |mr| mr.source_branch.match?(/[a-zA-Z0-9]+/) }
.sample(4)
.each do |merge_request|
next unless merge_request.diff_head_sha
......
......@@ -394,7 +394,7 @@ For example, `/` is represented by `%2F`:
GET /api/v4/projects/diaspora%2Fdiaspora
```
## Branches & tags name encoding
## Branches and tags name encoding
If your branch or tag contains a `/`, make sure the branch/tag name is
URL-encoded.
......@@ -405,6 +405,36 @@ For example, `/` is represented by `%2F`:
GET /api/v4/projects/1/branches/my%2Fbranch/commits
```
## Encoding API parameters of `array` and `hash` types
When making an API call with parameters of type `array` and/or `hash`, the parameters may be
specified as shown below.
### `array`
`import_sources` is a parameter of type `array`:
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
-d "import_sources[]=github" \
-d "import_sources[]=bitbucket" \
"https://gitlab.example.com/api/v4/some_endpoint
```
### `hash`
`override_params` is a parameter of type `hash`:
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" \
--form "namespace=email" \
--form "path=impapi" \
--form "file=@/path/to/somefile.txt"
--form "override_params[visibility]=private" \
--form "override_params[some_other_param]=some_value" \
https://gitlab.example.com/api/v4/projects/import
```
## `id` vs `iid`
When you work with the API, you may notice two similar fields in API entities:
......
......@@ -218,6 +218,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
- `snippet_id` (required) - The ID of a snippet
- `body` (required) - The content of a note
- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippet/11/notes?body=note
......@@ -340,6 +341,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding)
- `merge_request_iid` (required) - The IID of a merge request
- `body` (required) - The content of a note
- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
### Modify existing merge request note
......
......@@ -28,8 +28,11 @@ POST /projects/:id/export
| `upload[url]` | string | yes | The URL to upload the project |
| `upload[http_method]` | string | no | The HTTP method to upload the exported project. Only `PUT` and `POST` methods allowed. Default is `PUT` |
```console
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/export --data "description=FooBar&upload[http_method]=PUT&upload[url]=https://example-bucket.s3.eu-west-3.amazonaws.com/backup?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMBJHN2O62W8IELQ%2F20180312%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20180312T110328Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=8413facb20ff33a49a147a0b4abcff4c8487cc33ee1f7e450c46e8f695569dbd"
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/export \
--data "upload[http_method]=PUT" \
--data-urlencode "upload[url]=https://example-bucket.s3.eu-west-3.amazonaws.com/backup?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIMBJHN2O62W8IELQ%2F20180312%2Feu-west-3%2Fs3%2Faws4_request&X-Amz-Date=20180312T110328Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=8413facb20ff33a49a147a0b4abcff4c8487cc33ee1f7e450c46e8f695569dbd"
```
```json
......@@ -125,6 +128,29 @@ by `@`. For example:
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "path=api-project" --form "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import
```
cURL doesn't support posting a file from a remote server. Importing a project from a remote server can be accomplished through something like the following:
```python
import requests
import urllib
import json
import sys
s3_file = urllib.urlopen(presigned_url)
url = 'https://gitlab.example.com/api/v4/projects/import'
files = {'file': s3_file}
data = {
"path": "example-project",
"namespace": "example-group"
}
headers = {
'Private-Token': "9koXpg98eAheJpvBs5tK"
}
requests.post(url, headers=headers, data=data, files=files)
```
```json
{
"id": 1,
......
......@@ -2,27 +2,24 @@
To get started with Vue, read through [their documentation][vue-docs].
## Vue architecture
## Examples
All new features built with Vue.js must follow a [Flux architecture][flux].
The main goal we are trying to achieve is to have only one data flow and only one data entry.
In order to achieve this goal, you can either use [vuex](#vuex) or use the [store pattern][state-management], explained below:
What is described in the following sections can be found in these examples:
Each Vue bundle needs a Store - where we keep all the data -, a Service - that we use to communicate with the server - and a main Vue component.
- web ide: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/ide/stores
- security products: https://gitlab.com/gitlab-org/gitlab-ee/tree/master/ee/app/assets/javascripts/vue_shared/security_reports
- registry: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/registry/stores
Think of the Main Vue Component as the entry point of your application. This is the only smart
component that should exist in each Vue feature.
This component is responsible for:
1. Calling the Service to get data from the server
1. Calling the Store to store the data received
1. Mounting all the other components
## Vue architecture
![Vue Architecture](img/vue_arch.png)
All new features built with Vue.js must follow a [Flux architecture][flux].
The main goal we are trying to achieve is to have only one data flow and only one data entry.
In order to achieve this goal we use [vuex](#vuex).
You can also read about this architecture in vue docs about [state management][state-management]
and about [one way data flow][one-way-data-flow].
### Components, Stores and Services
### Components and Store
In some features implemented with Vue.js, like the [issue board][issue-boards]
or [environments table][environments-table]
......@@ -33,10 +30,8 @@ new_feature
├── components
│ └── component.vue
│ └── ...
├── stores
├── store
│ └── new_feature_store.js
├── services # only when not using vuex
│ └── new_feature_service.js
├── index.js
```
_For consistency purposes, we recommend you to follow the same structure._
......@@ -125,217 +120,6 @@ You can read more about components in Vue.js site, [Component System][component-
#### Vuex
Check this [page](vuex.md) for more details.
#### Flux like state management
The Store is a class that allows us to manage the state in a single
source of truth. It is not aware of the service or the components.
The concept we are trying to follow is better explained by Vue documentation
itself, please read this guide: [State Management][state-management]
### A folder for the Service
**If you are using Vuex you won't need this step**
The Service is a class used only to communicate with the server.
It does not store or manipulate any data. It is not aware of the store or the components.
We use [axios][axios] to communicate with the server.
Refer to [axios](axios.md) for more details.
Axios instance should only be imported in the service file.
```javascript
import axios from '~/lib/utils/axios_utils';
```
### End Result
The following example shows an application:
```javascript
// store.js
export default class Store {
/**
* This is where we will iniatialize the state of our data.
* Usually in a small SPA you don't need any options when starting the store.
* In that case you do need guarantee it's an Object and it's documented.
*
* @param {Object} options
*/
constructor(options) {
this.options = options;
// Create a state object to handle all our data in the same place
this.todos = [];
}
setTodos(todos = []) {
this.todos = todos;
}
addTodo(todo) {
this.todos.push(todo);
}
removeTodo(todoID) {
const state = this.todos;
const newState = state.filter((element) => {element.id !== todoID});
this.todos = newState;
}
}
// service.js
import axios from '~/lib/utils/axios_utils'
export default class Service {
constructor(options) {
this.todos = axios.create({
baseURL: endpoint.todosEndpoint
});
}
getTodos() {
return this.todos.get();
}
addTodo(todo) {
return this.todos.put(todo);
}
}
// todo_component.vue
<script>
export default {
props: {
data: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div>
<h1>
Title: {{data.title}}
</h1>
<p>
{{data.text}}
</p>
</div>
</template>
// todos_main_component.vue
<script>
import Store from 'store';
import Service from 'service';
import TodoComponent from 'todoComponent';
export default {
components: {
todo: TodoComponent,
},
/**
* Although most data belongs in the store, each component it's own state.
* We want to show a loading spinner while we are fetching the todos, this state belong
* in the component.
*
* We need to access the store methods through all methods of our component.
* We need to access the state of our store.
*/
data() {
const store = new Store();
return {
isLoading: false,
store: store,
todos: store.todos,
};
},
created() {
this.service = new Service('/todos');
this.getTodos();
},
methods: {
getTodos() {
this.isLoading = true;
this.service
.getTodos()
.then(response => {
this.store.setTodos(response);
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
// Show an error
});
},
addTodo(event) {
this.service
.addTodo({
title: 'New entry',
text: `You clicked on ${event.target.tagName}`,
})
.then(response => {
this.store.addTodo(response);
})
.catch(() => {
// Show an error
});
},
},
};
</script>
<template>
<div class="container">
<div v-if="isLoading">
<i
class="fa fa-spin fa-spinner"
aria-hidden="true" />
</div>
<div
v-if="!isLoading"
class="js-todo-list">
<template v-for='todo in todos'>
<todo :data="todo" />
</template>
<button
@click="addTodo"
class="js-add-todo">
Add Todo
</button>
</div>
<div>
</template>
// index.js
import todoComponent from 'todos_main_component.vue';
new Vue({
el: '.js-todo-app',
components: {
todoComponent,
},
render: createElement => createElement('todo-component' {
props: {
someProp: [],
}
}),
});
```
The [issue boards service][issue-boards-service]
is a good example of this pattern.
## Style guide
Please refer to the Vue section of our [style guide](style_guide_js.md#vue-js)
......@@ -446,6 +230,5 @@ need to test the rendered output. [Vue][vue-test] guide's to unit test show us e
[state-management]: https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
[one-way-data-flow]: https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow
[vue-test]: https://vuejs.org/v2/guide/unit-testing.html
[issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6
[flux]: https://facebook.github.io/flux
[axios]: https://github.com/axios/axios
......@@ -62,7 +62,7 @@ Click on `Add Kubernetes cluster`, the cluster is now connected to GitLab. At th
If you would like to utilize your own CI/CD scripts to deploy to the cluster, you can stop here.
## Disable Role Based-Access Control (RBAC)
## Disable Role-Based Access Control (RBAC)
Presently, Auto DevOps and one-click app installs do not support [Kubernetes role-based access control](https://kubernetes.io/docs/reference/access-authn-authz/rbac/). Support is [being worked on](https://gitlab.com/groups/gitlab-org/-/epics/136), but in the interim RBAC must be disabled to utilize for these features.
......
......@@ -11,7 +11,10 @@
1. [From SVN](svn.md)
1. [From TFS](tfs.md)
1. [From repo by URL](repo_by_url.md)
<<<<<<< HEAD
1. [From Gemnasium](gemnasium.md)
=======
>>>>>>> upstream/master
1. [By uploading a manifest file](manifest.md)
In addition to the specific migration documentation above, you can import any
......
......@@ -99,6 +99,8 @@ module API
current_user.admin? || parent.owned_by?(current_user)
end
opts[:updated_at] = opts[:created_at] if opts[:created_at]
project = parent if parent.is_a?(Project)
::Notes::CreateService.new(project, current_user, opts).execute
end
......
......@@ -50,7 +50,7 @@ module Gitaly
@info ||=
begin
Gitlab::GitalyClient::ServerService.new(@storage).info
rescue GRPC::Unavailable, GRPC::GRPC::DeadlineExceeded
rescue GRPC::Unavailable, GRPC::DeadlineExceeded
# This will show the server as being out of date
Gitaly::ServerInfoResponse.new(git_version: '', server_version: '', storage_statuses: [])
end
......
......@@ -86,9 +86,6 @@ module Gitlab
# Relative path of repo
attr_reader :relative_path
# Rugged repo object
attr_reader :rugged
attr_reader :gitlab_projects, :storage, :gl_repository, :relative_path
# This initializer method is only used on the client side (gitlab-ce).
......@@ -112,8 +109,9 @@ module Gitlab
[storage, relative_path] == [other.storage, other.relative_path]
end
# This method will be removed when Gitaly reaches v1.1.
def path
@path ||= File.join(
File.join(
Gitlab.config.repositories.storages[@storage].legacy_disk_path, @relative_path
)
end
......@@ -127,8 +125,9 @@ module Gitlab
raise Gitlab::Git::CommandError.new(e.message)
end
# This method will be removed when Gitaly reaches v1.1.
def rugged
@rugged ||= circuit_breaker.perform do
circuit_breaker.perform do
Rugged::Repository.new(path, alternates: alternate_object_directories)
end
rescue Rugged::RepositoryError, Rugged::OSError
......@@ -713,12 +712,6 @@ module Gitlab
Gitlab::Git.committer_hash(email: user.email, name: user.name)
end
def create_commit(params = {})
params[:message].delete!("\r")
Rugged::Commit.create(rugged, params)
end
# Delete the specified branch from the repository
def delete_branch(branch_name)
gitaly_migrate(:delete_branch, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
......@@ -1758,6 +1751,12 @@ module Gitlab
def sha_from_ref(ref)
rev_parse_target(ref).oid
end
def create_commit(params = {})
params[:message].delete!("\r")
Rugged::Commit.create(rugged, params)
end
end
end
end
......@@ -12,35 +12,12 @@ module Gitlab
end
# This method returns an array of new commit references
def new_refs
repository.rev_list(including: newrev, excluding: :all).split("\n")
end
# Finds newly added objects
# Returns an array of shas
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1233
#
# Can skip objects which do not have a path using required_path: true
# This skips commit objects and root trees, which might not be needed when
# looking for blobs
#
# When given a block it will yield objects as a lazy enumerator so
# the caller can limit work done instead of processing megabytes of data
def new_objects(options: [], require_path: nil, not_in: nil, &lazy_block)
opts = {
including: newrev,
options: options,
excluding: not_in.nil? ? :all : not_in,
require_path: require_path
}
get_objects(opts, &lazy_block)
end
def all_objects(options: [], require_path: nil, &lazy_block)
get_objects(including: :all,
options: options,
require_path: require_path,
&lazy_block)
def new_refs
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repository.rev_list(including: newrev, excluding: :all).split("\n")
end
end
private
......
......@@ -9,7 +9,7 @@ module Gitlab
end
def info
GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new)
GitalyClient.call(@storage, :server_service, :server_info, Gitaly::ServerInfoRequest.new, timeout: GitalyClient.fast_timeout)
end
end
end
......
......@@ -557,7 +557,11 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
<<<<<<< HEAD
msgid "Any Label"
=======
msgid "Any"
>>>>>>> upstream/master
msgstr ""
msgid "Appearance"
......@@ -803,6 +807,7 @@ msgstr ""
msgid "Below are examples of regex for existing tools:"
msgstr ""
<<<<<<< HEAD
msgid "Billing"
msgstr ""
......@@ -867,6 +872,9 @@ msgid "BillingPlans|paid annually at %{price_per_year}"
msgstr ""
msgid "BillingPlans|per user"
=======
msgid "Below you will find all the groups that are public."
>>>>>>> upstream/master
msgstr ""
msgid "Boards"
......@@ -1189,12 +1197,15 @@ msgstr ""
msgid "Choose the top-level group for your repository imports."
msgstr ""
<<<<<<< HEAD
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr ""
=======
>>>>>>> upstream/master
msgid "Choose which repositories you want to import."
msgstr ""
......@@ -2293,7 +2304,14 @@ msgstr ""
msgid "Discard draft"
msgstr ""
<<<<<<< HEAD
msgid "Discover GitLab Geo."
=======
msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr ""
msgid "Dismiss"
>>>>>>> upstream/master
msgstr ""
msgid "Dismiss Cycle Analytics introduction box"
......@@ -2620,6 +2638,12 @@ msgstr ""
msgid "Expand sidebar"
msgstr ""
msgid "Explore GitLab"
msgstr ""
msgid "Explore Groups"
msgstr ""
msgid "Explore groups"
msgstr ""
......@@ -3105,6 +3129,9 @@ msgstr ""
msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name}"
msgstr ""
msgid "Groups"
msgstr ""
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr ""
......@@ -3585,9 +3612,12 @@ msgstr ""
msgid "Manage project labels"
msgstr ""
<<<<<<< HEAD
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
=======
>>>>>>> upstream/master
msgid "Manifest"
msgstr ""
......@@ -3831,6 +3861,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
msgid "Most stars"
msgstr ""
msgid "Move"
msgstr ""
......@@ -3974,7 +4007,11 @@ msgstr ""
msgid "No messages were logged"
msgstr ""
<<<<<<< HEAD
msgid "No pushes for the selected time period."
=======
msgid "No public groups"
>>>>>>> upstream/master
msgstr ""
msgid "No repository"
......@@ -4850,9 +4887,12 @@ msgstr ""
msgid "Repository URL"
msgstr ""
<<<<<<< HEAD
msgid "Repository has no locks."
msgstr ""
=======
>>>>>>> upstream/master
msgid "Repository maintenance"
msgstr ""
......@@ -6087,6 +6127,9 @@ msgstr ""
msgid "Track time with quick actions"
msgstr ""
msgid "Trending"
msgstr ""
msgid "Trigger this manual action"
msgstr ""
......@@ -6266,6 +6309,9 @@ msgstr ""
msgid "Visibility and access controls"
msgstr ""
msgid "Visibility:"
msgstr ""
msgid "VisibilityLevel|Internal"
msgstr ""
......@@ -6491,6 +6537,9 @@ msgstr ""
msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
msgstr ""
msgid "You can easily contribute to them by requesting to join these groups."
msgstr ""
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
......
......@@ -55,7 +55,7 @@ Since the arguments would be passed to `rspec`, you could use all `rspec`
options there. For example, passing `--backtrace` and also line number:
```
bin/qa Test::Instance http://localhost qa/specs/features/login/standard_spec.rb:3 --backtrace
bin/qa Test::Instance http://localhost qa/specs/features/project/create_spec.rb:3 --backtrace
```
### Overriding the authenticated user
......
......@@ -41,14 +41,17 @@ module QA
autoload :Project, 'qa/factory/resource/project'
autoload :MergeRequest, 'qa/factory/resource/merge_request'
autoload :ProjectImportedFromGithub, 'qa/factory/resource/project_imported_from_github'
autoload :MergeRequestFromFork, 'qa/factory/resource/merge_request_from_fork'
autoload :DeployKey, 'qa/factory/resource/deploy_key'
autoload :Branch, 'qa/factory/resource/branch'
autoload :SecretVariable, 'qa/factory/resource/secret_variable'
autoload :Runner, 'qa/factory/resource/runner'
autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster'
autoload :User, 'qa/factory/resource/user'
autoload :ProjectMilestone, 'qa/factory/resource/project_milestone'
autoload :Wiki, 'qa/factory/resource/wiki'
autoload :Fork, 'qa/factory/resource/fork'
end
module Repository
......@@ -107,6 +110,7 @@ module QA
module Main
autoload :Login, 'qa/page/main/login'
autoload :OAuth, 'qa/page/main/oauth'
autoload :SignUp, 'qa/page/main/sign_up'
end
module Settings
......@@ -167,6 +171,10 @@ module QA
autoload :Index, 'qa/page/project/issue/index'
end
module Fork
autoload :New, 'qa/page/project/fork/new'
end
module Milestone
autoload :New, 'qa/page/project/milestone/new'
autoload :Index, 'qa/page/project/milestone/index'
......@@ -200,6 +208,10 @@ module QA
autoload :Sidebar, 'qa/page/issuable/sidebar'
end
module Layout
autoload :Banner, 'qa/page/layout/banner'
end
module MergeRequest
autoload :New, 'qa/page/merge_request/new'
autoload :Show, 'qa/page/merge_request/show'
......
......@@ -11,6 +11,8 @@ module QA
factory.output
end
product(:project) { |factory| factory.project }
def initialize
@file_name = 'file.txt'
@file_content = '# This is test project'
......
......@@ -5,7 +5,8 @@ module QA
module Repository
class Push < Factory::Base
attr_accessor :file_name, :file_content, :commit_message,
:branch_name, :new_branch, :output, :repository_uri
:branch_name, :new_branch, :output, :repository_uri,
:user
attr_writer :remote_branch
......@@ -31,9 +32,20 @@ module QA
def fabricate!
Git::Repository.perform do |repository|
repository.uri = repository_uri
repository.use_default_credentials
username = 'GitLab QA'
email = 'root@gitlab.com'
if user
repository.username = user.username
repository.password = user.password
username = user.name
email = user.email
end
repository.clone
repository.configure_identity('GitLab QA', 'root@gitlab.com')
repository.configure_identity(username, email)
if new_branch
repository.checkout_new_branch(branch_name)
......
module QA
module Factory
module Resource
class Fork < Factory::Base
dependency Factory::Repository::ProjectPush, as: :push
dependency Factory::Resource::User, as: :user
product(:user) { |factory| factory.user }
def fabricate!
push.project.visit!
Page::Project::Show.act { fork_project }
Page::Project::Fork::New.perform do |fork_new|
fork_new.choose_namespace(user.name)
end
Page::Layout::Banner.act { has_notice?('The project was successfully forked.') }
end
end
end
end
end
module QA
module Factory
module Resource
class MergeRequestFromFork < MergeRequest
attr_accessor :fork_branch
dependency Factory::Resource::Fork, as: :fork
dependency Factory::Repository::ProjectPush, as: :push do |push, factory|
push.project = factory.fork
push.branch_name = factory.fork_branch
push.file_name = 'file2.txt'
push.user = factory.fork.user
end
def fabricate!
fork.visit!
Page::Project::Show.act { new_merge_request }
Page::MergeRequest::New.act { create_merge_request }
end
end
end
end
end
......@@ -37,6 +37,7 @@ module QA
page.choose_test_namespace
page.choose_name(@name)
page.add_description(@description)
page.set_visibility('Public')
page.create_new_project
end
end
......
require 'securerandom'
module QA
module Factory
module Resource
class User < Factory::Base
attr_accessor :name, :username, :email, :password
def initialize
@name = "name-#{SecureRandom.hex(8)}"
@username = "username-#{SecureRandom.hex(8)}"
@email = "mail#{SecureRandom.hex(8)}@mail.com"
@password = 'password'
end
product(:name) { |factory| factory.name }
product(:username) { |factory| factory.username }
product(:email) { |factory| factory.email }
product(:password) { |factory| factory.password }
def fabricate!
Page::Menu::Main.act { sign_out }
Page::Main::Login.act { switch_to_register_tab }
Page::Main::SignUp.perform do |page|
page.sign_up!(name: name, username: username, email: email, password: password)
end
end
end
end
end
end
......@@ -4,6 +4,7 @@ module QA
class Sidebar < Page::Base
view 'app/views/shared/issuable/_sidebar.html.haml' do
element :labels_block, ".issuable-show-labels"
element :milestones_block, '.block.milestone'
end
def has_label?(label)
......@@ -11,6 +12,12 @@ module QA
!!find('span', text: label)
end
end
def has_milestone?(milestone)
page.within('.block.milestone') do
!!find("[href*='/milestones/']", text: milestone)
end
end
end
end
end
......
module QA
module Page
module Layout
class Banner < Page::Base
view 'app/views/layouts/header/_read_only_banner.html.haml' do
element :flash_notice, ".flash-notice"
end
def has_notice?(message)
page.within('.flash-notice') do
!!find('span', text: message)
end
end
end
end
end
end
......@@ -25,19 +25,24 @@ module QA
element :standard_tab, "link_to 'Standard'"
end
view 'app/views/devise/shared/_tabs_normal.html.haml' do
element :sign_in_tab, /nav-link.*login-pane.*Sign in/
element :register_tab, /nav-link.*register-pane.*Register/
end
def initialize
# The login page is usually the entry point for all the scenarios so
# we need to wait for the instance to start. That said, in some cases
# we are already logged-in so we check both cases here.
wait(max: 500) do
page.has_css?('.login-page') ||
Page::Menu::Main.act { has_personal_area? }
Page::Menu::Main.act { has_personal_area?(wait: 0) }
end
end
def sign_in_using_credentials
# Don't try to log-in if we're already logged-in
return if Page::Menu::Main.act { has_personal_area? }
return if Page::Menu::Main.act { has_personal_area?(wait: 0) }
using_wait_time 0 do
set_initial_password_if_present
......@@ -48,12 +53,22 @@ module QA
sign_in_using_gitlab_credentials
end
end
Page::Menu::Main.act { has_personal_area? }
end
def self.path
'/users/sign_in'
end
def switch_to_sign_in_tab
click_on 'Sign in'
end
def switch_to_register_tab
click_on 'Register'
end
private
def sign_in_using_ldap_credentials
......
module QA
module Page
module Main
class SignUp < Page::Base
view 'app/views/devise/shared/_signup_box.html.haml' do
element :name, 'text_field :name'
element :username, 'text_field :username'
element :email_field, 'email_field :email'
element :email_confirmation, 'email_field :email_confirmation'
element :password, 'password_field :password'
element :register_button, 'submit "Register"'
end
def sign_up!(name:, username:, email:, password:)
fill_in :new_user_name, with: name
fill_in :new_user_username, with: username
fill_in :new_user_email, with: email
fill_in :new_user_email_confirmation, with: email
fill_in :new_user_password, with: password
click_button 'Register'
Page::Menu::Main.act { has_personal_area? }
end
end
end
end
end
......@@ -60,9 +60,9 @@ module QA
end
end
def has_personal_area?
def has_personal_area?(wait: Capybara.default_max_wait_time)
# No need to wait, either we're logged-in, or not.
using_wait_time(0) { page.has_selector?('.qa-user-avatar') }
using_wait_time(wait) { page.has_selector?('.qa-user-avatar') }
end
private
......
......@@ -81,12 +81,6 @@ module QA
click_element :squash_checkbox
end
def has_milestone?(milestone_title)
page.within('.issuable-sidebar') do
!!find("[href*='/milestones/']", text: milestone_title, wait: 1)
end
end
end
end
end
......
module QA
module Page
module Project
module Fork
class New < Page::Base
view 'app/views/projects/forks/_fork_button.html.haml' do
element :namespace, 'link_to project_forks_path'
end
def choose_namespace(namespace = Runtime::Namespace.path)
click_on namespace
end
end
end
end
end
end
......@@ -14,6 +14,7 @@ module QA
element :project_path, 'text_field :path'
element :project_description, 'text_area :description'
element :project_create_button, "submit 'Create project'"
element :visibility_radios, 'visibility_level:'
end
view 'app/views/projects/_import_project_pane.html.haml' do
......@@ -42,6 +43,10 @@ module QA
click_on 'Create project'
end
def set_visibility(visibility)
choose visibility
end
def go_to_github_import
click_link 'GitHub'
end
......
......@@ -24,6 +24,11 @@ module QA
element :branches_dropdown
end
view 'app/views/projects/buttons/_fork.html.haml' do
element :fork_label, "%span= s_('GoToYourFork|Fork')"
element :fork_link, "link_to new_project_fork_path(@project)"
end
view 'app/views/projects/_files.html.haml' do
element :tree_holder, '.tree-holder'
end
......@@ -63,6 +68,10 @@ module QA
click_link 'New issue'
end
def fork_project
click_on 'Fork'
end
end
end
end
......
module QA
describe 'standard user login', :core do
it 'user logs in using credentials' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
# TODO, since `Signed in successfully` message was removed
# this is the only way to tell if user is signed in correctly.
#
Page::Menu::Main.perform do |menu|
expect(menu).to have_personal_area
end
end
end
end
......@@ -20,11 +20,12 @@ module QA
merge_request.milestone = current_milestone
end
Page::MergeRequest::Show.perform do |merge_request|
expect(page).to have_content('This is a merge request with a milestone')
expect(page).to have_content('Great feature with milestone')
expect(page).to have_content(/Opened [\w\s]+ ago/)
expect(merge_request).to have_milestone(current_milestone.title)
expect(page).to have_content('This is a merge request with a milestone')
expect(page).to have_content('Great feature with milestone')
expect(page).to have_content(/Opened [\w\s]+ ago/)
Page::Issuable::Sidebar.perform do |sidebar|
expect(sidebar).to have_milestone(current_milestone.title)
end
end
end
......
module QA
describe 'Project fork', :core do
it 'can submit merge requests to upstream master' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
merge_request = Factory::Resource::MergeRequestFromFork.fabricate! do |merge_request|
merge_request.fork_branch = 'feature-branch'
end
Page::Menu::Main.act { sign_out }
Page::Main::Login.act do
switch_to_sign_in_tab
sign_in_using_credentials
end
merge_request.visit!
Page::MergeRequest::Show.act { merge! }
expect(page).to have_content('The changes were merged')
end
end
end
......@@ -13,15 +13,11 @@ module QA
Page::Main::Login.act { sign_in_using_credentials }
end
after do |example|
after do
# We need to clear localStorage because we're using it for the dropdown,
# and capybara doesn't do this for us.
# https://github.com/teamcapybara/capybara/issues/1702
Capybara.execute_script 'localStorage.clear()'
# In order to help diagnose a false failure
# https://gitlab.com/gitlab-org/gitlab-ce/issues/48241
log_push_output if example.exception
end
context 'when developers and maintainers are allowed to push to a protected branch' do
......@@ -31,9 +27,9 @@ module QA
expect(protected_branch.name).to have_content(branch_name)
expect(protected_branch.push_allowance).to have_content('Developers + Maintainers')
@push = push_new_file(branch_name)
push = push_new_file(branch_name)
expect(@push.output).to match(/remote: To create a merge request for protected-branch, visit/)
expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
end
end
......@@ -41,11 +37,11 @@ module QA
it 'user without push rights fails to push to the protected branch' do
create_protected_branch(allow_to_push: false)
@push = push_new_file(branch_name)
push = push_new_file(branch_name)
expect(@push.output)
expect(push.output)
.to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
expect(@push.output)
expect(push.output)
.to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
......@@ -69,13 +65,5 @@ module QA
resource.new_branch = false
end
end
def log_push_output
if defined?(@push)
filename = File.join('tmp', "push-output-#{project.name}")
puts "Exception detected. Push output will be saved to #{filename}"
IO.binwrite(filename, @push.output)
end
end
end
end
......@@ -2,6 +2,11 @@ require "spec_helper"
describe Gitlab::Git::Branch, seed_helper: true do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:rugged) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repository.rugged
end
end
subject { repository.branches }
......@@ -124,6 +129,7 @@ describe Gitlab::Git::Branch, seed_helper: true do
it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) }
def create_commit
repository.create_commit(params.merge(committer: committer.merge(time: Time.now)))
params[:message].delete!("\r")
Rugged::Commit.create(rugged, params.merge(committer: committer.merge(time: Time.now)))
end
end
......@@ -27,6 +27,7 @@ EOT
too_large: false
}
# TODO use a Gitaly diff object instead
@rugged_diff = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths:
[".gitmodules"]).patches.first
......@@ -266,8 +267,12 @@ EOT
describe '#submodule?' do
before do
commit = repository.lookup('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
@diffs = commit.parents[0].diff(commit).patches
# TODO use a Gitaly diff object instead
rugged_commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repository.lookup('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
end
@diffs = rugged_commit.parents[0].diff(rugged_commit).patches
end
it { expect(described_class.new(@diffs[0]).submodule?).to eq(false) }
......
......@@ -611,21 +611,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
describe "#remove_remote" do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@repo.remove_remote("expendable")
end
it "should remove the remote" do
expect(@repo.rugged.remotes).not_to include("expendable")
end
after(:all) do
ensure_seeds
end
end
describe "#remote_update" do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
......@@ -633,7 +618,9 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
it "should add the remote" do
expect(@repo.rugged.remotes["expendable"].url).to(
rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access { @repo.rugged }
expect(rugged.remotes["expendable"].url).to(
eq(TEST_NORMAL_REPO_PATH)
)
end
......@@ -1157,6 +1144,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
@repo.rugged.config['core.autocrlf'] = true
end
around do |example|
# OK because autocrlf is only used in gitaly-ruby
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
example.run
end
end
it 'return the value of the autocrlf option' do
expect(@repo.autocrlf).to be(true)
end
......@@ -1172,6 +1166,13 @@ describe Gitlab::Git::Repository, seed_helper: true do
@repo.rugged.config['core.autocrlf'] = false
end
around do |example|
# OK because autocrlf= is only used in gitaly-ruby
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
example.run
end
end
it 'should set the autocrlf option to the provided option' do
@repo.autocrlf = :input
......@@ -2042,54 +2043,61 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
end
let(:rugged) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged }
end
let(:remote_name) { 'my-remote' }
let(:url) { 'http://my-repo.git' }
after do
ensure_seeds
end
describe '#add_remote' do
let(:url) { 'http://my-repo.git' }
let(:mirror_refmap) { '+refs/*:refs/*' }
it 'creates a new remote via Gitaly' do
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
.to receive(:add_remote).with(remote_name, url, mirror_refmap)
shared_examples 'add_remote' do
it 'added the remote' do
begin
rugged.remotes.delete(remote_name)
rescue Rugged::ConfigError
end
repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
expect(rugged.remotes[remote_name]).not_to be_nil
expect(rugged.config["remote.#{remote_name}.mirror"]).to eq('true')
expect(rugged.config["remote.#{remote_name}.prune"]).to eq('true')
expect(rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap)
end
end
context 'with Gitaly disabled', :skip_gitaly_mock do
it 'creates a new remote via Rugged' do
expect_any_instance_of(Rugged::RemoteCollection).to receive(:create)
.with(remote_name, url)
expect_any_instance_of(Rugged::Config).to receive(:[]=)
.with("remote.#{remote_name}.mirror", true)
expect_any_instance_of(Rugged::Config).to receive(:[]=)
.with("remote.#{remote_name}.prune", true)
expect_any_instance_of(Rugged::Config).to receive(:[]=)
.with("remote.#{remote_name}.fetch", mirror_refmap)
context 'using Gitaly' do
it_behaves_like 'add_remote'
end
repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap)
end
context 'with Gitaly disabled', :disable_gitaly do
it_behaves_like 'add_remote'
end
end
describe '#remove_remote' do
it 'removes the remote via Gitaly' do
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
.to receive(:remove_remote).with(remote_name)
shared_examples 'remove_remote' do
it 'removes the remote' do
rugged.remotes.create(remote_name, url)
repository.remove_remote(remote_name)
repository.remove_remote(remote_name)
expect(rugged.remotes[remote_name]).to be_nil
end
end
context 'with Gitaly disabled', :skip_gitaly_mock do
it 'removes the remote via Rugged' do
expect_any_instance_of(Rugged::RemoteCollection).to receive(:delete)
.with(remote_name)
context 'using Gitaly' do
it_behaves_like 'remove_remote'
end
repository.remove_remote(remote_name)
end
context 'with Gitaly disabled', :disable_gitaly do
it_behaves_like 'remove_remote'
end
end
end
......@@ -2281,20 +2289,25 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:worktree_path) { File.join(repository_path, 'worktrees', 'delete-me') }
it 'cleans up the files' do
repository.with_worktree(worktree_path, 'master', env: ENV) do
FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
# git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
# but the HEAD must be 40 characters long or git will ignore it.
File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
create_worktree = %W[git -C #{repository_path} worktree add --detach #{worktree_path} master]
raise 'preparation failed' unless system(*create_worktree, err: '/dev/null')
# git 2.16 fails with "fatal: bad object HEAD"
expect { repository.rev_list(including: :all) }.to raise_error(Gitlab::Git::Repository::GitError)
FileUtils.touch(worktree_path, mtime: Time.now - 8.hours)
# git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object,
# but the HEAD must be 40 characters long or git will ignore it.
File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA)
repository.clean_stale_repository_files
# git 2.16 fails with "fatal: bad object HEAD"
expect(rev_list_all).to be false
expect { repository.rev_list(including: :all) }.not_to raise_error
expect(File.exist?(worktree_path)).to be_falsey
end
repository.clean_stale_repository_files
expect(rev_list_all).to be true
expect(File.exist?(worktree_path)).to be_falsey
end
def rev_list_all
system(*%W[git -C #{repository_path} rev-list --all], out: '/dev/null', err: '/dev/null')
end
it 'increments a counter upon an error' do
......
......@@ -32,65 +32,4 @@ describe Gitlab::Git::RevList do
expect(rev_list.new_refs).to eq(%w[sha1 sha2])
end
end
context '#new_objects' do
it 'fetches list of newly pushed objects using rev-list' do
stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
expect { |b| rev_list.new_objects(&b) }.to yield_with_args(%w[sha1 sha2])
end
it 'can skip pathless objects' do
stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2 path/to/file")
expect { |b| rev_list.new_objects(require_path: true, &b) }.to yield_with_args(%w[sha2])
end
it 'can handle non utf-8 paths' do
non_utf_char = [0x89].pack("c*").force_encoding("UTF-8")
stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha2 πå†h/†ø/ƒîlé#{non_utf_char}\nsha1")
rev_list.new_objects(require_path: true) do |object_ids|
expect(object_ids.force).to eq(%w[sha2])
end
end
it 'can yield a lazy enumerator' do
stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
rev_list.new_objects do |object_ids|
expect(object_ids).to be_a Enumerator::Lazy
end
end
it 'returns the result of the block when given' do
stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
objects = rev_list.new_objects do |object_ids|
object_ids.first
end
expect(objects).to eq 'sha1'
end
it 'can accept list of references to exclude' do
stub_popen_rev_list('newrev', '--not', 'master', '--objects', output: "sha1\nsha2")
expect { |b| rev_list.new_objects(not_in: ['master'], &b) }.to yield_with_args(%w[sha1 sha2])
end
it 'handles empty list of references to exclude as listing all known objects' do
stub_popen_rev_list('newrev', '--objects', output: "sha1\nsha2")
expect { |b| rev_list.new_objects(not_in: [], &b) }.to yield_with_args(%w[sha1 sha2])
end
end
context '#all_objects' do
it 'fetches list of all pushed objects using rev-list' do
stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2")
expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2])
end
end
end
......@@ -734,26 +734,18 @@ describe Gitlab::GitAccess do
merge_into_protected_branch: "0b4bc9a #{merge_into_protected_branch} refs/heads/feature" }
end
def stub_git_hooks
# Running the `pre-receive` hook is expensive, and not necessary for this test.
allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) do |service, &block|
block.call(service)
end
end
def merge_into_protected_branch
@protected_branch_merge_commit ||= begin
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
stub_git_hooks
project.repository.add_branch(user, unprotected_branch, 'feature')
target_branch = project.repository.lookup('feature')
rugged = project.repository.rugged
target_branch = rugged.rev_parse('feature')
source_branch = project.repository.create_file(
user,
'filename',
'This is the file content',
message: 'This is a good commit message',
branch_name: unprotected_branch)
rugged = project.repository.rugged
author = { email: "email@example.com", time: Time.now, name: "Example Git User" }
merge_index = rugged.merge_commits(target_branch, source_branch)
......
# coding: utf-8
require "spec_helper"
describe ProjectWiki do
......@@ -10,7 +11,6 @@ describe ProjectWiki do
subject { project_wiki }
it { is_expected.to delegate_method(:empty?).to :pages }
it { is_expected.to delegate_method(:repository_storage).to :project }
it { is_expected.to delegate_method(:hashed_storage?).to :project }
......@@ -100,11 +100,19 @@ describe ProjectWiki do
context "when the wiki has pages" do
before do
project_wiki.create_page("index", "This is an awesome new Gollum Wiki")
project_wiki.create_page("another-page", "This is another page")
end
describe '#empty?' do
subject { super().empty? }
it { is_expected.to be_falsey }
# Re-enable this when https://gitlab.com/gitlab-org/gitaly/issues/1204 is fixed
xit 'only instantiates a Wiki page once' do
expect(WikiPage).to receive(:new).once.and_call_original
subject
end
end
end
end
......
......@@ -151,7 +151,9 @@ describe Repository do
it { is_expected.to eq(['v1.1.0', 'v1.0.0', annotated_tag_name]) }
after do
repository.rugged.tags.delete(annotated_tag_name)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repository.rugged.tags.delete(annotated_tag_name)
end
end
end
end
......@@ -2231,8 +2233,11 @@ describe Repository do
create_remote_branch('joe', 'remote_branch', masterrev)
repository.add_branch(user, 'local_branch', masterrev.id)
expect(repository.remote_branches('joe').any? { |branch| branch.name == 'local_branch' }).to eq(false)
expect(repository.remote_branches('joe').any? { |branch| branch.name == 'remote_branch' }).to eq(true)
# TODO: move this test to gitaly https://gitlab.com/gitlab-org/gitaly/issues/1243
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
expect(repository.remote_branches('joe').any? { |branch| branch.name == 'local_branch' }).to eq(false)
expect(repository.remote_branches('joe').any? { |branch| branch.name == 'remote_branch' }).to eq(true)
end
end
end
......
......@@ -121,6 +121,7 @@ shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
expect(Time.parse(json_response['updated_at'])).to be_like_time(creation_time)
end
end
......
......@@ -209,12 +209,7 @@ describe GitGarbageCollectWorker do
tree: old_commit.tree,
parents: [old_commit]
)
Gitlab::Git::OperationService.new(nil, project.repository.raw_repository).send(
:update_ref,
"refs/heads/#{SecureRandom.hex(6)}",
new_commit_sha,
Gitlab::Git::BLANK_SHA
)
rugged.references.create("refs/heads/#{SecureRandom.hex(6)}", new_commit_sha)
end
def packs(project)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment