Skip to content
Snippets Groups Projects
Commit d8c06be4 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 2fa7d2dd
No related branches found
No related tags found
No related merge requests found
Showing
with 541 additions and 220 deletions
---
title: Add API endpoint to trigger Group Structure Export
merge_request: 19779
author:
type: added
---
title: Execute limited request for diff commits instead of preloading
merge_request: 19485
author:
type: performance
Loading
Loading
@@ -191,6 +191,31 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get 'proxy/:datasource_id/*proxy_path', to: 'grafana_api#proxy'
get :metrics_dashboard, to: 'grafana_api#metrics_dashboard'
end
resource :mattermost, only: [:new, :create]
resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :edit, :update, :destroy]
resource :mirror, only: [:show, :update] do
member do
get :ssh_host_keys, constraints: { format: :json }
post :update_now
end
end
resource :cycle_analytics, only: [:show]
namespace :cycle_analytics do
scope :events, controller: 'events' do
get :issue
get :plan
get :code
get :test
get :review
get :staging
get :production
end
end
end
# End of the /-/ scope.
 
Loading
Loading
@@ -235,8 +260,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
 
resource :mattermost, only: [:new, :create]
namespace :prometheus do
resources :metrics, constraints: { id: %r{[^\/]+} }, only: [:index, :new, :create, :edit, :update, :destroy] do
get :active_common, on: :collection
Loading
Loading
@@ -364,17 +387,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
put '/service_desk' => 'service_desk#update', as: :service_desk_refresh
end
 
resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :edit, :update, :destroy]
resource :mirror, only: [:show, :update] do
member do
get :ssh_host_keys, constraints: { format: :json }
post :update_now
end
end
Gitlab.ee do
resources :push_rules, constraints: { id: /\d+/ }, only: [:update]
end
Loading
Loading
@@ -463,20 +475,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
 
resource :cycle_analytics, only: [:show]
namespace :cycle_analytics do
scope :events, controller: 'events' do
get :issue
get :plan
get :code
get :test
get :review
get :staging
get :production
end
end
namespace :serverless do
scope :functions do
get '/:environment_id/:id', to: 'functions#show'
Loading
Loading
@@ -678,7 +676,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
:network, :graphs, :autocomplete_sources,
:project_members, :deploy_keys, :deploy_tokens,
:labels, :milestones, :services, :boards, :releases,
:forks, :group_links, :import, :avatar)
:forks, :group_links, :import, :avatar, :mirror,
:cycle_analytics, :mattermost, :variables, :triggers)
end
end
end
Loading
Loading
@@ -139,13 +139,68 @@ type Commit {
"""
Latest pipeline of the commit
"""
latestPipeline: Pipeline
latestPipeline(
"""
Filter pipelines by the ref they are run for
"""
ref: String
"""
Filter pipelines by the sha of the commit they are run for
"""
sha: String
"""
Filter pipelines by their status
"""
status: PipelineStatusEnum
): Pipeline @deprecated(reason: "use pipelines")
 
"""
Raw commit message
"""
message: String
 
"""
Pipelines of the commit ordered latest first
"""
pipelines(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
"""
Filter pipelines by the ref they are run for
"""
ref: String
"""
Filter pipelines by the sha of the commit they are run for
"""
sha: String
"""
Filter pipelines by their status
"""
status: PipelineStatusEnum
): PipelineConnection
"""
SHA1 ID of the commit
"""
Loading
Loading
Loading
Loading
@@ -10210,15 +10210,44 @@
"name": "latestPipeline",
"description": "Latest pipeline of the commit",
"args": [
{
"name": "status",
"description": "Filter pipelines by their status",
"type": {
"kind": "ENUM",
"name": "PipelineStatusEnum",
"ofType": null
},
"defaultValue": null
},
{
"name": "ref",
"description": "Filter pipelines by the ref they are run for",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "sha",
"description": "Filter pipelines by the sha of the commit they are run for",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "Pipeline",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
"isDeprecated": true,
"deprecationReason": "use pipelines"
},
{
"name": "message",
Loading
Loading
@@ -10234,6 +10263,89 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pipelines",
"description": "Pipelines of the commit ordered latest first",
"args": [
{
"name": "status",
"description": "Filter pipelines by their status",
"type": {
"kind": "ENUM",
"name": "PipelineStatusEnum",
"ofType": null
},
"defaultValue": null
},
{
"name": "ref",
"description": "Filter pipelines by the ref they are run for",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "sha",
"description": "Filter pipelines by the sha of the commit they are run for",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "PipelineConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "sha",
"description": "SHA1 ID of the commit",
Loading
Loading
@@ -10306,6 +10418,118 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "PipelineConnection",
"description": "The connection type for Pipeline.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PipelineEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Pipeline",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "PipelineEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Pipeline",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Pipeline",
Loading
Loading
@@ -13205,118 +13429,6 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "PipelineConnection",
"description": "The connection type for Pipeline.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PipelineEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Pipeline",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "PipelineEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Pipeline",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "IssueConnection",
Loading
Loading
Loading
Loading
@@ -185,19 +185,40 @@ Container Scanning can be executed on an offline air-gapped GitLab Ultimate inst
1. Host the following Docker images on a [local Docker container registry](../../packages/container_registry/index.md):
- [arminc/clair-db vulnerabilities database](https://hub.docker.com/r/arminc/clair-db)
- [GitLab klar analyzer](https://gitlab.com/gitlab-org/security-products/analyzers/klar)
1. [Override the container scanning template](#overriding-the-container-scanning-template) in your `.gitlab-ci.yml` file to refer to the Docker
images hosted on your local Docker container registry:
1. [Override the container scanning template](#overriding-the-container-scanning-template) in your `.gitlab-ci.yml` file to refer to the Docker images hosted on your local Docker container registry:
 
```yaml
include:
- template: Container-Scanning.gitlab-ci.yml
 
container_scanning:
image: your.local.registry:5000/gitlab-klar-analyzer
image: $CI_REGISTRY/namespace/gitlab-klar-analyzer
variables:
CLAIR_DB_IMAGE: your.local.registry:5000/clair-vulnerabilities-db
CLAIR_DB_IMAGE: $CI_REGISTRY/namespace/clair-vulnerabilities-db
```
 
It may be worthwhile to set up a [scheduled pipeline](../../project/pipelines/schedules.md) to automatically build a new version of the vulnerabilities database on a preset schedule. You can use the following `.gitlab-yml.ci` as a template:
```yaml
image: docker:stable
services:
- docker:stable-dind
stages:
- build
build_latest_vulnerabilities:
stage: build
script:
- docker pull arminc/clair-db:latest
- docker tag arminc/clair-db:latest $CI_REGISTRY/namespace/clair-vulnerabilities-db
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker push $CI_REGISTRY/namespace/clair-vulnerabilities-db
```
The above template will work for a GitLab Docker registry running on a local installation, however, if you're using a non-GitLab Docker registry, you'll need to change the `$CI_REGISTRY` value and the `docker login` credentials to match the details of your local registry.
## Troubleshooting
 
### docker: Error response from daemon: failed to copy xattrs
Loading
Loading
Loading
Loading
@@ -313,6 +313,41 @@ The sample function can now be triggered from any HTTP client using a simple `PO
 
![function execution](img/function-execution.png)
 
### Running functions locally
Running a function locally is a good way to quickly verify behavior during development.
Running functions locally requires:
- Go 1.12 or newer installed.
- Docker Engine installed and running.
- `gitlabktl` installed using the Go package manager:
```shell
GO111MODULE=on go get gitlab.com/gitlab-org/gitlabktl
```
To run a function locally:
1. Navigate to the root of your GitLab serverless project.
1. Build your function into a Docker image:
```shell
gitlabktl serverless build
```
1. Run your function in Docker:
```shell
docker run -itp 8080:8080 <your_function_name>
```
1. Invoke your function:
```shell
curl http://localhost:8080
```
## Deploying Serverless applications
 
> Introduced in GitLab 11.5.
Loading
Loading
Loading
Loading
@@ -113,6 +113,7 @@ module API
mount ::API::Files
mount ::API::GroupBoards
mount ::API::GroupClusters
mount ::API::GroupExport
mount ::API::GroupLabels
mount ::API::GroupMilestones
mount ::API::Groups
Loading
Loading
# frozen_string_literal: true
module API
class GroupExport < Grape::API
before do
authorize! :admin_group, user_group
end
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups, requirements: { id: %r{[^/]+} } do
desc 'Download export' do
detail 'This feature was introduced in GitLab 12.5.'
end
get ':id/export/download' do
if user_group.export_file_exists?
present_carrierwave_file!(user_group.export_file)
else
render_api_error!('404 Not found or has expired', 404)
end
end
desc 'Start export' do
detail 'This feature was introduced in GitLab 12.5.'
end
post ':id/export' do
GroupExportWorker.perform_async(current_user.id, user_group.id, params)
accepted!
end
end
end
end
Loading
Loading
@@ -18,7 +18,7 @@ code_quality:
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:12-0-stable" /code
"registry.gitlab.com/gitlab-org/security-products/codequality:12-5-stable" /code
artifacts:
reports:
codequality: gl-code-quality-report.json
Loading
Loading
# frozen_string_literal: true
module Gitlab
module Graphql
module Loaders
class PipelineForShaLoader
attr_accessor :project, :sha
def initialize(project, sha)
@project, @sha = project, sha
end
def find_last
BatchLoader::GraphQL.for(sha).batch(key: project) do |shas, loader, args|
pipelines = args[:key].ci_pipelines.latest_for_shas(shas)
pipelines.each do |pipeline|
loader.call(pipeline.sha, pipeline)
end
end
end
end
end
end
end
Loading
Loading
@@ -5000,7 +5000,7 @@ msgstr ""
msgid "CurrentUser|Settings"
msgstr ""
 
msgid "CurrentUser|Start a trial"
msgid "CurrentUser|Start a Gold trial"
msgstr ""
 
msgid "Custom CI configuration path"
Loading
Loading
@@ -7306,9 +7306,6 @@ msgstr ""
msgid "FeatureFlags|Inactive flag for %{scope}"
msgstr ""
 
msgid "FeatureFlags|Include additional user IDs"
msgstr ""
msgid "FeatureFlags|Install a %{docs_link_anchored_start}compatible client library%{docs_link_anchored_end} and specify the API URL, application name, and instance ID during the configuration setup. %{docs_link_start}More Information%{docs_link_end}"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -9,8 +9,6 @@ describe 'user reads pipeline status', :js do
let(:x110_pipeline) { create_pipeline('x1.1.0', 'failed') }
 
before do
stub_feature_flags(vue_file_list: false)
project.add_maintainer(user)
 
project.repository.add_tag(user, 'x1.1.0', 'v1.1.0')
Loading
Loading
@@ -25,7 +23,7 @@ describe 'user reads pipeline status', :js do
visit project_tree_path(project, expected_pipeline.ref)
wait_for_requests
 
page.within('.blob-commit-info') do
page.within('.commit-detail') do
expect(page).to have_link('', href: project_pipeline_path(project, expected_pipeline))
expect(page).to have_selector(".ci-status-icon-#{expected_pipeline.status}")
end
Loading
Loading
Loading
Loading
@@ -133,45 +133,19 @@ describe('StageNavItem', () => {
hasStageName();
});
 
it('renders options menu', () => {
expect(wrapper.find('.more-actions-toggle').exists()).toBe(true);
it('does not render options menu', () => {
expect(wrapper.find('.more-actions-toggle').exists()).toBe(false);
});
 
describe('Default stages', () => {
beforeEach(() => {
wrapper = createComponent(
{ canEdit: true, isUserAllowed: true, isDefaultStage: true },
false,
);
});
it('can hide the stage', () => {
expect(wrapper.text()).toContain('Hide stage');
});
it('can not edit the stage', () => {
expect(wrapper.text()).not.toContain('Edit stage');
});
it('can not remove the stage', () => {
expect(wrapper.text()).not.toContain('Remove stage');
});
it('can not edit the stage', () => {
expect(wrapper.text()).not.toContain('Edit stage');
});
it('can not remove the stage', () => {
expect(wrapper.text()).not.toContain('Remove stage');
});
 
describe('Custom stages', () => {
beforeEach(() => {
wrapper = createComponent(
{ canEdit: true, isUserAllowed: true, isDefaultStage: false },
false,
);
});
it('can edit the stage', () => {
expect(wrapper.text()).toContain('Edit stage');
});
it('can remove the stage', () => {
expect(wrapper.text()).toContain('Remove stage');
});
it('can not hide the stage', () => {
expect(wrapper.text()).not.toContain('Hide stage');
});
it('can not hide the stage', () => {
expect(wrapper.text()).not.toContain('Hide stage');
});
});
});
Loading
Loading
@@ -17,7 +17,7 @@ function createCommitData(data = {}) {
avatarUrl: 'https://test.com',
webUrl: 'https://test.com/test',
},
latestPipeline: {
pipeline: {
detailedStatus: {
detailsPath: 'https://test.com/pipeline',
icon: 'failed',
Loading
Loading
@@ -74,7 +74,7 @@ describe('Repository last commit component', () => {
});
 
it('hides pipeline components when pipeline does not exist', () => {
factory(createCommitData({ latestPipeline: null }));
factory(createCommitData({ pipeline: null }));
 
expect(vm.find('.js-commit-pipeline').exists()).toBe(false);
});
Loading
Loading
Loading
Loading
@@ -2,7 +2,7 @@
 
exports[`Repository file preview component renders file HTML 1`] = `
<article
class="file-holder js-hide-on-navigation limited-width-container readme-holder"
class="file-holder limited-width-container readme-holder"
>
<div
class="file-title"
Loading
Loading
Loading
Loading
@@ -28,7 +28,7 @@ describe('Repository table component', () => {
it('renders file preview', () => {
factory('/');
 
vm.setData({ entries: { blobs: [{ name: 'README.md ' }] } });
vm.setData({ entries: { blobs: [{ name: 'README.md' }] } });
 
expect(vm.find(FilePreview).exists()).toBe(true);
});
Loading
Loading
import { readmeFile } from '~/repository/utils/readme';
describe('readmeFile', () => {
describe('markdown files', () => {
it('returns markdown file', () => {
expect(readmeFile([{ name: 'README' }, { name: 'README.md' }])).toEqual({
name: 'README.md',
});
expect(readmeFile([{ name: 'README' }, { name: 'index.md' }])).toEqual({
name: 'index.md',
});
});
});
describe('plain files', () => {
it('returns plain file', () => {
expect(readmeFile([{ name: 'README' }, { name: 'TEST.md' }])).toEqual({
name: 'README',
});
expect(readmeFile([{ name: 'readme' }, { name: 'TEST.md' }])).toEqual({
name: 'readme',
});
});
});
describe('non-previewable file', () => {
it('returns undefined', () => {
expect(readmeFile([{ name: 'index.js' }, { name: 'TEST.md' }])).toBe(undefined);
});
});
});
Loading
Loading
@@ -13,6 +13,14 @@ describe Resolvers::BaseResolver do
end
end
 
let(:last_resolver) do
Class.new(described_class) do
def resolve(**args)
[1, 2]
end
end
end
describe '.single' do
it 'returns a subclass from the resolver' do
expect(resolver.single.superclass).to eq(resolver)
Loading
Loading
@@ -29,6 +37,22 @@ describe Resolvers::BaseResolver do
end
end
 
describe '.last' do
it 'returns a subclass from the resolver' do
expect(last_resolver.last.superclass).to eq(last_resolver)
end
it 'returns the same subclass every time' do
expect(last_resolver.last.object_id).to eq(last_resolver.last.object_id)
end
it 'returns a resolver that gives the last result from the original resolver' do
result = resolve(last_resolver.last)
expect(result).to eq(2)
end
end
context 'when field is a connection' do
it 'increases complexity based on arguments' do
field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 1)
Loading
Loading
# frozen_string_literal: true
require 'spec_helper'
describe Resolvers::CommitPipelinesResolver do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let(:commit) { create(:commit, project: project) }
let_it_be(:current_user) { create(:user) }
let!(:pipeline) do
create(
:ci_pipeline,
project: project,
sha: commit.id,
ref: 'master',
status: 'success'
)
end
let!(:pipeline2) do
create(
:ci_pipeline,
project: project,
sha: commit.id,
ref: 'master',
status: 'failed'
)
end
let!(:pipeline3) do
create(
:ci_pipeline,
project: project,
sha: commit.id,
ref: 'my_branch',
status: 'failed'
)
end
before do
commit.project.add_developer(current_user)
end
def resolve_pipelines
resolve(described_class, obj: commit, ctx: { current_user: current_user }, args: { ref: 'master' })
end
it 'resolves pipelines for commit and ref' do
pipelines = resolve_pipelines
expect(pipelines).to eq([pipeline2, pipeline])
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment